From d754d76fd0912b78e495cab0eb2fa73405c00952 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 13 Apr 2022 14:19:21 +0000 Subject: [PATCH 0001/1312] Add direct Klever concurrency specials --- src/analyses/libraryFunctions.ml | 5 ++++- src/analyses/mutexEventsAnalysis.ml | 6 ++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 0c00391dcb..ce89391691 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -24,7 +24,8 @@ let classify' fn exps = `Unknown fn in match fn with - | "pthread_create" -> + | "pthread_create" + | "pthread_create_N" -> (* Klever *) begin match exps with | [id;_;fn;x] -> `ThreadCreate (id, fn, x) | _ -> strange_arguments () @@ -72,6 +73,7 @@ let classify' fn exps = | "pthread_rwlock_wrlock" | "GetResource" | "_raw_spin_lock" | "_raw_spin_lock_flags" | "_raw_spin_lock_irqsave" | "_raw_spin_lock_irq" | "_raw_spin_lock_bh" | "spin_lock_irqsave" | "spin_lock" + | "ldv_mutex_model_lock" | "ldv_spin_model_lock" (* Klever *) -> `Lock (get_bool "sem.lock.fail", true, true) | "pthread_mutex_lock" | "__pthread_mutex_lock" -> `Lock (get_bool "sem.lock.fail", true, false) @@ -84,6 +86,7 @@ let classify' fn exps = | "mutex_unlock" | "ReleaseResource" | "_write_unlock" | "_read_unlock" | "_raw_spin_unlock_irqrestore" | "pthread_mutex_unlock" | "__pthread_mutex_unlock" | "spin_unlock_irqrestore" | "up_read" | "up_write" | "up" + | "ldv_mutex_model_unlock" | "ldv_spin_model_unlock" (* Klever *) -> `Unlock | x -> `Unknown x diff --git a/src/analyses/mutexEventsAnalysis.ml b/src/analyses/mutexEventsAnalysis.ml index 4a3ef078ce..050b8de600 100644 --- a/src/analyses/mutexEventsAnalysis.ml +++ b/src/analyses/mutexEventsAnalysis.ml @@ -77,7 +77,8 @@ struct let unlock remove_fn = match f.vname, arglist with | _, [arg] - | ("spin_unlock_irqrestore" | "_raw_spin_unlock_irqrestore"), [arg; _] -> + | ("spin_unlock_irqrestore" | "_raw_spin_unlock_irqrestore"), [arg; _] + | "ldv_mutex_model_unlock", [arg; _] -> (* Klever *) List.iter (fun e -> ctx.split () [Events.Unlock (remove_fn e)] ) (eval_exp_addr (Analyses.ask_of_ctx ctx) arg); @@ -92,7 +93,8 @@ struct | `Lock (failing, rw, nonzero_return_when_aquired), _ -> begin match f.vname, arglist with | _, [arg] - | "spin_lock_irqsave", [arg; _] -> + | "spin_lock_irqsave", [arg; _] + | "ldv_mutex_model_lock", [arg; _] -> (* Klever *) (*print_endline @@ "Mutex `Lock "^f.vname;*) lock ctx rw failing nonzero_return_when_aquired (Analyses.ask_of_ctx ctx) lv arg | _ -> failwith "lock has multiple arguments" From 8f3160943db3dc36a622b0914242a3fa496a2978 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 13 Apr 2022 14:20:08 +0000 Subject: [PATCH 0002/1312] Add special rtnl_lock from Linux --- src/analyses/mutexEventsAnalysis.ml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/analyses/mutexEventsAnalysis.ml b/src/analyses/mutexEventsAnalysis.ml index 050b8de600..a7448af4f4 100644 --- a/src/analyses/mutexEventsAnalysis.ml +++ b/src/analyses/mutexEventsAnalysis.ml @@ -11,6 +11,7 @@ open GobConfig let big_kernel_lock = LockDomain.Addr.from_var (Goblintutil.create_var (makeGlobalVar "[big kernel lock]" intType)) let console_sem = LockDomain.Addr.from_var (Goblintutil.create_var (makeGlobalVar "[console semaphore]" intType)) +let rtnl_lock = LockDomain.Addr.from_var (Goblintutil.create_var (makeGlobalVar "[rtnl_lock]" intType)) let verifier_atomic = LockDomain.Addr.from_var (Goblintutil.create_var (makeGlobalVar "[__VERIFIER_atomic]" intType)) module Spec: MCPSpec = @@ -118,10 +119,14 @@ struct (*print_endline @@ "Mutex `Unlock "^f.vname;*) unlock remove_rw | _, "spinlock_check" -> () - | _, "acquire_console_sem" when get_bool "kernel" -> + | _, "acquire_console_sem"-> (* TODO: removed for Klever: when get_bool "kernel" *) ctx.emit (Events.Lock (console_sem, true)) - | _, "release_console_sem" when get_bool "kernel" -> + | _, "release_console_sem" -> (* TODO: removed for Klever: when get_bool "kernel" *) ctx.emit (Events.Unlock console_sem) + | _, "rtnl_lock"-> + ctx.emit (Events.Lock (rtnl_lock, true)) + | _, ("rtnl_unlock" | "__rtnl_unlock") -> + ctx.emit (Events.Unlock rtnl_lock) | _, "__builtin_prefetch" | _, "misc_deregister" -> () | _, "__VERIFIER_atomic_begin" when get_bool "ana.sv-comp.functions" -> From 1fbf9105c125d1bf4903d43ea477cd88cbb0df6b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 13 Apr 2022 14:20:19 +0000 Subject: [PATCH 0003/1312] Add symb_locks to ldv-races conf --- conf/ldv-races.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/conf/ldv-races.json b/conf/ldv-races.json index 2414413de4..2b30576e1b 100644 --- a/conf/ldv-races.json +++ b/conf/ldv-races.json @@ -27,7 +27,9 @@ "access", "escape", "expRelation", - "mhp" + "mhp", + "var_eq", + "symb_locks" ], "malloc": { "wrappers": [ From 84b5207053a0f0149e07a961f9bdf33c5087efa7 Mon Sep 17 00:00:00 2001 From: Atul Agarwal Date: Thu, 4 May 2023 14:26:03 +0200 Subject: [PATCH 0004/1312] Create build_testing.yml Trying build test using github actions --- .github/workflows/build_testing.yml | 35 +++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 .github/workflows/build_testing.yml diff --git a/.github/workflows/build_testing.yml b/.github/workflows/build_testing.yml new file mode 100644 index 0000000000..8ec9b340c3 --- /dev/null +++ b/.github/workflows/build_testing.yml @@ -0,0 +1,35 @@ +name: build_testing + +on: + - push + - pull_request + +jobs: + tests: + strategy: + fail-fast: false + matrix: + os: + - macos-latest + - ubuntu-latest + ocaml-compiler: + - 4.14.0 + + runs-on: ${{ matrix.os }} + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Setup OCaml ${{ matrix.ocaml-compiler }} + env: + OPAMLOCKED: locked + uses: ocaml/setup-ocaml@v2 + with: + ocaml-compiler: ${{ matrix.ocaml-compiler }} + + - name: Install opam dependencies + run: opam install . --deps-only --locked + + - name: Build + run: ./make.sh nat From b5b7b5a34dddfa5f26d2cc3b6092ac3fc98dded6 Mon Sep 17 00:00:00 2001 From: Atul Agarwal Date: Thu, 4 May 2023 14:33:40 +0200 Subject: [PATCH 0005/1312] Update build_testing.yml Changed the ocaml compiler version to 4.12.0 --- .github/workflows/build_testing.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_testing.yml b/.github/workflows/build_testing.yml index 8ec9b340c3..1b464043c7 100644 --- a/.github/workflows/build_testing.yml +++ b/.github/workflows/build_testing.yml @@ -13,7 +13,7 @@ jobs: - macos-latest - ubuntu-latest ocaml-compiler: - - 4.14.0 + - 4.12.0 runs-on: ${{ matrix.os }} From 0ee88d3836cc5e603b34f246ebc6244e0eba0c10 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Fri, 5 May 2023 19:51:19 +0200 Subject: [PATCH 0006/1312] Hint to run "make" in the tutorial --- docs/developer-guide/firstanalysis.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/developer-guide/firstanalysis.md b/docs/developer-guide/firstanalysis.md index 38668c28da..21221c4a77 100644 --- a/docs/developer-guide/firstanalysis.md +++ b/docs/developer-guide/firstanalysis.md @@ -67,7 +67,7 @@ The key part now is to define transfer functions for assignment. We only handle There is no need to implement the transfer functions for branching for this example; it only relies on lattice join operations to correctly take both paths into account. The assignment relies on the function `eval`, which is almost there. It just needs you to fix the evaluation of constants! Unless you jumped straight to this line, it should not be too complicated to fix this. -With this in place, we should have sufficient information to tell Goblint that the assertion does hold. +With this in place, we should have sufficient information to tell Goblint that the assertion does hold (run `make` to compile the updated analysis in Goblint). ## Extending the domain From 96c777d9325641d7d70248769d3ea4ec719a7daf Mon Sep 17 00:00:00 2001 From: FelixKrayer Date: Tue, 24 Jan 2023 08:45:41 +0100 Subject: [PATCH 0007/1312] tmpSpecial analysis and printalbe for math library descriptors --- src/analyses/libraryDesc.ml | 183 +++++++++++++++++++++- src/analyses/tmpSpecial.ml | 64 ++++++++ src/domains/queries.ml | 9 ++ tests/regression/57-floats/19-specialEq.c | 20 +++ 4 files changed, 275 insertions(+), 1 deletion(-) create mode 100644 src/analyses/tmpSpecial.ml create mode 100644 tests/regression/57-floats/19-specialEq.c diff --git a/src/analyses/libraryDesc.ml b/src/analyses/libraryDesc.ml index a477fc1809..1b915faa01 100644 --- a/src/analyses/libraryDesc.ml +++ b/src/analyses/libraryDesc.ml @@ -1,6 +1,6 @@ (** Library function descriptor (specification). *) module Cil = GoblintCil - +open Cil (** Pointer argument access specification. *) module Access = struct @@ -143,3 +143,184 @@ let of_old ?(attrs: attr list=[]) (old_accesses: Accesses.old) (classify_name): accs = Accesses.of_old old_accesses; special = special_of_old classify_name; } + +module MathPrintable = struct + include Printable.Std + type t = math + + let name () = "MathPrintable" + + let relift = function + | Nan (fk, exp) -> Nan (CilType.Fkind.relift fk, CilType.Exp.relift exp) + | Inf fk -> Inf (CilType.Fkind.relift fk) + | Isfinite exp -> Isfinite (CilType.Exp.relift exp) + | Isinf exp -> Isinf (CilType.Exp.relift exp) + | Isnan exp -> Isnan (CilType.Exp.relift exp) + | Isnormal exp -> Isnormal (CilType.Exp.relift exp) + | Signbit exp -> Signbit (CilType.Exp.relift exp) + | Isgreater (exp1, exp2) -> Isgreater (CilType.Exp.relift exp1, CilType.Exp.relift exp2) + | Isgreaterequal (exp1, exp2) -> Isgreaterequal (CilType.Exp.relift exp1, CilType.Exp.relift exp2) + | Isless (exp1, exp2) -> Isless (CilType.Exp.relift exp1, CilType.Exp.relift exp2) + | Islessequal (exp1, exp2) -> Islessequal (CilType.Exp.relift exp1, CilType.Exp.relift exp2) + | Islessgreater (exp1, exp2) -> Islessgreater (CilType.Exp.relift exp1, CilType.Exp.relift exp2) + | Isunordered (exp1, exp2) -> Isunordered (CilType.Exp.relift exp1, CilType.Exp.relift exp2) + | Ceil (fk, exp) -> Ceil (CilType.Fkind.relift fk, CilType.Exp.relift exp) + | Floor (fk, exp) -> Floor (CilType.Fkind.relift fk, CilType.Exp.relift exp) + | Fabs (fk, exp) -> Fabs (CilType.Fkind.relift fk, CilType.Exp.relift exp) + | Fmax (fk, exp1, exp2) -> Fmax (CilType.Fkind.relift fk, CilType.Exp.relift exp1, CilType.Exp.relift exp2) + | Fmin (fk, exp1, exp2) -> Fmin (fk, CilType.Exp.relift exp1, CilType.Exp.relift exp2) + | Acos (fk, exp) -> Acos (CilType.Fkind.relift fk, CilType.Exp.relift exp) + | Asin (fk, exp) -> Asin (CilType.Fkind.relift fk, CilType.Exp.relift exp) + | Atan (fk, exp) -> Atan (CilType.Fkind.relift fk, CilType.Exp.relift exp) + | Atan2 (fk, exp1, exp2) -> Atan2 (CilType.Fkind.relift fk, CilType.Exp.relift exp1, CilType.Exp.relift exp2) + | Cos (fk, exp) -> Cos (CilType.Fkind.relift fk, CilType.Exp.relift exp) + | Sin (fk, exp) -> Sin (CilType.Fkind.relift fk, CilType.Exp.relift exp) + | Tan (fk, exp) -> Tan (CilType.Fkind.relift fk, CilType.Exp.relift exp) + + + let order = function + | Nan _ -> 1 + | Inf _ -> 2 + | Isfinite _ -> 3 + | Isinf _ -> 4 + | Isnan _ -> 5 + | Isnormal _ -> 6 + | Signbit _ -> 7 + | Isgreater _ -> 8 + | Isgreaterequal _ -> 9 + | Isless _ -> 10 + | Islessequal _ -> 11 + | Islessgreater _ -> 12 + | Isunordered _ -> 13 + | Ceil _ -> 14 + | Floor _ -> 15 + | Fabs _ -> 16 + | Fmax _ -> 17 + | Fmin _ -> 18 + | Acos _ -> 19 + | Asin _ -> 20 + | Atan _ -> 21 + | Atan2 _ -> 22 + | Cos _ -> 23 + | Sin _ -> 24 + | Tan _ -> 25 + + let equal m1 m2 = (compare m1 m2) == 0 + let hash = order + + let cmp_fk_exp (fk1, e1) (fk2, e2) = + let r = (CilType.Fkind.compare fk1 fk2) in + if r <> 0 then + r + else + CilType.Exp.compare e1 e2 + + let cmp_exp_exp (e1, e1') (e2, e2') = + let r = (CilType.Exp.compare e1 e2) in + if r <> 0 then + r + else + CilType.Exp.compare e1' e2' + + let cmp_fk_exp_exp (fk1, e1, e1') (fk2, e2, e2') = + let r = (CilType.Fkind.compare fk1 fk2) in + if r <> 0 then + r + else + cmp_exp_exp (e1, e1') (e2, e2') + + let compare m1 m2 = + let r = Stdlib.compare (order m1) (order m2) in + if r <> 0 then + r + else + match m1, m2 with + | Nan fe1, Nan fe2 -> cmp_fk_exp fe1 fe2 + | Inf fk1, Inf fk2 -> CilType.Fkind.compare fk1 fk2 + | Isfinite e1, Isfinite e2 -> CilType.Exp.compare e1 e2 + | Isinf e1, Isinf e2 -> CilType.Exp.compare e1 e2 + | Isnan e1, Isnan e2 -> CilType.Exp.compare e1 e2 + | Isnormal e1, Isnormal e2 -> CilType.Exp.compare e1 e2 + | Signbit e1, Signbit e2 -> CilType.Exp.compare e1 e2 + | Isgreater ee1, Isgreater ee2 -> cmp_exp_exp ee1 ee2 + | Isgreaterequal ee1, Isgreaterequal ee2 -> cmp_exp_exp ee1 ee2 + | Isless ee1, Isless ee2 -> cmp_exp_exp ee1 ee2 + | Islessequal ee1, Islessequal ee2 -> cmp_exp_exp ee1 ee2 + | Islessgreater ee1, Islessgreater ee2 -> cmp_exp_exp ee1 ee2 + | Isunordered ee1, Isunordered ee2 -> cmp_exp_exp ee1 ee2 + | Ceil fe1, Ceil fe2 -> cmp_fk_exp fe1 fe2 + | Floor fe1, Floor fe2 -> cmp_fk_exp fe1 fe2 + | Fabs fe1, Fabs fe2 -> cmp_fk_exp fe1 fe2 + | Fmax fee1, Fmax fee2 -> cmp_fk_exp_exp fee1 fee2 + | Fmin fee1, Fmin fee2 -> cmp_fk_exp_exp fee1 fee2 + | Acos fe1, Acos fe2 -> cmp_fk_exp fe1 fe2 + | Asin fe1, Asin fe2 -> cmp_fk_exp fe1 fe2 + | Atan fe1, Atan fe2 -> cmp_fk_exp fe1 fe2 + | Atan2 fee1, Atan2 fee2 -> cmp_fk_exp_exp fee1 fee2 + | Cos fe1, Cos fe2 -> cmp_fk_exp fe1 fe2 + | Sin fe1, Sin fe2 -> cmp_fk_exp fe1 fe2 + | Tan fe1, Tan fe2 -> cmp_fk_exp fe1 fe2 + | _ -> failwith "impossible" + + let show = function + | Nan _ -> "nan" + | Inf _ -> "inf" + | Isfinite _ -> "isFinite" + | Isinf _ -> "isInf" + | Isnan _ -> "isNan" + | Isnormal _ -> "isNormal" + | Signbit _ -> "signbit" + | Isgreater _ -> "isGreater" + | Isgreaterequal _ -> "isGreaterEqual" + | Isless _ -> "isLess" + | Islessequal _ -> "isLessEqual" + | Islessgreater _ -> "isLessGreater" + | Isunordered _ -> "isUnordered" + | Ceil _ -> "ceil" + | Floor _ -> "floor" + | Fabs _ -> "fabs" + | Fmax _ -> "fmax" + | Fmin _ -> "fmin" + | Acos _ -> "acos" + | Asin _ -> "asin" + | Atan _ -> "atan" + | Atan2 _ -> "atan2" + | Cos _ -> "cos" + | Sin _ -> "sin" + | Tan _ -> "tan" + + let pretty () = function + | Nan (fk, exp) -> Pretty.dprintf "(%a )nan(%a)" d_fkind fk d_exp exp + | Inf fk -> Pretty.dprintf "(%a )inf()" d_fkind fk + | Isfinite exp -> Pretty.dprintf "isFinite(%a)" d_exp exp + | Isinf exp -> Pretty.dprintf "isInf(%a)" d_exp exp + | Isnan exp -> Pretty.dprintf "isNan(%a)" d_exp exp + | Isnormal exp -> Pretty.dprintf "isNormal(%a)" d_exp exp + | Signbit exp -> Pretty.dprintf "signbit(%a)" d_exp exp + | Isgreater (exp1, exp2) -> Pretty.dprintf "isGreater(%a, %a)" d_exp exp1 d_exp exp2 + | Isgreaterequal (exp1, exp2) -> Pretty.dprintf "isGreaterEqual(%a, %a)" d_exp exp1 d_exp exp2 + | Isless (exp1, exp2) -> Pretty.dprintf "isLess(%a, %a)" d_exp exp1 d_exp exp2 + | Islessequal (exp1, exp2) -> Pretty.dprintf "isLessEqual(%a, %a)" d_exp exp1 d_exp exp2 + | Islessgreater (exp1, exp2) -> Pretty.dprintf "isLessGreater(%a, %a)" d_exp exp1 d_exp exp2 + | Isunordered (exp1, exp2) -> Pretty.dprintf "isUnordered(%a, %a)" d_exp exp1 d_exp exp2 + | Ceil (fk, exp) -> Pretty.dprintf "(%a )ceil(%a)" d_fkind fk d_exp exp + | Floor (fk, exp) -> Pretty.dprintf "(%a )floor(%a)" d_fkind fk d_exp exp + | Fabs (fk, exp) -> Pretty.dprintf "(%a )fabs(%a)" d_fkind fk d_exp exp + | Fmax (fk, exp1, exp2) -> Pretty.dprintf "(%a )fmax(%a, %a)" d_fkind fk d_exp exp1 d_exp exp2 + | Fmin (fk, exp1, exp2) -> Pretty.dprintf "(%a )fmin(%a, %a)" d_fkind fk d_exp exp1 d_exp exp2 + | Acos (fk, exp) -> Pretty.dprintf "(%a )acos(%a)" d_fkind fk d_exp exp + | Asin (fk, exp) -> Pretty.dprintf "(%a )asin(%a)" d_fkind fk d_exp exp + | Atan (fk, exp) -> Pretty.dprintf "(%a )atan(%a)" d_fkind fk d_exp exp + | Atan2 (fk, exp1, exp2) -> Pretty.dprintf "(%a )atan2(%a, %a)" d_fkind fk d_exp exp1 d_exp exp2 + | Cos (fk, exp) -> Pretty.dprintf "(%a )cos(%a)" d_fkind fk d_exp exp + | Sin (fk, exp) -> Pretty.dprintf "(%a )sin(%a)" d_fkind fk d_exp exp + | Tan (fk, exp) -> Pretty.dprintf "(%a )tan(%a)" d_fkind fk d_exp exp + + let printXml f x = BatPrintf.fprintf f "\n\n%s\n\n\n" (show x) + let to_yojson _ = failwith "ToDo Implement in future" +end + +module MathLifted = Lattice.Flat (MathPrintable) (struct + let top_name = "Unknown math desc" + let bot_name = "Nonexistent math desc" +end) diff --git a/src/analyses/tmpSpecial.ml b/src/analyses/tmpSpecial.ml new file mode 100644 index 0000000000..f02f5cb48d --- /dev/null +++ b/src/analyses/tmpSpecial.ml @@ -0,0 +1,64 @@ +(* Analysis that tracks which variables hold the results of calls to math library functions. *) + +open Prelude.Ana +open Analyses + +module Spec = +struct + include Analyses.IdentitySpec + + let name () = "tmpSpecial" + module ML = LibraryDesc.MathLifted + module D = MapDomain.MapBot (Basetype.Variables) (ML) + module C = Lattice.Unit + + let context _ _ = () + + (* transfer functions *) + let assign ctx (lval:lval) (rval:exp) : D.t = + match lval with + | (Var v, _) -> D.remove v ctx.local + (* TODO: Handle mem -> may point to*) + | _ -> ctx.local + + let branch ctx (exp:exp) (tv:bool) : D.t = + ctx.local + + let body ctx (f:fundec) : D.t = + ctx.local + + let return ctx (exp:exp option) (f:fundec) : D.t = + ctx.local + + let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = + [ctx.local, D.bot ()] + + let combine ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) f_ask : D.t = + D.bot () + + let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = + (* Just dbg prints *) + (match lval with + | Some (Var v, offs) -> if M.tracing then M.tracel "tmpSpecial" "Special: %s with\n lval %a; vinfo %a; attr %a \n" f.vname d_lval (Var v, offs) d_varinfo v d_attrlist v.vattr + | _ -> if M.tracing then M.tracel "tmpSpecial" "Special: %s\n" f.vname); + + let desc = LibraryFunctions.find f in + let res = + match lval, desc.special arglist with + | (Some (Var v, _)), (Math { fun_args; }) -> D.add v (ML.lift fun_args) ctx.local + | _ -> ctx.local + in + if M.tracing then M.tracel "tmpSpecial" "Result: %a\n\n" D.pretty res; + res + + let query ctx (type a) (q: a Queries.t): a Queries.result = + Queries.Result.top q + + let startstate v = D.bot () + let threadenter ctx lval f args = [D.bot ()] + let threadspawn ctx lval f args fctx = ctx.local + let exitstate v = D.bot () +end + +let _ = + MCP.register_analysis (module Spec : MCPSpec) diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 66db991826..811b3f74c3 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -13,6 +13,8 @@ module TS = SetDomain.ToppedSet (CilType.Typ) (struct let topname = "All" end) module ES = SetDomain.Reverse (SetDomain.ToppedSet (CilType.Exp) (struct let topname = "All" end)) module VS = SetDomain.ToppedSet (CilType.Varinfo) (struct let topname = "All" end) +module ML = LibraryDesc.MathLifted + module VI = Lattice.Flat (Basetype.Variables) (struct let top_name = "Unknown line" let bot_name = "Unreachable line" @@ -99,6 +101,7 @@ type _ t = | MayAccessed: AccessDomain.EventSet.t t | MayBeTainted: LS.t t | MayBeModifiedSinceSetjmp: JmpBufDomain.BufferEntry.t -> VS.t t + | TmpSpecial: varinfo -> ML.t t type 'a result = 'a @@ -159,6 +162,7 @@ struct | MayAccessed -> (module AccessDomain.EventSet) | MayBeTainted -> (module LS) | MayBeModifiedSinceSetjmp _ -> (module VS) + | TmpSpecial _ -> (module ML) (** Get bottom result for query. *) let bot (type a) (q: a t): a result = @@ -218,6 +222,7 @@ struct | MayAccessed -> AccessDomain.EventSet.top () | MayBeTainted -> LS.top () | MayBeModifiedSinceSetjmp _ -> VS.top () + | TmpSpecial _ -> ML.top () end (* The type any_query can't be directly defined in Any as t, @@ -274,6 +279,7 @@ struct | Any ActiveJumpBuf -> 46 | Any ValidLongJmp -> 47 | Any (MayBeModifiedSinceSetjmp _) -> 48 + | Any (TmpSpecial _) -> 42 let rec compare a b = let r = Stdlib.compare (order a) (order b) in @@ -316,6 +322,7 @@ struct | Any (IterSysVars (vq1, vf1)), Any (IterSysVars (vq2, vf2)) -> VarQuery.compare vq1 vq2 (* not comparing fs *) | Any (MustProtectedVars m1), Any (MustProtectedVars m2) -> compare_mustprotectedvars m1 m2 | Any (MayBeModifiedSinceSetjmp e1), Any (MayBeModifiedSinceSetjmp e2) -> JmpBufDomain.BufferEntry.compare e1 e2 + | Any (TmpSpecial vi1), Any (TmpSpecial vi2) -> CilType.Varinfo.compare vi1 vi2 (* only argumentless queries should remain *) | _, _ -> Stdlib.compare (order a) (order b) @@ -351,6 +358,7 @@ struct | Any (InvariantGlobal vi) -> Hashtbl.hash vi | Any (MustProtectedVars m) -> hash_mustprotectedvars m | Any (MayBeModifiedSinceSetjmp e) -> JmpBufDomain.BufferEntry.hash e + | Any (TmpSpecial vi) -> CilType.Varinfo.hash vi (* IterSysVars: *) (* - argument is a function and functions cannot be compared in any meaningful way. *) (* - doesn't matter because IterSysVars is always queried from outside of the analysis, so MCP's query caching is not done for it. *) @@ -404,6 +412,7 @@ struct | Any MayBeTainted -> Pretty.dprintf "MayBeTainted" | Any DYojson -> Pretty.dprintf "DYojson" | Any MayBeModifiedSinceSetjmp buf -> Pretty.dprintf "MayBeModifiedSinceSetjmp %a" JmpBufDomain.BufferEntry.pretty buf + | Any (TmpSpecial vi) -> Pretty.dprintf "TmpSpecial %a" CilType.Varinfo.pretty vi end let to_value_domain_ask (ask: ask) = diff --git a/tests/regression/57-floats/19-specialEq.c b/tests/regression/57-floats/19-specialEq.c new file mode 100644 index 0000000000..2a181d0588 --- /dev/null +++ b/tests/regression/57-floats/19-specialEq.c @@ -0,0 +1,20 @@ +//PARAM: --set ana.activated[+] tmpSpecial +#include +#include + +void main() { + float f; + int c; + + + + if ( __builtin_isfinite(f) ) { + __goblint_check(f); + } + float x; + x = __builtin_atan2(f, 0.4); + if (__builtin_isnan(f) && __builtin_isinf(__builtin_inff())) + { + f = 0; + } +} \ No newline at end of file From 508180c87201ba70c386c1e7607004d4282b7e2e Mon Sep 17 00:00:00 2001 From: FelixKrayer Date: Fri, 3 Feb 2023 00:12:11 +0100 Subject: [PATCH 0008/1312] invalidate if argument is written --- src/analyses/tmpSpecial.ml | 108 +++++++++++++++++++--- tests/regression/57-floats/19-specialEq.c | 34 +++++-- 2 files changed, 118 insertions(+), 24 deletions(-) diff --git a/src/analyses/tmpSpecial.ml b/src/analyses/tmpSpecial.ml index f02f5cb48d..b5bda86289 100644 --- a/src/analyses/tmpSpecial.ml +++ b/src/analyses/tmpSpecial.ml @@ -1,4 +1,5 @@ -(* Analysis that tracks which variables hold the results of calls to math library functions. *) +(* Analysis that tracks which variables hold the results of calls to math library functions. + For each equivalence a set of lvals is tracked, that contains all lvals on which the arguments of the corresponding call depend, so an equivalence can be removed if one of the lvals is written.*) open Prelude.Ana open Analyses @@ -9,17 +10,49 @@ struct let name () = "tmpSpecial" module ML = LibraryDesc.MathLifted - module D = MapDomain.MapBot (Basetype.Variables) (ML) + module LS = SetDomain.ToppedSet(Lval.CilLval) (struct let topname = "All" end) + module MlLsProd = Lattice.Prod (ML) (LS) + module D = MapDomain.MapBot (Basetype.Variables) (MlLsProd) module C = Lattice.Unit + let rec resolve (offs : offset) : (CilType.Fieldinfo.t, Basetype.CilExp.t) Lval.offs = + match offs with + | NoOffset -> `NoOffset + | Field (f_info, f_offs) -> `Field (f_info, (resolve f_offs)) + | Index (i_exp, i_offs) -> `Index (i_exp, (resolve i_offs)) + + let ls_of_lv ctx (lval:lval) : LS.t = + match lval with + | (Var v, offs) -> LS.of_list [(v, resolve offs)] + | (Mem e, _) -> (ctx.ask (Queries.MayPointTo e)) + + let rec ls_of_exp ctx (exp:exp) : LS.t = + match exp with + | Const _ -> LS.bot () + | Lval lv -> ls_of_lv ctx lv + | SizeOf _ -> LS.bot () + | Real e -> ls_of_exp ctx e + | Imag e -> ls_of_exp ctx e + | SizeOfE e -> ls_of_exp ctx e + | SizeOfStr _ -> LS.empty () + | AlignOf _ -> LS.top () (* TODO: what is this*) + | AlignOfE _ -> LS.top () (* TODO: what is this*) + | UnOp (_,e,_) -> ls_of_exp ctx e + | BinOp (_,e1,e2,_) -> LS.union (ls_of_exp ctx e1) (ls_of_exp ctx e2) + | Question (q,e1,e2,_) -> LS.union (ls_of_exp ctx q) (LS.union (ls_of_exp ctx e1) (ls_of_exp ctx e2)) + | CastE (_,e) -> ls_of_exp ctx e + | AddrOf _ -> ctx.ask (Queries.MayPointTo exp) + | AddrOfLabel _ -> LS.top () (* TODO: what is this*) + | StartOf _ -> LS.top () (* TODO: what is this*) + + let context _ _ = () (* transfer functions *) let assign ctx (lval:lval) (rval:exp) : D.t = - match lval with - | (Var v, _) -> D.remove v ctx.local - (* TODO: Handle mem -> may point to*) - | _ -> ctx.local + (* get the set of all lvals written by the assign. Then remove all entrys from the map where the dependencies overlap with the set of written lvals *) + let lvalsWritten = ls_of_lv ctx lval in + D.filter (fun v (ml, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) ctx.local let branch ctx (exp:exp) (tv:bool) : D.t = ctx.local @@ -37,22 +70,69 @@ struct D.bot () let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = + let d = ctx.local in + (* Just dbg prints *) (match lval with | Some (Var v, offs) -> if M.tracing then M.tracel "tmpSpecial" "Special: %s with\n lval %a; vinfo %a; attr %a \n" f.vname d_lval (Var v, offs) d_varinfo v d_attrlist v.vattr | _ -> if M.tracing then M.tracel "tmpSpecial" "Special: %s\n" f.vname); - let desc = LibraryFunctions.find f in - let res = + (* add new math fun dec*) + let d = match lval, desc.special arglist with - | (Some (Var v, _)), (Math { fun_args; }) -> D.add v (ML.lift fun_args) ctx.local - | _ -> ctx.local + | (Some (Var v, _)), (Math { fun_args; }) -> + let lvalDep = List.fold LS.union (LS.empty ()) (List.map (ls_of_exp ctx) arglist) in + if LS.is_top lvalDep then + d + else + D.add v ((ML.lift fun_args, lvalDep)) d + | _ -> d + in + + (* remove entrys, dependent on lvals that were possibly written by the special function *) + let shallow_addrs = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = false } arglist in + let deep_addrs = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = true } arglist in + let deep_addrs = + if List.mem LibraryDesc.InvalidateGlobals desc.attrs then ( + foldGlobals !Cilfacade.current_file (fun acc global -> + match global with + | GVar (vi, _, _) when not (BaseUtil.is_static vi) -> + mkAddrOf (Var vi, NoOffset) :: acc + (* TODO: what about GVarDecl? (see "base.ml -> special_unknown_invalidate")*) + | _ -> acc + ) deep_addrs + ) + else + deep_addrs + in + let d = List.fold_left (fun accD addr -> + let lvalsWritten = ctx.ask (Queries.MayPointTo addr) in + D.filter (fun v (_, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) accD) d shallow_addrs + in + let d = List.fold_left (fun accD addr -> + let lvalsWritten = ctx.ask (Queries.ReachableFrom addr) in + D.filter (fun v (_, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) accD) d deep_addrs + in + + (* same for lval assignment of the call (though this should always be a "tmp___n")*) + let d = + match lval with + | Some lv -> ( + (* get the set of all lvals written by the assign. Then remove all entrys from the map where the dependencies overlap with the set of written lvals *) + let lvalsWritten = ls_of_lv ctx lv in + D.filter (fun v (ml, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) d + ) + | None -> d in - if M.tracing then M.tracel "tmpSpecial" "Result: %a\n\n" D.pretty res; - res + if M.tracing then M.tracel "tmpSpecial" "Result: %a\n\n" D.pretty d; + d - let query ctx (type a) (q: a Queries.t): a Queries.result = - Queries.Result.top q + let query ctx (type a) (q: a Queries.t) : a Queries.result = + match q with + | TmpSpecial v -> let ml = fst (D.find v ctx.local) in + if ML.is_bot ml then Queries.Result.top q + else ml + | _ -> Queries.Result.top q let startstate v = D.bot () let threadenter ctx lval f args = [D.bot ()] diff --git a/tests/regression/57-floats/19-specialEq.c b/tests/regression/57-floats/19-specialEq.c index 2a181d0588..e9bf57f3aa 100644 --- a/tests/regression/57-floats/19-specialEq.c +++ b/tests/regression/57-floats/19-specialEq.c @@ -2,19 +2,33 @@ #include #include + void main() { float f; - int c; + float g; + float common; + float* fptr; + float* gptr; + int unk1; + int unk2; + + float other; + + if (unk1) + fptr = &f; + else + fptr = &common; + + if (unk2) + gptr = &g; + else + gptr = &common; + - if ( __builtin_isfinite(f) ) { - __goblint_check(f); - } - float x; - x = __builtin_atan2(f, 0.4); - if (__builtin_isnan(f) && __builtin_isinf(__builtin_inff())) - { - f = 0; - } + other = __builtin_cos(*fptr); + *gptr = 0.7; + + __builtin_inf(); } \ No newline at end of file From f80bd4ff8acc15d3441cb16564a4cd922576a8d3 Mon Sep 17 00:00:00 2001 From: FelixKrayer Date: Sat, 11 Feb 2023 18:34:49 +0100 Subject: [PATCH 0009/1312] improve baseInvariant for calls to math functions --- src/analyses/baseInvariant.ml | 54 ++++++++++++++++++++++- src/cdomains/floatDomain.ml | 49 +++++++++++++++++++- src/cdomains/floatDomain.mli | 12 +++++ tests/regression/57-floats/19-specialEq.c | 35 ++++----------- 4 files changed, 120 insertions(+), 30 deletions(-) diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index 6b5f03750a..92204d9bea 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -547,6 +547,11 @@ struct in let eval e st = eval_rv a gs st e in let eval_bool e st = match eval e st with `Int i -> ID.to_bool i | _ -> None in + let unroll_fk_of_exp e = + match unrollType (Cilfacade.typeOf e) with + | TFloat (fk, _) -> fk + | _ -> failwith "impossible" + in let rec inv_exp c_typed exp (st:D.t): D.t = (* trying to improve variables in an expression so it is bottom means dead code *) if VD.is_bot_value c_typed then contra st else @@ -682,6 +687,7 @@ struct | Lval x, (`Int _ | `Float _ | `Address _) -> (* meet x with c *) let update_lval c x c' pretty = refine_lv ctx a gs st c x c' pretty exp in let t = Cil.unrollType (Cilfacade.typeOfLval x) in (* unroll type to deal with TNamed *) + if M.tracing then M.trace "invSpecial" "invariant with Lval %a, c_typed %a, type %a\n" d_lval x VD.pretty c_typed d_type t; begin match c_typed with | `Int c -> let c' = match t with @@ -691,7 +697,30 @@ struct | TFloat (fk, _) -> `Float (FD.of_int fk c) | _ -> `Int c in - update_lval c x c' ID.pretty + let st = update_lval c x c' ID.pretty in + (* handle special calls *) + begin match t with + | TInt (ik, _) -> + begin match x with + | ((Var v), _) -> + if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial v)); + let tv = not (ID.leq c (ID.of_bool ik false)) in + begin match ctx.ask (Queries.TmpSpecial v) with + | `Lifted (Isfinite xFloat) when tv -> inv_exp (`Float (FD.finite (unroll_fk_of_exp xFloat))) xFloat st + | `Lifted (Isnan xFloat) when tv -> inv_exp (`Float (FD.nan_of (unroll_fk_of_exp xFloat))) xFloat st + (* should be correct according to C99 standard*) + | `Lifted (Isgreater (xFloat, yFloat)) -> inv_exp (`Int (ID.of_bool ik tv)) (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))) st + | `Lifted (Isgreaterequal (xFloat, yFloat)) -> inv_exp (`Int (ID.of_bool ik tv)) (BinOp (Ge, xFloat, yFloat, (typeOf xFloat))) st + | `Lifted (Isless (xFloat, yFloat)) -> inv_exp (`Int (ID.of_bool ik tv)) (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))) st + | `Lifted (Islessequal (xFloat, yFloat)) -> inv_exp (`Int (ID.of_bool ik tv)) (BinOp (Le, xFloat, yFloat, (typeOf xFloat))) st + | `Lifted (Islessgreater (xFloat, yFloat)) -> inv_exp (`Int (ID.of_bool ik tv)) (BinOp (LOr, (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))), (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))), (TInt (IBool, [])))) st + | `Lifted (Isunordered (xFloat, yFloat)) -> st (* something can probably be done here *) + | _ -> st + end + | _ -> st + end + | _ -> st + end | `Float c -> let c' = match t with (* | TPtr _ -> ..., pointer conversion from/to float is not supported *) @@ -701,7 +730,28 @@ struct | TFloat (fk, _) -> `Float (FD.cast_to fk c) | _ -> `Float c in - update_lval c x c' FD.pretty + let st = update_lval c x c' FD.pretty in + (* handle special calls *) + begin match t with + | TFloat (fk, _) -> + begin match x with + | ((Var v), _) -> + if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial v)); + begin match ctx.ask (Queries.TmpSpecial v) with + | `Lifted (Ceil (ret_fk, xFloat)) when FD.is_interval c -> inv_exp (`Float (FD.inv_ceil (FD.cast_to ret_fk c))) xFloat st + | `Lifted (Floor (ret_fk, xFloat)) when FD.is_interval c -> inv_exp (`Float (FD.inv_floor (FD.cast_to ret_fk c))) xFloat st + | `Lifted (Fabs (ret_fk, xFloat)) -> + let inv = FD.inv_fabs (FD.cast_to ret_fk c) in + if FD.is_bot inv then + raise Analyses.Deadcode + else + inv_exp (`Float inv) xFloat st + | _ -> st + end + | _ -> st + end + | _ -> st + end | `Address c -> let c' = c_typed in (* TODO: need any of the type-matching nonsense? *) update_lval c x c' AD.pretty diff --git a/src/cdomains/floatDomain.ml b/src/cdomains/floatDomain.ml index 4eb024adf9..4bd1017696 100644 --- a/src/cdomains/floatDomain.ml +++ b/src/cdomains/floatDomain.ml @@ -41,6 +41,13 @@ module type FloatArith = sig val tan : t -> t (** tan(x) *) + (** {inversions of unary functions}*) + val inv_ceil : t -> t + (** (inv_ceil z -> x) if (z = ceil(x)) *) + val inv_floor : t -> t + (** (inv_floor z -> x) if (z = floor(x)) *) + val inv_fabs : t -> t + (** (inv_fabs z -> x) if (z = fabs(x)) *) (** {b Comparison operators} *) val lt : t -> t -> IntDomain.IntDomTuple.t @@ -88,11 +95,13 @@ module type FloatDomainBase = sig val starting : float -> t val ending_before : float -> t val starting_after : float -> t + val finite : t val minimal: t -> float option val maximal: t -> float option val is_exact : t -> bool + val is_interval : t -> bool end module FloatIntervalImpl(Float_t : CFloatType) = struct @@ -173,6 +182,10 @@ module FloatIntervalImpl(Float_t : CFloatType) = struct | MinusInfinity -> true | _ -> false + let is_interval = function + | Interval _ -> true + | _ -> false + let norm = function | Interval (low, high) as x -> if Float_t.is_finite low && Float_t.is_finite high then @@ -210,6 +223,7 @@ module FloatIntervalImpl(Float_t : CFloatType) = struct let ending_before e = of_interval' (Float_t.lower_bound, Float_t.pred @@ Float_t.of_float Up e) let starting s = of_interval' (Float_t.of_float Down s, Float_t.upper_bound) let starting_after s = of_interval' (Float_t.succ @@ Float_t.of_float Down s, Float_t.upper_bound) + let finite = of_interval' (Float_t.lower_bound, Float_t.upper_bound) let minimal = function | Bot -> raise (ArithmeticOnFloatBot (Printf.sprintf "minimal %s" (show Bot))) @@ -661,6 +675,16 @@ module FloatIntervalImpl(Float_t : CFloatType) = struct | (l, h) when l = h && l = Float_t.zero -> of_const 0. (*tan(0) = 0*) | _ -> top () (**could be exact for intervals where l=h, or even for some intervals *) + let eval_inv_ceil = function + | (l, h) -> Interval (Float_t.floor (Float_t.pred l), h) + + let eval_inv_floor = function + | (l, h) -> Interval (l, Float_t.ceil (Float_t.succ h)) + + let eval_inv_fabs = function + | (_, h) when h > Float_t.zero -> Interval (Float_t.neg h, h) + | _ -> top () + let isfinite op = match op with | Bot -> raise (ArithmeticOnFloatBot (Printf.sprintf "unop %s" (show op))) @@ -733,6 +757,10 @@ module FloatIntervalImpl(Float_t : CFloatType) = struct let cos = eval_unop (top ()) eval_cos let sin = eval_unop (top ()) eval_sin let tan = eval_unop (top ()) eval_tan + + let inv_ceil = eval_unop (top ()) eval_inv_ceil + let inv_floor = eval_unop (top ()) eval_inv_floor + let inv_fabs = eval_unop (top ()) eval_inv_fabs end module F64Interval = FloatIntervalImpl(CDouble) @@ -761,11 +789,13 @@ module type FloatDomain = sig val starting : Cil.fkind -> float -> t val ending_before : Cil.fkind -> float -> t val starting_after : Cil.fkind -> float -> t + val finite : Cil.fkind -> t val minimal: t -> float option val maximal: t -> float option val is_exact : t -> bool + val is_interval : t -> bool val get_fkind : t -> Cil.fkind val invariant: Cil.exp -> t -> Invariant.t end @@ -836,6 +866,9 @@ module FloatIntervalImplLifted = struct let cos = lift (F1.cos, F2.cos) let sin = lift (F1.sin, F2.sin) let tan = lift (F1.tan, F2.tan) + let inv_ceil = lift (F1.inv_ceil, F2.inv_ceil) + let inv_floor = lift (F1.inv_floor, F2.inv_floor) + let inv_fabs = lift (F1.inv_fabs, F2.inv_fabs) let add = lift2 (F1.add, F2.add) let sub = lift2 (F1.sub, F2.sub) let mul = lift2 (F1.mul, F2.mul) @@ -860,7 +893,7 @@ module FloatIntervalImplLifted = struct let is_bot = dispatch (F1.is_bot, F2.is_bot) let top_of fkind = dispatch_fkind fkind (F1.top, F2.top) let top () = failwith "top () is not implemented for FloatIntervalImplLifted." - let is_top = dispatch (F1.is_bot, F2.is_bot) + let is_top = dispatch (F1.is_top, F2.is_top) let nan_of fkind = dispatch_fkind fkind (F1.nan, F2.nan) let is_nan = dispatch (F1.is_nan, F2.is_nan) @@ -869,6 +902,7 @@ module FloatIntervalImplLifted = struct let minus_inf_of fkind = dispatch_fkind fkind (F1.minus_inf, F2.minus_inf) let is_inf = dispatch (F1.is_inf, F2.is_inf) let is_neg_inf = dispatch (F1.is_minus_inf, F2.is_minus_inf) + let is_interval = dispatch (F1.is_interval, F2.is_interval) let get_fkind = function | F32 _ -> FFloat @@ -900,6 +934,7 @@ module FloatIntervalImplLifted = struct let of_interval fkind i = dispatch_fkind fkind ((fun () -> F1.of_interval i), (fun () -> F2.of_interval i)) let starting fkind s = dispatch_fkind fkind ((fun () -> F1.starting s), (fun () -> F2.starting s)) let starting_after fkind s = dispatch_fkind fkind ((fun () -> F1.starting_after s), (fun () -> F2.starting_after s)) + let finite fkind = dispatch_fkind fkind ((fun () -> F1.finite), (fun () -> F2.finite)) let ending fkind e = dispatch_fkind fkind ((fun () -> F1.ending e), (fun () -> F2.ending e)) let ending_before fkind e = dispatch_fkind fkind ((fun () -> F1.ending_before e), (fun () -> F2.ending_before e)) let minimal = dispatch (F1.minimal, F2.minimal) @@ -1003,6 +1038,8 @@ module FloatDomTupleImpl = struct create { fi= (fun (type a) (module F : FloatDomain with type t = a) -> F.ending_before fkind); } let starting_after fkind = create { fi= (fun (type a) (module F : FloatDomain with type t = a) -> F.starting_after fkind); } + let finite = + create { fi= (fun (type a) (module F : FloatDomain with type t = a) -> F.finite); } let of_string fkind = create { fi= (fun (type a) (module F : FloatDomain with type t = a) -> F.of_string fkind); } @@ -1031,6 +1068,9 @@ module FloatDomTupleImpl = struct let is_exact = exists % mapp { fp= (fun (type a) (module F : FloatDomain with type t = a) -> F.is_exact); } + let is_interval = + for_all + % mapp { fp= (fun (type a) (module F : FloatDomain with type t = a) -> F.is_interval); } let is_top = for_all % mapp { fp= (fun (type a) (module F : FloatDomain with type t = a) -> F.is_top); } @@ -1080,6 +1120,13 @@ module FloatDomTupleImpl = struct let tan = map { f1= (fun (type a) (module F : FloatDomain with type t = a) -> F.tan); } + let inv_ceil = + map { f1= (fun (type a) (module F : FloatDomain with type t = a) -> F.inv_ceil); } + let inv_floor = + map { f1= (fun (type a) (module F : FloatDomain with type t = a) -> F.inv_floor); } + let inv_fabs = + map { f1= (fun (type a) (module F : FloatDomain with type t = a) -> F.inv_fabs); } + (* f2: binary ops *) let join = map2 { f2= (fun (type a) (module F : FloatDomain with type t = a) -> F.join); } diff --git a/src/cdomains/floatDomain.mli b/src/cdomains/floatDomain.mli index 8be4304c5e..67ec9d17dd 100644 --- a/src/cdomains/floatDomain.mli +++ b/src/cdomains/floatDomain.mli @@ -42,6 +42,14 @@ module type FloatArith = sig val tan : t -> t (** tan(x) *) + (** {inversions of unary functions}*) + val inv_ceil : t -> t + (** (inv_ceil z -> x) if (z = ceil(x)) *) + val inv_floor : t -> t + (** (inv_floor z -> x) if (z = floor(x)) *) + val inv_fabs : t -> t + (** (inv_fabs z -> x) if (z = fabs(x)) *) + (** {b Comparison operators} *) val lt : t -> t -> IntDomain.IntDomTuple.t @@ -89,11 +97,13 @@ module type FloatDomainBase = sig val starting : float -> t val ending_before : float -> t val starting_after : float -> t + val finite : t val minimal: t -> float option val maximal: t -> float option val is_exact : t -> bool + val is_interval : t -> bool end (* Only exposed for testing *) @@ -123,11 +133,13 @@ module type FloatDomain = sig val starting : Cil.fkind -> float -> t val ending_before : Cil.fkind -> float -> t val starting_after : Cil.fkind -> float -> t + val finite : Cil.fkind -> t val minimal: t -> float option val maximal: t -> float option val is_exact : t -> bool + val is_interval : t -> bool val get_fkind : t -> Cil.fkind val invariant: Cil.exp -> t -> Invariant.t end diff --git a/tests/regression/57-floats/19-specialEq.c b/tests/regression/57-floats/19-specialEq.c index e9bf57f3aa..05eacbfa8d 100644 --- a/tests/regression/57-floats/19-specialEq.c +++ b/tests/regression/57-floats/19-specialEq.c @@ -4,31 +4,12 @@ void main() { - float f; - float g; - float common; - float* fptr; - float* gptr; - int unk1; - int unk2; - - float other; - - if (unk1) - fptr = &f; - else - fptr = &common; - - if (unk2) - gptr = &g; - else - gptr = &common; - - - - - other = __builtin_cos(*fptr); - *gptr = 0.7; - - __builtin_inf(); + double f; + int unk; + + if (__builtin_isnan(f) ) { + __goblint_check( __builtin_isfinite(f)); + } else { + __goblint_check( __builtin_isnan(f)); + } } \ No newline at end of file From 13ab91b2654e42c235e9fa8953ef370858ccfcdd Mon Sep 17 00:00:00 2001 From: FelixKrayer Date: Tue, 7 Mar 2023 22:53:17 +0100 Subject: [PATCH 0010/1312] tests --- src/cdomains/floatDomain.ml | 45 +++++++------ .../57-floats/19-library-invariant.c | 65 +++++++++++++++++++ tests/regression/57-floats/19-specialEq.c | 15 ----- .../20-library-invariant-invalidate.c | 15 +++++ 4 files changed, 106 insertions(+), 34 deletions(-) create mode 100644 tests/regression/57-floats/19-library-invariant.c delete mode 100644 tests/regression/57-floats/19-specialEq.c create mode 100644 tests/regression/57-floats/20-library-invariant-invalidate.c diff --git a/src/cdomains/floatDomain.ml b/src/cdomains/floatDomain.ml index 4bd1017696..a95926b29e 100644 --- a/src/cdomains/floatDomain.ml +++ b/src/cdomains/floatDomain.ml @@ -326,13 +326,13 @@ module FloatIntervalImpl(Float_t : CFloatType) = struct warn_on_special "Second operand" "comparison" op2 (** evaluation of the unary and binary operations *) - let eval_unop onTop eval_operation op = - warn_on_specials_unop op; + let eval_unop ?(warn=false) eval_operation op = + if warn then warn_on_specials_unop op; match op with | Bot -> raise (ArithmeticOnFloatBot (Printf.sprintf "unop %s" (show op))) | Interval v -> eval_operation v - | Top -> onTop - | _ -> onTop (* TODO: Do better *) + | Top -> top () + | _ -> top () (* TODO: Do better *) let eval_binop eval_operation v1 v2 = let is_exact_before = is_exact (Interval v1) && is_exact (Interval v2) in @@ -675,15 +675,15 @@ module FloatIntervalImpl(Float_t : CFloatType) = struct | (l, h) when l = h && l = Float_t.zero -> of_const 0. (*tan(0) = 0*) | _ -> top () (**could be exact for intervals where l=h, or even for some intervals *) - let eval_inv_ceil = function - | (l, h) -> Interval (Float_t.floor (Float_t.pred l), h) + let eval_inv_ceil = function (*TODO*) + | (l, h) -> Interval (Float_t.succ( Float_t.floor (Float_t.pred l)), h) - let eval_inv_floor = function - | (l, h) -> Interval (l, Float_t.ceil (Float_t.succ h)) + let eval_inv_floor = function (*TODO*) + | (l, h) -> Interval (l, Float_t.pred (Float_t.ceil (Float_t.succ h))) let eval_inv_fabs = function - | (_, h) when h > Float_t.zero -> Interval (Float_t.neg h, h) - | _ -> top () + | (_, h) when h < Float_t.zero -> Bot (* Result of fabs cannot be negative *) + | (_, h) -> Interval (Float_t.neg h, h) let isfinite op = match op with @@ -751,16 +751,23 @@ module FloatIntervalImpl(Float_t : CFloatType) = struct | PlusInfinity -> PlusInfinity | MinusInfinity -> MinusInfinity - let acos = eval_unop (top ()) eval_acos - let asin = eval_unop (top ()) eval_asin - let atan = eval_unop (top ()) eval_atan - let cos = eval_unop (top ()) eval_cos - let sin = eval_unop (top ()) eval_sin - let tan = eval_unop (top ()) eval_tan + let acos = eval_unop eval_acos + let asin = eval_unop eval_asin + let atan = eval_unop eval_atan + let cos = eval_unop eval_cos + let sin = eval_unop eval_sin + let tan = eval_unop eval_tan - let inv_ceil = eval_unop (top ()) eval_inv_ceil - let inv_floor = eval_unop (top ()) eval_inv_floor - let inv_fabs = eval_unop (top ()) eval_inv_fabs + let inv_ceil = eval_unop ~warn:false eval_inv_ceil + let inv_floor = eval_unop ~warn:false eval_inv_floor + let inv_fabs op = + match op with + | Bot -> raise (ArithmeticOnFloatBot (Printf.sprintf "unop %s" (show op))) + | Top -> Top + | Interval v -> eval_inv_fabs v + | NaN -> NaN (* so we assume, fabs(NaN) = NaN?)*) + | PlusInfinity -> Top (* +/-inf *) + | MinusInfinity -> Bot end module F64Interval = FloatIntervalImpl(CDouble) diff --git a/tests/regression/57-floats/19-library-invariant.c b/tests/regression/57-floats/19-library-invariant.c new file mode 100644 index 0000000000..b60fe1ef18 --- /dev/null +++ b/tests/regression/57-floats/19-library-invariant.c @@ -0,0 +1,65 @@ +//PARAM: --enable ana.float.interval --set ana.activated[+] tmpSpecial +#include +#include +#include + +void main() { + double f, g; + double x; + int unk; + + // isnan, isfinite + if(__builtin_isfinite(f)) { + __goblint_check(__builtin_isfinite(f)); + __goblint_check(! __builtin_isnan(f)); + } + if(__builtin_isnan(f)) { + __goblint_check(__builtin_isnan(f)); + __goblint_check(! __builtin_isfinite(f)); + } + + // Comparison + x = (unk) ? -100. : 100.; + if(__builtin_isgreater(x, 0.)) { + __goblint_check(x > 0.); + } + if(__builtin_isgreaterequal(x, 0.)) { + __goblint_check(x >= 0.); + } + if(__builtin_isless(x, 0.)) { + __goblint_check(x < 0.); + } + if(__builtin_islessequal(x, 0.)) { + __goblint_check(x <= 0.); + } + if(__builtin_islessgreater(x, 0.)) { + __goblint_check(x < 0. || x > 0.); // UNKNOWN + } + + // fabs + if(__builtin_fabs(f) == 4.) { + __goblint_check(f >= -4.); + __goblint_check(f <= 4.); + } + g = (unk) ? (3.) : (5.); + if(__builtin_fabs(f) == g) { + __goblint_check(f >= -5.); + __goblint_check(f <= 5.); + } + if(__builtin_fabs(f) == -6.) { + // DEAD + g = 0.; + } + + // ceil, floor + if(ceil(f) == 5.) { + __goblint_check(f <= 5.); + __goblint_check(f > 4.); + __goblint_check(f >= 4.5); // UNKNOWN! + } + if(floor(f) == 5.) { + __goblint_check(f >= 5.); + __goblint_check(f < 6.); + __goblint_check(f >= 5.5); // UNKNOWN! + } +} diff --git a/tests/regression/57-floats/19-specialEq.c b/tests/regression/57-floats/19-specialEq.c deleted file mode 100644 index 05eacbfa8d..0000000000 --- a/tests/regression/57-floats/19-specialEq.c +++ /dev/null @@ -1,15 +0,0 @@ -//PARAM: --set ana.activated[+] tmpSpecial -#include -#include - - -void main() { - double f; - int unk; - - if (__builtin_isnan(f) ) { - __goblint_check( __builtin_isfinite(f)); - } else { - __goblint_check( __builtin_isnan(f)); - } -} \ No newline at end of file diff --git a/tests/regression/57-floats/20-library-invariant-invalidate.c b/tests/regression/57-floats/20-library-invariant-invalidate.c new file mode 100644 index 0000000000..7a93264c9d --- /dev/null +++ b/tests/regression/57-floats/20-library-invariant-invalidate.c @@ -0,0 +1,15 @@ +//PARAM: --enable ana.float.interval --set ana.activated[+] tmpSpecial +#include +#include + +void main() { + double f, g; + int unk; + + g = __builtin_fabs(f); + f = 7.; + + if(g == 5.) { + __goblint_check(f <= 5.); // FAIL + } +} From 6ca138fe69337246709d2e902cf3ac8d0ee910dd Mon Sep 17 00:00:00 2001 From: FelixKrayer Date: Thu, 9 Mar 2023 18:36:30 +0100 Subject: [PATCH 0011/1312] sound but less precise inversions of ceil/floor --- src/cdomains/floatDomain.ml | 8 ++++---- tests/regression/57-floats/19-library-invariant.c | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/cdomains/floatDomain.ml b/src/cdomains/floatDomain.ml index a95926b29e..ca017111c1 100644 --- a/src/cdomains/floatDomain.ml +++ b/src/cdomains/floatDomain.ml @@ -675,11 +675,11 @@ module FloatIntervalImpl(Float_t : CFloatType) = struct | (l, h) when l = h && l = Float_t.zero -> of_const 0. (*tan(0) = 0*) | _ -> top () (**could be exact for intervals where l=h, or even for some intervals *) - let eval_inv_ceil = function (*TODO*) - | (l, h) -> Interval (Float_t.succ( Float_t.floor (Float_t.pred l)), h) + let eval_inv_ceil = function (*TODO: can probably be more precise*) + | (l, h) -> Interval (Float_t.lower_bound, h) - let eval_inv_floor = function (*TODO*) - | (l, h) -> Interval (l, Float_t.pred (Float_t.ceil (Float_t.succ h))) + let eval_inv_floor = function (*TODO: can probably be more precise*) + | (l, h) -> Interval (l, Float_t.upper_bound) let eval_inv_fabs = function | (_, h) when h < Float_t.zero -> Bot (* Result of fabs cannot be negative *) diff --git a/tests/regression/57-floats/19-library-invariant.c b/tests/regression/57-floats/19-library-invariant.c index b60fe1ef18..535a306174 100644 --- a/tests/regression/57-floats/19-library-invariant.c +++ b/tests/regression/57-floats/19-library-invariant.c @@ -54,12 +54,12 @@ void main() { // ceil, floor if(ceil(f) == 5.) { __goblint_check(f <= 5.); - __goblint_check(f > 4.); + __goblint_check(f > 4.); // TODO __goblint_check(f >= 4.5); // UNKNOWN! } if(floor(f) == 5.) { __goblint_check(f >= 5.); - __goblint_check(f < 6.); + __goblint_check(f < 6.); // TODO __goblint_check(f >= 5.5); // UNKNOWN! } } From 7849a7aef27123f323ecaa6442f8882f9b04bc65 Mon Sep 17 00:00:00 2001 From: FelixKrayer Date: Tue, 14 Mar 2023 19:13:21 +0100 Subject: [PATCH 0012/1312] Use lval instead of var for dependencies --- src/analyses/baseInvariant.ml | 12 ++++++------ src/analyses/tmpSpecial.ml | 25 +++++++++++++------------ src/domains/queries.ml | 8 ++++---- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index 92204d9bea..b0ae6fef93 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -702,10 +702,10 @@ struct begin match t with | TInt (ik, _) -> begin match x with - | ((Var v), _) -> - if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial v)); + | ((Var v), offs) -> + if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, TmpSpecial.Spec.resolve offs))); let tv = not (ID.leq c (ID.of_bool ik false)) in - begin match ctx.ask (Queries.TmpSpecial v) with + begin match ctx.ask (Queries.TmpSpecial (v, TmpSpecial.Spec.resolve offs)) with | `Lifted (Isfinite xFloat) when tv -> inv_exp (`Float (FD.finite (unroll_fk_of_exp xFloat))) xFloat st | `Lifted (Isnan xFloat) when tv -> inv_exp (`Float (FD.nan_of (unroll_fk_of_exp xFloat))) xFloat st (* should be correct according to C99 standard*) @@ -735,9 +735,9 @@ struct begin match t with | TFloat (fk, _) -> begin match x with - | ((Var v), _) -> - if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial v)); - begin match ctx.ask (Queries.TmpSpecial v) with + | ((Var v), offs) -> + if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, TmpSpecial.Spec.resolve offs))); + begin match ctx.ask (Queries.TmpSpecial (v, TmpSpecial.Spec.resolve offs)) with | `Lifted (Ceil (ret_fk, xFloat)) when FD.is_interval c -> inv_exp (`Float (FD.inv_ceil (FD.cast_to ret_fk c))) xFloat st | `Lifted (Floor (ret_fk, xFloat)) when FD.is_interval c -> inv_exp (`Float (FD.inv_floor (FD.cast_to ret_fk c))) xFloat st | `Lifted (Fabs (ret_fk, xFloat)) -> diff --git a/src/analyses/tmpSpecial.ml b/src/analyses/tmpSpecial.ml index b5bda86289..7e5d636e41 100644 --- a/src/analyses/tmpSpecial.ml +++ b/src/analyses/tmpSpecial.ml @@ -12,7 +12,7 @@ struct module ML = LibraryDesc.MathLifted module LS = SetDomain.ToppedSet(Lval.CilLval) (struct let topname = "All" end) module MlLsProd = Lattice.Prod (ML) (LS) - module D = MapDomain.MapBot (Basetype.Variables) (MlLsProd) + module D = MapDomain.MapBot (Lval.CilLval) (MlLsProd) module C = Lattice.Unit let rec resolve (offs : offset) : (CilType.Fieldinfo.t, Basetype.CilExp.t) Lval.offs = @@ -52,7 +52,8 @@ struct let assign ctx (lval:lval) (rval:exp) : D.t = (* get the set of all lvals written by the assign. Then remove all entrys from the map where the dependencies overlap with the set of written lvals *) let lvalsWritten = ls_of_lv ctx lval in - D.filter (fun v (ml, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) ctx.local + if M.tracing then M.trace "tmpSpecial" "lvalsWritten %a\n" LS.pretty lvalsWritten; + D.filter (fun _ (ml, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) ctx.local let branch ctx (exp:exp) (tv:bool) : D.t = ctx.local @@ -74,19 +75,19 @@ struct (* Just dbg prints *) (match lval with - | Some (Var v, offs) -> if M.tracing then M.tracel "tmpSpecial" "Special: %s with\n lval %a; vinfo %a; attr %a \n" f.vname d_lval (Var v, offs) d_varinfo v d_attrlist v.vattr + | Some lv -> if M.tracing then M.tracel "tmpSpecial" "Special: %s with\n lval %a\n" f.vname d_lval lv | _ -> if M.tracing then M.tracel "tmpSpecial" "Special: %s\n" f.vname); let desc = LibraryFunctions.find f in - (* add new math fun dec*) + (* add new math fun desc*) let d = match lval, desc.special arglist with - | (Some (Var v, _)), (Math { fun_args; }) -> + | Some (Var v, offs), (Math { fun_args; }) -> let lvalDep = List.fold LS.union (LS.empty ()) (List.map (ls_of_exp ctx) arglist) in if LS.is_top lvalDep then d else - D.add v ((ML.lift fun_args, lvalDep)) d - | _ -> d + D.add (v, resolve offs) ((ML.lift fun_args, lvalDep)) d + | _ -> d in (* remove entrys, dependent on lvals that were possibly written by the special function *) @@ -107,20 +108,20 @@ struct in let d = List.fold_left (fun accD addr -> let lvalsWritten = ctx.ask (Queries.MayPointTo addr) in - D.filter (fun v (_, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) accD) d shallow_addrs + D.filter (fun _ (_, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) accD) d shallow_addrs in let d = List.fold_left (fun accD addr -> let lvalsWritten = ctx.ask (Queries.ReachableFrom addr) in - D.filter (fun v (_, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) accD) d deep_addrs + D.filter (fun _ (_, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) accD) d deep_addrs in - (* same for lval assignment of the call (though this should always be a "tmp___n")*) + (* same for lval assignment of the call*) let d = match lval with | Some lv -> ( (* get the set of all lvals written by the assign. Then remove all entrys from the map where the dependencies overlap with the set of written lvals *) let lvalsWritten = ls_of_lv ctx lv in - D.filter (fun v (ml, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) d + D.filter (fun _ (ml, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) d ) | None -> d in @@ -129,7 +130,7 @@ struct let query ctx (type a) (q: a Queries.t) : a Queries.result = match q with - | TmpSpecial v -> let ml = fst (D.find v ctx.local) in + | TmpSpecial lv -> let ml = fst (D.find lv ctx.local) in if ML.is_bot ml then Queries.Result.top q else ml | _ -> Queries.Result.top q diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 811b3f74c3..1d85c86070 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -101,7 +101,7 @@ type _ t = | MayAccessed: AccessDomain.EventSet.t t | MayBeTainted: LS.t t | MayBeModifiedSinceSetjmp: JmpBufDomain.BufferEntry.t -> VS.t t - | TmpSpecial: varinfo -> ML.t t + | TmpSpecial: Lval.CilLval.t -> ML.t t type 'a result = 'a @@ -322,7 +322,7 @@ struct | Any (IterSysVars (vq1, vf1)), Any (IterSysVars (vq2, vf2)) -> VarQuery.compare vq1 vq2 (* not comparing fs *) | Any (MustProtectedVars m1), Any (MustProtectedVars m2) -> compare_mustprotectedvars m1 m2 | Any (MayBeModifiedSinceSetjmp e1), Any (MayBeModifiedSinceSetjmp e2) -> JmpBufDomain.BufferEntry.compare e1 e2 - | Any (TmpSpecial vi1), Any (TmpSpecial vi2) -> CilType.Varinfo.compare vi1 vi2 + | Any (TmpSpecial lv1), Any (TmpSpecial lv2) -> Lval.CilLval.compare lv1 lv2 (* only argumentless queries should remain *) | _, _ -> Stdlib.compare (order a) (order b) @@ -358,7 +358,7 @@ struct | Any (InvariantGlobal vi) -> Hashtbl.hash vi | Any (MustProtectedVars m) -> hash_mustprotectedvars m | Any (MayBeModifiedSinceSetjmp e) -> JmpBufDomain.BufferEntry.hash e - | Any (TmpSpecial vi) -> CilType.Varinfo.hash vi + | Any (TmpSpecial lv) -> Lval.CilLval.hash lv (* IterSysVars: *) (* - argument is a function and functions cannot be compared in any meaningful way. *) (* - doesn't matter because IterSysVars is always queried from outside of the analysis, so MCP's query caching is not done for it. *) @@ -412,7 +412,7 @@ struct | Any MayBeTainted -> Pretty.dprintf "MayBeTainted" | Any DYojson -> Pretty.dprintf "DYojson" | Any MayBeModifiedSinceSetjmp buf -> Pretty.dprintf "MayBeModifiedSinceSetjmp %a" JmpBufDomain.BufferEntry.pretty buf - | Any (TmpSpecial vi) -> Pretty.dprintf "TmpSpecial %a" CilType.Varinfo.pretty vi + | Any (TmpSpecial lv) -> Pretty.dprintf "TmpSpecial %a" Lval.CilLval.pretty lv end let to_value_domain_ask (ask: ask) = From 66404c4993456d55b52e50f332185d3a377537d0 Mon Sep 17 00:00:00 2001 From: FelixKrayer Date: Sun, 7 May 2023 15:25:06 +0200 Subject: [PATCH 0013/1312] fix invalidation --- src/analyses/tmpSpecial.ml | 30 +++++++++++------- .../20-library-invariant-invalidate.c | 31 +++++++++++++++---- 2 files changed, 43 insertions(+), 18 deletions(-) diff --git a/src/analyses/tmpSpecial.ml b/src/analyses/tmpSpecial.ml index 7e5d636e41..c2042cd3d4 100644 --- a/src/analyses/tmpSpecial.ml +++ b/src/analyses/tmpSpecial.ml @@ -75,20 +75,9 @@ struct (* Just dbg prints *) (match lval with - | Some lv -> if M.tracing then M.tracel "tmpSpecial" "Special: %s with\n lval %a\n" f.vname d_lval lv + | Some lv -> if M.tracing then M.tracel "tmpSpecial" "Special: %s with lval %a\n" f.vname d_lval lv | _ -> if M.tracing then M.tracel "tmpSpecial" "Special: %s\n" f.vname); let desc = LibraryFunctions.find f in - (* add new math fun desc*) - let d = - match lval, desc.special arglist with - | Some (Var v, offs), (Math { fun_args; }) -> - let lvalDep = List.fold LS.union (LS.empty ()) (List.map (ls_of_exp ctx) arglist) in - if LS.is_top lvalDep then - d - else - D.add (v, resolve offs) ((ML.lift fun_args, lvalDep)) d - | _ -> d - in (* remove entrys, dependent on lvals that were possibly written by the special function *) let shallow_addrs = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = false } arglist in @@ -125,9 +114,26 @@ struct ) | None -> d in + + (* add new math fun desc*) + let d = + match lval, desc.special arglist with + | Some (Var v, offs), (Math { fun_args; }) -> + let argsDep = List.fold LS.union (LS.empty ()) (List.map (ls_of_exp ctx) arglist) in + let lvalsWritten = ls_of_lv ctx (Var v, offs) in + (* only add descriptor, if the set of lvals contained in the args is known and none is written by the assignment *) + (* actually it would be necessary to check here, if one of the arguments is written by the call. However this is not the case for any of the math functions and no other functions are covered so far *) + if LS.is_top argsDep || not (LS.is_empty (LS.meet argsDep lvalsWritten)) then + d + else + D.add (v, resolve offs) ((ML.lift fun_args, LS.union argsDep lvalsWritten)) d + | _ -> d + in + if M.tracing then M.tracel "tmpSpecial" "Result: %a\n\n" D.pretty d; d + let query ctx (type a) (q: a Queries.t) : a Queries.result = match q with | TmpSpecial lv -> let ml = fst (D.find lv ctx.local) in diff --git a/tests/regression/57-floats/20-library-invariant-invalidate.c b/tests/regression/57-floats/20-library-invariant-invalidate.c index 7a93264c9d..bc00279af3 100644 --- a/tests/regression/57-floats/20-library-invariant-invalidate.c +++ b/tests/regression/57-floats/20-library-invariant-invalidate.c @@ -3,13 +3,32 @@ #include void main() { - double f, g; - int unk; + double f1, g1; + double f2, g2; + double unk_double; + double f3; - g = __builtin_fabs(f); - f = 7.; + // example 1: + g1 = __builtin_fabs(f1); + f1 = 7.; - if(g == 5.) { - __goblint_check(f <= 5.); // FAIL + if(g1 == 5.) { + __goblint_check(f1 <= 5.); // FAIL + } + + // example 2: + g2 = __builtin_fabs(f2); + g2 = unk_double; + + if(g2 == 5.) { + __goblint_check(f2 <= 5.); // UNKNOWN! + } + + // example 3: + // the check is not interesting, this only exists to make sure the analyzer can handle this case and terminates + f3 = __builtin_fabs(f3); + + if(f3 == 0.) { + __goblint_check(f3 <= 5.); } } From d99608a1e8cb1dd09ae9212c592699050cf65c9f Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 8 May 2023 14:50:49 +0200 Subject: [PATCH 0014/1312] Fix issues after merge --- src/analyses/tmpSpecial.ml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/analyses/tmpSpecial.ml b/src/analyses/tmpSpecial.ml index c2042cd3d4..8df101d7c1 100644 --- a/src/analyses/tmpSpecial.ml +++ b/src/analyses/tmpSpecial.ml @@ -1,7 +1,7 @@ (* Analysis that tracks which variables hold the results of calls to math library functions. For each equivalence a set of lvals is tracked, that contains all lvals on which the arguments of the corresponding call depend, so an equivalence can be removed if one of the lvals is written.*) -open Prelude.Ana +open GoblintCil open Analyses module Spec = @@ -26,7 +26,7 @@ struct | (Var v, offs) -> LS.of_list [(v, resolve offs)] | (Mem e, _) -> (ctx.ask (Queries.MayPointTo e)) - let rec ls_of_exp ctx (exp:exp) : LS.t = + let rec ls_of_exp ctx (exp:exp) : LS.t = match exp with | Const _ -> LS.bot () | Lval lv -> ls_of_lv ctx lv @@ -95,11 +95,11 @@ struct else deep_addrs in - let d = List.fold_left (fun accD addr -> + let d = List.fold_left (fun accD addr -> let lvalsWritten = ctx.ask (Queries.MayPointTo addr) in D.filter (fun _ (_, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) accD) d shallow_addrs in - let d = List.fold_left (fun accD addr -> + let d = List.fold_left (fun accD addr -> let lvalsWritten = ctx.ask (Queries.ReachableFrom addr) in D.filter (fun _ (_, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) accD) d deep_addrs in @@ -112,14 +112,14 @@ struct let lvalsWritten = ls_of_lv ctx lv in D.filter (fun _ (ml, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) d ) - | None -> d + | None -> d in (* add new math fun desc*) let d = match lval, desc.special arglist with - | Some (Var v, offs), (Math { fun_args; }) -> - let argsDep = List.fold LS.union (LS.empty ()) (List.map (ls_of_exp ctx) arglist) in + | Some (Var v, offs), (Math { fun_args; }) -> + let argsDep = List.fold_left LS.union (LS.empty ()) (List.map (ls_of_exp ctx) arglist) in let lvalsWritten = ls_of_lv ctx (Var v, offs) in (* only add descriptor, if the set of lvals contained in the args is known and none is written by the assignment *) (* actually it would be necessary to check here, if one of the arguments is written by the call. However this is not the case for any of the math functions and no other functions are covered so far *) From 5510982c7986b9d0c08c02bdc5e8823d05d29b23 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Thu, 11 May 2023 11:38:04 +0200 Subject: [PATCH 0015/1312] added comments to the termination.ml file; TODO: some things are still unclear and have to be added later --- src/analyses/termination.ml | 171 ++++++++++++++++++++++++++---------- 1 file changed, 126 insertions(+), 45 deletions(-) diff --git a/src/analyses/termination.ml b/src/analyses/termination.ml index 37ee8bc9ef..c5f2ca7f7d 100644 --- a/src/analyses/termination.ml +++ b/src/analyses/termination.ml @@ -4,6 +4,8 @@ open Prelude.Ana open Analyses module M = Messages +(* J: returns if a and b contain a value + if yes: return this x, otherwise nothing *) let (||?) a b = match a,b with Some x,_ | _, Some x -> Some x | _ -> None module TermDomain = struct @@ -11,15 +13,22 @@ module TermDomain = struct end (* some kind of location string suitable for variable names? *) +(* J: returns a string_ "lineNr_columnNr" *) +(* J: for location (10,5) it evaluates to: 10_5*) let show_location_id l = string_of_int l.line ^ "_" ^ string_of_int l.column +(* J: the new variable is created here and inserted into the code + in the code the variable is set to 0 (before the loop) and incremented (after the loop) + is it ever checked if the newly created variable is really new???*) +(* J: ??? Who defines the Loop, what are the variables*) class loopCounterVisitor (fd : fundec) = object(self) inherit nopCilVisitor method! vstmt s = let action s = match s.skind with | Loop (b, loc, eloc, _, _) -> (* insert loop counter variable *) + (* J: for location (10,5) it evaluates to: term10_5*) let name = "term"^show_location_id loc in let typ = intType in (* TODO the type should be the same as the one of the original loop counter *) let v = Goblintutil.create_var (makeLocalVar fd name ~init:(SingleInit zero) typ) in @@ -28,14 +37,20 @@ class loopCounterVisitor (fd : fundec) = object(self) (* increment it every iteration *) let inc_stmt = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in b.bstmts <- inc_stmt :: b.bstmts; - let nb = mkBlock [init_stmt; mkStmt s.skind] in + let nb = mkBlock [init_stmt; mkStmt s.skind] in (* J: s.kind = Loop(b, loc, eloc, ...)*) s.skind <- Block nb; s | _ -> s in ChangeDoChildrenPost (s, action) end +(* J: creates a new hash table with size 13 for loop breaks*) +(* J: int: is a number which is unique in a function*) +(* J: ??? Why 13*) let loopBreaks : (int, location) Hashtbl.t = Hashtbl.create 13 (* break stmt sid -> corresponding loop *) +(* J: if there is some break associated with the loop (?) we add this break to the hash table + key = break.sid + data = location *) class loopBreaksVisitor (fd : fundec) = object(self) inherit nopCilVisitor method! vstmt s = @@ -47,10 +62,15 @@ class loopBreaksVisitor (fd : fundec) = object(self) end (* if the given block contains a goto while_break.* we have the termination condition for a loop *) +(* J: returns binding from loopBreaks hash table associated with a number which is uniquely identifie with the statement + if there is a Goto, otherwise nothing*) +(* J: stmt.sid = A number (>= 0) that is unique in a function. *) +(* J: ???*) let exits = function | { bstmts = [{ skind = Goto (stmt, loc); _ }]; _ } -> Hashtbl.find_option loopBreaks !stmt.sid | _ -> None (* TODO handle return (need to find out what loop we are in) *) +(* J: ???*) let lvals_of_expr = let rec f a = function | Const _ | SizeOf _ | SizeOfStr _ | AlignOf _ | AddrOfLabel _ -> a @@ -60,125 +80,186 @@ let lvals_of_expr = | Question (c,t,e,_) -> f a c @ f a t @ f a e in f [] +(* J: create hash table of size 13 for variables*) let loopVars : (location, lval) Hashtbl.t = Hashtbl.create 13 (* loop location -> lval used for exit *) +(* J: adds the location and left varibale to the loopVars, if one block of the if statement contains a goto block*) class loopVarsVisitor (fd : fundec) = object inherit nopCilVisitor method! vstmt s = let add_exit_cond e loc = match lvals_of_expr e with + (* J: ??? Same as when isArithmeticType Cilfacade.typeOf e*) | [lval] when Cilfacade.typeOf e |> isArithmeticType -> Hashtbl.add loopVars loc lval + (* J : add lval to hash table when the expression on location loc is of arithmetic type*) | _ -> () in (match s.skind with + (* J: map_default f x (Some v) returns f v and map_default f x None returns x.*) + (* J: If there exists a goto statement: call add_exit_cond e (SOME exits tb ||? exits fb) + If e is of arithmetic type: add the location to the loopVars hash table *) | If (e, tb, fb, loc, eloc) -> Option.map_default (add_exit_cond e) () (exits tb ||? exits fb) | _ -> ()); DoChildren end +(* J: ??? visits the expression e and removes all casts from the expression*) let stripCastsDeep e = let v = object inherit nopCilVisitor + (* J: ChangeTo: Replace the expression with the given one*) + (* J: Removes casts from this expression, but ignores casts within other expression constructs. + So we delete the (A) and (B) casts from "(A)(B)(x + (C)y)", but leave the (C) cast.*) method! vexpr e = ChangeTo (stripCasts e) end in visitCilExpr v e (* keep the enclosing loop for statements *) +(* J: store pointer pointing to Nothing for loops*) let cur_loop = ref None (* current loop *) let cur_loop' = ref None (* for nested loops *) +(* J: searches if the variable name___ for the given location is present in the function definition + if not: the variable is created and initialized to 0*) let makeVar fd loc name = + (* J: for location = (10,5) and name = "hi" the id evaluates to: hi__10_5*) let id = name ^ "__" ^ show_location_id loc in + (* J: fd.slocals = Locals of the function definition*) + (* J: returns the first element which is a local and which name is id (for example hi__10_5)*) try List.find (fun v -> v.vname = id) fd.slocals + (* J: when the variable is not found in the function definition then it is newly created and initialized with 0*) with Not_found -> let typ = intType in (* TODO the type should be the same as the one of the original loop counter *) Goblintutil.create_var (makeLocalVar fd id ~init:(SingleInit zero) typ) +(* J: creates an empty function with name "__goblint_assume" and makes a lvalue out of it*) let f_assume = Lval (var (emptyFunction "__goblint_assume").svar) +(* J: creates an empty function with name "__goblint_check" and makes a lvalue out of it*) let f_check = Lval (var (emptyFunction "__goblint_check").svar) +(* J: ??? Loop pointer handling: Why do we fist set cur_loop' = cur_loop and then reverse this operation???*) +(* J: inserts new variable t with init and increment and adds a check if t is bounded*) class loopInstrVisitor (fd : fundec) = object(self) inherit nopCilVisitor method! vstmt s = (* TODO: use Loop eloc? *) (match s.skind with + (* J: if the statement is a loop adjust the loop pointer*) | Loop (_, loc, eloc, _, _) -> - cur_loop' := !cur_loop; - cur_loop := Some loc + cur_loop' := !cur_loop; (* J: set the nested loop to the current loop*) + cur_loop := Some loc (* J: set the newly found loop as current loop*) | _ -> ()); + (* J: adds the variable t to the loop, increments it and checks after the loop if it is bounded + adds also the variables d1 and d2, with incrementation, which affects t + depending on the structure, d1 and d2 are set differently*) let action s = (* first, restore old cur_loop *) + (* J: if the statement is a loop set the nested loop as the current loop*) (match s.skind with | Loop (_, loc, eloc, _, _) -> - cur_loop := !cur_loop'; + cur_loop := !cur_loop'; (* J: current loop is the nested loop*) | _ -> ()); + (* J: true if the current loop variable is set and this variable is bound in the hash table*) let in_loop () = Option.is_some !cur_loop && Hashtbl.mem loopVars (Option.get !cur_loop) in + (* J: t is the new variable which should be bounded + if there is a loop with bounded variable: add code for init and increment (for t, d1, d2) + if there is a loop with unbounded variable: do nothing + if a loop ended: check if t is bounded + if there is an instruction with an assignment: increment d1 and d2 if the assignment affects the loop var + do this recursively for all children*) match s.skind with + (* J: if the statement is a loop, and when the location is bound in the hash table: + - add the creational and initializational code for t, d1, d2 before the loop + - add the incrementational code for t, d1, d2 in the loop + *) | Loop (b, loc, eloc, Some continue, Some break) when Hashtbl.mem loopVars loc -> (* find loop var for current loop *) let x = Hashtbl.find loopVars loc in (* insert loop counter and diff to loop var *) + (* J: create variables t, d1, d2 with names: t___, d1___, d2___*) let t = var @@ makeVar fd loc "t" in let d1 = var @@ makeVar fd loc "d1" in let d2 = var @@ makeVar fd loc "d2" in (* make init stmts *) + (* J: set t=0, d1, d2 = lvalue of x = value in the hashtable associated with the loop variable*) let t_init = mkStmtOneInstr @@ Set (t, zero, loc, eloc) in let d1_init = mkStmtOneInstr @@ Set (d1, Lval x, loc, eloc) in let d2_init = mkStmtOneInstr @@ Set (d2, Lval x, loc, eloc) in (* increment/decrement in every iteration *) - let t_inc = mkStmtOneInstr @@ Set (t, increm (Lval t) 1, loc, eloc) in - let d1_inc = mkStmtOneInstr @@ Set (d1, increm (Lval d1) (-1), loc, eloc) in - let d2_inc = mkStmtOneInstr @@ Set (d2, increm (Lval d2) 1 , loc, eloc) in - let typ = intType in - let e1 = BinOp (Eq, Lval t, BinOp (MinusA, Lval x, Lval d1, typ), typ) in - let e2 = BinOp (Eq, Lval t, BinOp (MinusA, Lval d2, Lval x, typ), typ) in - let inv1 = mkStmtOneInstr @@ Call (None, f_assume, [e1], loc, eloc) in + (* J: increment t and d2, decrement d1*) + let t_inc = mkStmtOneInstr @@ Set (t, increm (Lval t) 1, loc, eloc) in (* J: t = t + 1*) + let d1_inc = mkStmtOneInstr @@ Set (d1, increm (Lval d1) (-1), loc, eloc) in (* J: d1 = d1 - 1*) + let d2_inc = mkStmtOneInstr @@ Set (d2, increm (Lval d2) 1 , loc, eloc) in (* J: d2 = d2 + 1*) + let typ = intType in (* J: Note: x is the loop variable*) + let e1 = BinOp (Eq, Lval t, BinOp (MinusA, Lval x, Lval d1, typ), typ) in (* J: t = (x - d1) *) + let e2 = BinOp (Eq, Lval t, BinOp (MinusA, Lval d2, Lval x, typ), typ) in (* J: t = (d2 - x) *) + (* J: make a statement for e1 and e2*) + (* J: ??? what happens with the call*) + let inv1 = mkStmtOneInstr @@ Call (None, f_assume, [e1], loc, eloc) in let inv2 = mkStmtOneInstr @@ Call (None, f_assume, [e2], loc, eloc) in - (match b.bstmts with + (match b.bstmts with (* J: we are still in a loop*) | cont :: cond :: ss -> (* changing succs/preds directly doesn't work -> need to replace whole stmts *) - b.bstmts <- cont :: cond :: inv1 :: inv2 :: d1_inc :: d2_inc :: t_inc :: ss; - let nb = mkBlock [t_init; d1_init; d2_init; mkStmt s.skind] in - s.skind <- Block nb; + (* from: cont :: cond :: ss + to: cont :: + cond :: + t = (x - d1) :: t = (d2 - x) :: (??? Is this correct with the call???) + d1 = d1 - 1 :: d2 = d2 + 1 :: t = t + 1 :: + ss + *) + b.bstmts <- cont :: cond :: inv1 :: inv2 :: d1_inc :: d2_inc :: t_inc :: ss; (* J: in the loop*) + let nb = mkBlock [t_init; d1_init; d2_init; mkStmt s.skind] in (* J: make a block out of the init statements before the loop*) + s.skind <- Block nb; | _ -> ()); - s + s (* J: return s with added code for init and increment*) + (* J: if the variable in the loops is not bounded, it is not possible to continue*) | Loop (b, loc, eloc, Some continue, Some break) -> print_endline @@ "WARN: Could not determine loop variable for loop at " ^ CilType.Location.show loc; s + (* J: when the statement is not a loop and a loop ended: + - add t >= 0 in the code after the loop*) | _ when Hashtbl.mem loopBreaks s.sid -> (* after a loop, we check that t is bounded/positive (no overflow happened) *) - let loc = Hashtbl.find loopBreaks s.sid in - let t = var @@ makeVar fd loc "t" in - let e3 = BinOp (Ge, Lval t, zero, intType) in - let inv3 = mkStmtOneInstr @@ Call (None, f_check, [e3], loc, locUnknown) in - let nb = mkBlock [mkStmt s.skind; inv3] in + let loc = Hashtbl.find loopBreaks s.sid in (* J: holds the current binding of the number of the current function in the hash table*) + let t = var @@ makeVar fd loc "t" in (* J: get the name for variable t = t___*) + let e3 = BinOp (Ge, Lval t, zero, intType) in (* J: t >= 0*) + let inv3 = mkStmtOneInstr @@ Call (None, f_check, [e3], loc, locUnknown) in (* J: make a statement to check t >= 0*) + let nb = mkBlock [mkStmt s.skind; inv3] in (* J: add the statement to the block*) s.skind <- Block nb; - s + s (* J: return s with the added check*) + (* J: If there is an instruction (containing an assignment) and it is in a loop: + If the loop variable and lvalue of Set are structural unequal + Do nothing + else + add an incrementation step for d1 and d2 (depending on the binOp)*) | Instr [Set (lval, e, loc, eloc)] when in_loop () -> (* find loop var for current loop *) let cur_loop = Option.get !cur_loop in - let x = Hashtbl.find loopVars cur_loop in - if x <> lval then + let x = Hashtbl.find loopVars cur_loop in (* J: holds the current binding of the number of the current function in the hash table*) + if x <> lval then (* J: x and lval are structural unequal*) s else (* we only care about the loop var *) + (* J: create the variables d1 and d2 with name: d1___, d2___*) let d1 = makeVar fd cur_loop "d1" in let d2 = makeVar fd cur_loop "d2" in (match stripCastsDeep e with + (* J: if x' + e2 or x' - e2 with x' = x and the type arithmetic: + - adds incrementation for d1 and d2 to the code*) | BinOp (op, Lval x', e2, typ) when (op = PlusA || op = MinusA) && x' = x && isArithmeticType typ -> (* TODO x = 1 + x, MinusA! *) (* increase diff by same expr *) - let d1_inc = mkStmtOneInstr @@ Set (var d1, BinOp (PlusA, Lval (var d1), e2, typ), loc, eloc) in - let d2_inc = mkStmtOneInstr @@ Set (var d2, BinOp (PlusA, Lval (var d2), e2, typ), loc, eloc) in - let nb = mkBlock [d1_inc; d2_inc; mkStmt s.skind] in + let d1_inc = mkStmtOneInstr @@ Set (var d1, BinOp (PlusA, Lval (var d1), e2, typ), loc, eloc) in (* J: d1 = d1 + e2*) + let d2_inc = mkStmtOneInstr @@ Set (var d2, BinOp (PlusA, Lval (var d2), e2, typ), loc, eloc) in (* J: d2 = d2 + e2*) + let nb = mkBlock [d1_inc; d2_inc; mkStmt s.skind] in (* J: add the incrementation steps at the front*) s.skind <- Block nb; - s + s (* J: return s with the added incrementation*) | _ -> (* otherwise diff is e - counter *) - let t = makeVar fd cur_loop "t" in - let te = Cilfacade.typeOf e in - let dt1 = mkStmtOneInstr @@ Set (var d1, BinOp (MinusA, Lval x, Lval (var t), te), loc, eloc) in - let dt2 = mkStmtOneInstr @@ Set (var d2, BinOp (MinusA, Lval x, Lval (var t), te), loc, eloc) in - let nb = mkBlock [mkStmt s.skind; dt1; dt2] in + let t = makeVar fd cur_loop "t" in (* J: varibale name for t*) + let te = Cilfacade.typeOf e in (* J: type of e*) + let dt1 = mkStmtOneInstr @@ Set (var d1, BinOp (MinusA, Lval x, Lval (var t), te), loc, eloc) in (* J: d1 = x - t*) + let dt2 = mkStmtOneInstr @@ Set (var d2, BinOp (MinusA, Lval x, Lval (var t), te), loc, eloc) in (* J: d2 = x - t*) + let nb = mkBlock [mkStmt s.skind; dt1; dt2] in (* J: add the incrementation steps at the end*) s.skind <- Block nb; s ) | _ -> s in - ChangeDoChildrenPost (s, action) + ChangeDoChildrenPost (s, action) (* J: continue with the children*) end @@ -215,24 +296,24 @@ struct (* ctx.local *) (* | _ -> ctx.local *) - let startstate v = D.bot () - let threadenter ctx lval f args = [D.bot ()] + let startstate v = D.bot () (* J: start with bottom*) + let threadenter ctx lval f args = [D.bot ()] (* J: enter threads with bottom*) let exitstate v = D.bot () end class recomputeVisitor (fd : fundec) = object(self) inherit nopCilVisitor method! vfunc fd = - computeCFGInfo fd true; - SkipChildren + computeCFGInfo fd true; (* J: make the cfg and return a list of statements with global statement number*) + SkipChildren (* J: don't visit children*) end let _ = (* Cilfacade.register_preprocess Spec.name (new loopCounterVisitor); *) - Cilfacade.register_preprocess (Spec.name ()) (new loopBreaksVisitor); - Cilfacade.register_preprocess (Spec.name ()) (new loopVarsVisitor); - Cilfacade.register_preprocess (Spec.name ()) (new loopInstrVisitor); - Cilfacade.register_preprocess (Spec.name ()) (new recomputeVisitor); - Hashtbl.clear loopBreaks; (* because the sids are now different *) - Cilfacade.register_preprocess (Spec.name ()) (new loopBreaksVisitor); - MCP.register_analysis (module Spec : MCPSpec) + Cilfacade.register_preprocess (Spec.name ()) (new loopBreaksVisitor); (* J: fill hash table loopBreaks: containing breaks ?*) + Cilfacade.register_preprocess (Spec.name ()) (new loopVarsVisitor); (* J: fill hash table loopVars: containing varibales identified with loops ?*) + Cilfacade.register_preprocess (Spec.name ()) (new loopInstrVisitor); (* J: inserts new variable with init, increment, and bounded check to code*) + Cilfacade.register_preprocess (Spec.name ()) (new recomputeVisitor); (* J: ??? *) + Hashtbl.clear loopBreaks; (* because the sids are now different *) (* J: delete entries in loopBreaks*) + Cilfacade.register_preprocess (Spec.name ()) (new loopBreaksVisitor); (* J: newly set hash table loopBreaks with goto statements*) + MCP.register_analysis (module Spec : MCPSpec) (* J: ???*) From 8facb0c9ca8c382194a88bcdb4c01f14b051efd9 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Thu, 11 May 2023 19:58:15 +0200 Subject: [PATCH 0016/1312] added preprocessing file for loop termination analysis: this file contains code adding before it is analyzed --- src/analyses/termination.ml | 4 +++- src/util/terminationPreprocessing.ml | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 src/util/terminationPreprocessing.ml diff --git a/src/analyses/termination.ml b/src/analyses/termination.ml index c5f2ca7f7d..8fa8a0ee12 100644 --- a/src/analyses/termination.ml +++ b/src/analyses/termination.ml @@ -240,6 +240,7 @@ class loopInstrVisitor (fd : fundec) = object(self) (match stripCastsDeep e with (* J: if x' + e2 or x' - e2 with x' = x and the type arithmetic: - adds incrementation for d1 and d2 to the code*) + (* J: if the loopVar is changed*) | BinOp (op, Lval x', e2, typ) when (op = PlusA || op = MinusA) && x' = x && isArithmeticType typ -> (* TODO x = 1 + x, MinusA! *) (* increase diff by same expr *) let d1_inc = mkStmtOneInstr @@ Set (var d1, BinOp (PlusA, Lval (var d1), e2, typ), loc, eloc) in (* J: d1 = d1 + e2*) @@ -259,6 +260,7 @@ class loopInstrVisitor (fd : fundec) = object(self) ) | _ -> s in + (* J: *) ChangeDoChildrenPost (s, action) (* J: continue with the children*) end @@ -316,4 +318,4 @@ let _ = Cilfacade.register_preprocess (Spec.name ()) (new recomputeVisitor); (* J: ??? *) Hashtbl.clear loopBreaks; (* because the sids are now different *) (* J: delete entries in loopBreaks*) Cilfacade.register_preprocess (Spec.name ()) (new loopBreaksVisitor); (* J: newly set hash table loopBreaks with goto statements*) - MCP.register_analysis (module Spec : MCPSpec) (* J: ???*) + MCP.register_analysis (module Spec : MCPSpec) (* J: ???*) \ No newline at end of file diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml new file mode 100644 index 0000000000..73982cb0f1 --- /dev/null +++ b/src/util/terminationPreprocessing.ml @@ -0,0 +1,3 @@ +(* - code in src/analysis/termination.ml contains loopCounterVisitor which might be interesting + - check if overflow happend with new variable + - how do we deal with nested loops?*) \ No newline at end of file From 7fbb12ae3c72b2115846b694832e22955996b492 Mon Sep 17 00:00:00 2001 From: Atul Agarwal Date: Sun, 14 May 2023 23:21:19 +0200 Subject: [PATCH 0017/1312] Update termination.ml Indentations fix test --- src/analyses/termination.ml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/analyses/termination.ml b/src/analyses/termination.ml index 8fa8a0ee12..0040055e03 100644 --- a/src/analyses/termination.ml +++ b/src/analyses/termination.ml @@ -238,9 +238,8 @@ class loopInstrVisitor (fd : fundec) = object(self) let d1 = makeVar fd cur_loop "d1" in let d2 = makeVar fd cur_loop "d2" in (match stripCastsDeep e with - (* J: if x' + e2 or x' - e2 with x' = x and the type arithmetic: - - adds incrementation for d1 and d2 to the code*) - (* J: if the loopVar is changed*) + (* J: if x' + e2 or x' - e2 with x' = x and the type arithmetic:- adds incrementation for d1 and d2 to the code*) + (* J: if the loopVar is changed*) | BinOp (op, Lval x', e2, typ) when (op = PlusA || op = MinusA) && x' = x && isArithmeticType typ -> (* TODO x = 1 + x, MinusA! *) (* increase diff by same expr *) let d1_inc = mkStmtOneInstr @@ Set (var d1, BinOp (PlusA, Lval (var d1), e2, typ), loc, eloc) in (* J: d1 = d1 + e2*) @@ -318,4 +317,4 @@ let _ = Cilfacade.register_preprocess (Spec.name ()) (new recomputeVisitor); (* J: ??? *) Hashtbl.clear loopBreaks; (* because the sids are now different *) (* J: delete entries in loopBreaks*) Cilfacade.register_preprocess (Spec.name ()) (new loopBreaksVisitor); (* J: newly set hash table loopBreaks with goto statements*) - MCP.register_analysis (module Spec : MCPSpec) (* J: ???*) \ No newline at end of file + MCP.register_analysis (module Spec : MCPSpec) (* J: ???*) From ff19371cf58c5d9d584ec2c8f78fcdb50611aecd Mon Sep 17 00:00:00 2001 From: Atul Agarwal Date: Sun, 14 May 2023 23:24:39 +0200 Subject: [PATCH 0018/1312] Update termination.ml indentation test check --- src/analyses/termination.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/analyses/termination.ml b/src/analyses/termination.ml index 0040055e03..eb6c263870 100644 --- a/src/analyses/termination.ml +++ b/src/analyses/termination.ml @@ -238,8 +238,8 @@ class loopInstrVisitor (fd : fundec) = object(self) let d1 = makeVar fd cur_loop "d1" in let d2 = makeVar fd cur_loop "d2" in (match stripCastsDeep e with - (* J: if x' + e2 or x' - e2 with x' = x and the type arithmetic:- adds incrementation for d1 and d2 to the code*) - (* J: if the loopVar is changed*) + (* J: if x' + e2 or x' - e2 with x' = x and the type arithmetic:- adds incrementation for d1 and d2 to the code*) + (* J: if the loopVar is changed*) | BinOp (op, Lval x', e2, typ) when (op = PlusA || op = MinusA) && x' = x && isArithmeticType typ -> (* TODO x = 1 + x, MinusA! *) (* increase diff by same expr *) let d1_inc = mkStmtOneInstr @@ Set (var d1, BinOp (PlusA, Lval (var d1), e2, typ), loc, eloc) in (* J: d1 = d1 + e2*) From cb3f49429d429b51a2fd27b1960103a895220ba5 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Wed, 17 May 2023 11:16:48 +0200 Subject: [PATCH 0019/1312] One comment in old termination analysis --- src/analyses/termination.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/analyses/termination.ml b/src/analyses/termination.ml index eb6c263870..f963d0d094 100644 --- a/src/analyses/termination.ml +++ b/src/analyses/termination.ml @@ -317,4 +317,5 @@ let _ = Cilfacade.register_preprocess (Spec.name ()) (new recomputeVisitor); (* J: ??? *) Hashtbl.clear loopBreaks; (* because the sids are now different *) (* J: delete entries in loopBreaks*) Cilfacade.register_preprocess (Spec.name ()) (new loopBreaksVisitor); (* J: newly set hash table loopBreaks with goto statements*) - MCP.register_analysis (module Spec : MCPSpec) (* J: ???*) + MCP.register_analysis (module Spec : MCPSpec) (* A: register this (termination) analysis withing the master control program, which + collects all active analyses and represents the combination of them as a new, single analysis to FromSpec *) From 20f0bbd5751b4a854361dd0c6f3724500d077eae Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Wed, 17 May 2023 11:25:31 +0200 Subject: [PATCH 0020/1312] Create new termination analysis file --- src/analyses/termination_new.ml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/analyses/termination_new.ml diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml new file mode 100644 index 0000000000..afc2ea3ece --- /dev/null +++ b/src/analyses/termination_new.ml @@ -0,0 +1,12 @@ +open Prelude.Ana +open Analyses + +module Spec : Analyses.MCPSpec = +struct + + let query ctx (type a) (q: a Queries.t): a Queries.result = () + +end + +let _ = + MCP.register_analysis (module Spec : MCPSpec) From c4e92dba282891d8bd4e92d624cb26bb72b86abd Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Wed, 17 May 2023 12:15:06 +0200 Subject: [PATCH 0021/1312] Better dummy query --- src/analyses/termination_new.ml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index afc2ea3ece..9d1319119f 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -1,12 +1,19 @@ +(** Work in progress *) + open Prelude.Ana open Analyses +let terminates loop = () (* TODO *) + module Spec : Analyses.MCPSpec = struct - let query ctx (type a) (q: a Queries.t): a Queries.result = () + let query ctx (type a) (q: a Queries.t): a Queries.result = + let open Queries in + Result.top q (* TODO *) end let _ = + (* Register this analysis within the master control program *) MCP.register_analysis (module Spec : MCPSpec) From 14f2bdd90c4f5fb1ad69913d87441feedb0ed4fe Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Wed, 17 May 2023 18:00:23 +0200 Subject: [PATCH 0022/1312] Add required functions and values --- src/analyses/termination_new.ml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index 9d1319119f..e6b202b69e 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -8,6 +8,17 @@ let terminates loop = () (* TODO *) module Spec : Analyses.MCPSpec = struct + let name () = "termination" + + module D = Lattice.Unit (* TODO *) + module C = D (* TODO *) + + let startstate _ = D.bot () (* TODO *) + let exitstate = startstate (* TODO *) + + (** Provides some default implementations *) + include Analyses.IdentitySpec + let query ctx (type a) (q: a Queries.t): a Queries.result = let open Queries in Result.top q (* TODO *) @@ -15,5 +26,5 @@ struct end let _ = - (* Register this analysis within the master control program *) + (** Register this analysis within the master control program *) MCP.register_analysis (module Spec : MCPSpec) From 6fc9a1efe42f38f5ef2c2cef20c99c8523068726 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Wed, 17 May 2023 18:19:30 +0200 Subject: [PATCH 0023/1312] Do not open the prelude --- src/analyses/termination_new.ml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index e6b202b69e..c81ff72d71 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -1,6 +1,5 @@ (** Work in progress *) -open Prelude.Ana open Analyses let terminates loop = () (* TODO *) From ec89bc2be9b31350b4f7cd135ab8cb128bc3f8a9 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Thu, 18 May 2023 15:38:59 +0200 Subject: [PATCH 0024/1312] added code adding to terminationPreprocessing file in util; created sh script to execute the code easily and store the cil output in the output.txt file; NOTE: the signs.ml file does currently contain the termination analysis; signsOrig.ml contains the original signs analysis --- output.txt | 2167 ++++++++++++++++++++++++++ runningGob.sh | 3 + src/analyses/tutorials/signs.ml | 22 + src/analyses/tutorials/signsOrig.ml | 92 ++ src/util/terminationPreprocessing.ml | 42 +- 5 files changed, 2325 insertions(+), 1 deletion(-) create mode 100644 output.txt create mode 100755 runningGob.sh create mode 100644 src/analyses/tutorials/signsOrig.ml diff --git a/output.txt b/output.txt new file mode 100644 index 0000000000..d3b021a1f3 --- /dev/null +++ b/output.txt @@ -0,0 +1,2167 @@ +/* Generated by CIL v. 2.0.1-48-g4df989f */ +/* print_CIL_Input is true */ + +#line 31 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef unsigned char __u_char; +#line 32 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef unsigned short __u_short; +#line 33 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef unsigned int __u_int; +#line 34 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef unsigned long __u_long; +#line 37 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef signed char __int8_t; +#line 38 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef unsigned char __uint8_t; +#line 39 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef short __int16_t; +#line 40 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef unsigned short __uint16_t; +#line 41 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef int __int32_t; +#line 42 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef unsigned int __uint32_t; +#line 44 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef long __int64_t; +#line 45 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef unsigned long __uint64_t; +#line 52 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef __int8_t __int_least8_t; +#line 53 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef __uint8_t __uint_least8_t; +#line 54 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef __int16_t __int_least16_t; +#line 55 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef __uint16_t __uint_least16_t; +#line 56 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef __int32_t __int_least32_t; +#line 57 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef __uint32_t __uint_least32_t; +#line 58 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef __int64_t __int_least64_t; +#line 59 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef __uint64_t __uint_least64_t; +#line 63 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef long __quad_t; +#line 64 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef unsigned long __u_quad_t; +#line 72 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef long __intmax_t; +#line 73 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef unsigned long __uintmax_t; +#line 145 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef unsigned long __dev_t; +#line 146 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef unsigned int __uid_t; +#line 147 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef unsigned int __gid_t; +#line 148 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef unsigned long __ino_t; +#line 149 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef unsigned long __ino64_t; +#line 150 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef unsigned int __mode_t; +#line 151 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef unsigned long __nlink_t; +#line 152 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef long __off_t; +#line 153 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef long __off64_t; +#line 154 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef int __pid_t; +#line 155 "/usr/include/x86_64-linux-gnu/bits/types.h" +struct __anonstruct___fsid_t_109580352 { + int __val[2] ; +}; +#line 155 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef struct __anonstruct___fsid_t_109580352 __fsid_t; +#line 156 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef long __clock_t; +#line 157 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef unsigned long __rlim_t; +#line 158 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef unsigned long __rlim64_t; +#line 159 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef unsigned int __id_t; +#line 160 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef long __time_t; +#line 161 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef unsigned int __useconds_t; +#line 162 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef long __suseconds_t; +#line 163 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef long __suseconds64_t; +#line 165 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef int __daddr_t; +#line 166 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef int __key_t; +#line 169 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef int __clockid_t; +#line 172 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef void *__timer_t; +#line 175 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef long __blksize_t; +#line 180 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef long __blkcnt_t; +#line 181 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef long __blkcnt64_t; +#line 184 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef unsigned long __fsblkcnt_t; +#line 185 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef unsigned long __fsblkcnt64_t; +#line 188 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef unsigned long __fsfilcnt_t; +#line 189 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef unsigned long __fsfilcnt64_t; +#line 192 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef long __fsword_t; +#line 194 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef long __ssize_t; +#line 197 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef long __syscall_slong_t; +#line 199 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef unsigned long __syscall_ulong_t; +#line 203 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef __off64_t __loff_t; +#line 204 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef char *__caddr_t; +#line 207 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef long __intptr_t; +#line 210 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef unsigned int __socklen_t; +#line 215 "/usr/include/x86_64-linux-gnu/bits/types.h" +typedef int __sig_atomic_t; +#line 209 "/usr/lib/gcc/x86_64-linux-gnu/11/include/stddef.h" +typedef unsigned long size_t; +#line 10 "/usr/include/x86_64-linux-gnu/bits/types/time_t.h" +typedef __time_t time_t; +#line 11 "/usr/include/x86_64-linux-gnu/bits/types/struct_timespec.h" +struct timespec { + __time_t tv_sec ; + __syscall_slong_t tv_nsec ; +}; +#line 38 "/usr/include/sched.h" +typedef __pid_t pid_t; +#line 23 "/usr/include/x86_64-linux-gnu/bits/types/struct_sched_param.h" +struct sched_param { + int sched_priority ; +}; +#line 32 "/usr/include/x86_64-linux-gnu/bits/cpu-set.h" +typedef unsigned long __cpu_mask; +#line 39 "/usr/include/x86_64-linux-gnu/bits/cpu-set.h" +struct __anonstruct_cpu_set_t_826868708 { + __cpu_mask __bits[1024UL / (8UL * sizeof(__cpu_mask ))] ; +}; +#line 39 "/usr/include/x86_64-linux-gnu/bits/cpu-set.h" +typedef struct __anonstruct_cpu_set_t_826868708 cpu_set_t; +#line 7 "/usr/include/x86_64-linux-gnu/bits/types/clock_t.h" +typedef __clock_t clock_t; +#line 7 "/usr/include/x86_64-linux-gnu/bits/types/struct_tm.h" +struct tm { + int tm_sec ; + int tm_min ; + int tm_hour ; + int tm_mday ; + int tm_mon ; + int tm_year ; + int tm_wday ; + int tm_yday ; + int tm_isdst ; + long tm_gmtoff ; + char const *tm_zone ; +}; +#line 7 "/usr/include/x86_64-linux-gnu/bits/types/clockid_t.h" +typedef __clockid_t clockid_t; +#line 7 "/usr/include/x86_64-linux-gnu/bits/types/timer_t.h" +typedef __timer_t timer_t; +#line 8 "/usr/include/x86_64-linux-gnu/bits/types/struct_itimerspec.h" +struct itimerspec { + struct timespec it_interval ; + struct timespec it_value ; +}; +#line 49 "/usr/include/time.h" +struct sigevent ; +#line 27 "/usr/include/x86_64-linux-gnu/bits/types/__locale_t.h" +struct __locale_data ; +#line 27 "/usr/include/x86_64-linux-gnu/bits/types/__locale_t.h" +struct __locale_struct { + struct __locale_data *__locales[13] ; + unsigned short const *__ctype_b ; + int const *__ctype_tolower ; + int const *__ctype_toupper ; + char const *__names[13] ; +}; +#line 41 "/usr/include/x86_64-linux-gnu/bits/types/__locale_t.h" +typedef struct __locale_struct *__locale_t; +#line 24 "/usr/include/x86_64-linux-gnu/bits/types/locale_t.h" +typedef __locale_t locale_t; +#line 25 "/usr/include/x86_64-linux-gnu/bits/atomic_wide_counter.h" +struct __anonstruct___value32_817613185 { + unsigned int __low ; + unsigned int __high ; +}; +#line 25 "/usr/include/x86_64-linux-gnu/bits/atomic_wide_counter.h" +union __anonunion___atomic_wide_counter_643133811 { + unsigned long long __value64 ; + struct __anonstruct___value32_817613185 __value32 ; +}; +#line 25 "/usr/include/x86_64-linux-gnu/bits/atomic_wide_counter.h" +typedef union __anonunion___atomic_wide_counter_643133811 __atomic_wide_counter; +#line 51 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" +struct __pthread_internal_list { + struct __pthread_internal_list *__prev ; + struct __pthread_internal_list *__next ; +}; +#line 51 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" +typedef struct __pthread_internal_list __pthread_list_t; +#line 57 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" +struct __pthread_internal_slist { + struct __pthread_internal_slist *__next ; +}; +#line 57 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" +typedef struct __pthread_internal_slist __pthread_slist_t; +#line 22 "/usr/include/x86_64-linux-gnu/bits/struct_mutex.h" +struct __pthread_mutex_s { + int __lock ; + unsigned int __count ; + int __owner ; + unsigned int __nusers ; + int __kind ; + short __spins ; + short __elision ; + __pthread_list_t __list ; +}; +#line 23 "/usr/include/x86_64-linux-gnu/bits/struct_rwlock.h" +struct __pthread_rwlock_arch_t { + unsigned int __readers ; + unsigned int __writers ; + unsigned int __wrphase_futex ; + unsigned int __writers_futex ; + unsigned int __pad3 ; + unsigned int __pad4 ; + int __cur_writer ; + int __shared ; + signed char __rwelision ; + unsigned char __pad1[7] ; + unsigned long __pad2 ; + unsigned int __flags ; +}; +#line 94 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" +struct __pthread_cond_s { + __atomic_wide_counter __wseq ; + __atomic_wide_counter __g1_start ; + unsigned int __g_refs[2] ; + unsigned int __g_size[2] ; + unsigned int __g1_orig_size ; + unsigned int __wrefs ; + unsigned int __g_signals[2] ; +}; +#line 105 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" +typedef unsigned int __tss_t; +#line 106 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" +typedef unsigned long __thrd_t; +#line 108 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" +struct __anonstruct___once_flag_826868709 { + int __data ; +}; +#line 108 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" +typedef struct __anonstruct___once_flag_826868709 __once_flag; +#line 27 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" +typedef unsigned long pthread_t; +#line 32 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" +union __anonunion_pthread_mutexattr_t_488594144 { + char __size[4] ; + int __align ; +}; +#line 32 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" +typedef union __anonunion_pthread_mutexattr_t_488594144 pthread_mutexattr_t; +#line 41 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" +union __anonunion_pthread_condattr_t_488594145 { + char __size[4] ; + int __align ; +}; +#line 41 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" +typedef union __anonunion_pthread_condattr_t_488594145 pthread_condattr_t; +#line 49 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" +typedef unsigned int pthread_key_t; +#line 53 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" +typedef int pthread_once_t; +#line 56 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" +union pthread_attr_t { + char __size[56] ; + long __align ; +}; +#line 62 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" +typedef union pthread_attr_t pthread_attr_t; +#line 67 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" +union __anonunion_pthread_mutex_t_335460617 { + struct __pthread_mutex_s __data ; + char __size[40] ; + long __align ; +}; +#line 67 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" +typedef union __anonunion_pthread_mutex_t_335460617 pthread_mutex_t; +#line 75 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" +union __anonunion_pthread_cond_t_951761805 { + struct __pthread_cond_s __data ; + char __size[48] ; + long long __align ; +}; +#line 75 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" +typedef union __anonunion_pthread_cond_t_951761805 pthread_cond_t; +#line 86 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" +union __anonunion_pthread_rwlock_t_656928968 { + struct __pthread_rwlock_arch_t __data ; + char __size[56] ; + long __align ; +}; +#line 86 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" +typedef union __anonunion_pthread_rwlock_t_656928968 pthread_rwlock_t; +#line 93 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" +union __anonunion_pthread_rwlockattr_t_145707745 { + char __size[8] ; + long __align ; +}; +#line 93 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" +typedef union __anonunion_pthread_rwlockattr_t_145707745 pthread_rwlockattr_t; +#line 103 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" +typedef int volatile pthread_spinlock_t; +#line 108 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" +union __anonunion_pthread_barrier_t_145707746 { + char __size[32] ; + long __align ; +}; +#line 108 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" +typedef union __anonunion_pthread_barrier_t_145707746 pthread_barrier_t; +#line 114 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" +union __anonunion_pthread_barrierattr_t_951761806 { + char __size[4] ; + int __align ; +}; +#line 114 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" +typedef union __anonunion_pthread_barrierattr_t_951761806 pthread_barrierattr_t; +#line 31 "/usr/include/x86_64-linux-gnu/bits/setjmp.h" +typedef long __jmp_buf[8]; +#line 5 "/usr/include/x86_64-linux-gnu/bits/types/__sigset_t.h" +struct __anonstruct___sigset_t_764561023 { + unsigned long __val[1024UL / (8UL * sizeof(unsigned long ))] ; +}; +#line 5 "/usr/include/x86_64-linux-gnu/bits/types/__sigset_t.h" +typedef struct __anonstruct___sigset_t_764561023 __sigset_t; +#line 26 "/usr/include/x86_64-linux-gnu/bits/types/struct___jmp_buf_tag.h" +struct __jmp_buf_tag { + __jmp_buf __jmpbuf ; + int __mask_was_saved ; + __sigset_t __saved_mask ; +}; +#line 37 "/usr/include/pthread.h" +enum __anonenum_34415463 { + PTHREAD_CREATE_JOINABLE = 0, + PTHREAD_CREATE_DETACHED = 1 +} ; +#line 47 +enum __anonenum_508643754 { + PTHREAD_MUTEX_TIMED_NP = 0, + PTHREAD_MUTEX_RECURSIVE_NP = 1, + PTHREAD_MUTEX_ERRORCHECK_NP = 2, + PTHREAD_MUTEX_ADAPTIVE_NP = 3, + PTHREAD_MUTEX_NORMAL = 0, + PTHREAD_MUTEX_RECURSIVE = 1, + PTHREAD_MUTEX_ERRORCHECK = 2, + PTHREAD_MUTEX_DEFAULT = 0 +} ; +#line 69 +enum __anonenum_931900394 { + PTHREAD_MUTEX_STALLED = 0, + PTHREAD_MUTEX_STALLED_NP = 0, + PTHREAD_MUTEX_ROBUST = 1, + PTHREAD_MUTEX_ROBUST_NP = 1 +} ; +#line 81 +enum __anonenum_205214487 { + PTHREAD_PRIO_NONE = 0, + PTHREAD_PRIO_INHERIT = 1, + PTHREAD_PRIO_PROTECT = 2 +} ; +#line 104 +enum __anonenum_25043950 { + PTHREAD_RWLOCK_PREFER_READER_NP = 0, + PTHREAD_RWLOCK_PREFER_WRITER_NP = 1, + PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP = 2, + PTHREAD_RWLOCK_DEFAULT_NP = 0 +} ; +#line 124 +enum __anonenum_436439511 { + PTHREAD_INHERIT_SCHED = 0, + PTHREAD_EXPLICIT_SCHED = 1 +} ; +#line 134 +enum __anonenum_998661166 { + PTHREAD_SCOPE_SYSTEM = 0, + PTHREAD_SCOPE_PROCESS = 1 +} ; +#line 144 +enum __anonenum_146137331 { + PTHREAD_PROCESS_PRIVATE = 0, + PTHREAD_PROCESS_SHARED = 1 +} ; +#line 159 "/usr/include/pthread.h" +struct _pthread_cleanup_buffer { + void (*__routine)(void * ) ; + void *__arg ; + int __canceltype ; + struct _pthread_cleanup_buffer *__prev ; +}; +#line 168 +enum __anonenum_53396917 { + PTHREAD_CANCEL_ENABLE = 0, + PTHREAD_CANCEL_DISABLE = 1 +} ; +#line 175 +enum __anonenum_904563783 { + PTHREAD_CANCEL_DEFERRED = 0, + PTHREAD_CANCEL_ASYNCHRONOUS = 1 +} ; +#line 538 "/usr/include/pthread.h" +struct __cancel_jmp_buf_tag { + __jmp_buf __cancel_jmp_buf ; + int __mask_was_saved ; +}; +#line 544 "/usr/include/pthread.h" +struct __anonstruct___pthread_unwind_buf_t_530692248 { + struct __cancel_jmp_buf_tag __cancel_jmp_buf[1] ; + void *__pad[4] ; +}; +#line 544 "/usr/include/pthread.h" +typedef struct __anonstruct___pthread_unwind_buf_t_530692248 __attribute__((__aligned__)) __pthread_unwind_buf_t; +#line 557 "/usr/include/pthread.h" +struct __pthread_cleanup_frame { + void (*__cancel_routine)(void * ) ; + void *__cancel_arg ; + int __do_it ; + int __cancel_type ; +}; +#line 143 "/usr/lib/gcc/x86_64-linux-gnu/11/include/stddef.h" +typedef long ptrdiff_t; +#line 321 "/usr/lib/gcc/x86_64-linux-gnu/11/include/stddef.h" +typedef int wchar_t; +#line 415 "/usr/lib/gcc/x86_64-linux-gnu/11/include/stddef.h" +struct __anonstruct_max_align_t_896270833 { + long long __max_align_ll __attribute__((__aligned__(__alignof__(long long )))) ; + long double __max_align_ld __attribute__((__aligned__(__alignof__(long double )))) ; +}; +#line 415 "/usr/lib/gcc/x86_64-linux-gnu/11/include/stddef.h" +typedef struct __anonstruct_max_align_t_896270833 max_align_t; +/* compiler builtin: + void __builtin_va_copy(__builtin_va_list , __builtin_va_list ) ; */ +/* compiler builtin: + void *__builtin_frob_return_address(void * ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __sync_and_and_fetch(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __sync_fetch_and_or(...) ; */ +/* compiler builtin: + int __builtin_popcountll(unsigned long long ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_or_fetch(...) ; */ +/* compiler builtin: + float __builtin_atanf(float ) ; */ +/* compiler builtin: + float __attribute__((____vector_size____(16))) __builtin_ia32_addps(float __attribute__((____vector_size____(16))) , + float __attribute__((____vector_size____(16))) ) ; */ +/* compiler builtin: + unsigned long __builtin_strcspn(char const * , char const * ) ; */ +/* compiler builtin: + float __builtin_asinf(float ) ; */ +/* compiler builtin: + float __attribute__((____vector_size____(16))) __builtin_ia32_maxps(float __attribute__((____vector_size____(16))) , + float __attribute__((____vector_size____(16))) ) ; */ +/* compiler builtin: + float __attribute__((____vector_size____(16))) __builtin_ia32_unpckhps(float __attribute__((____vector_size____(16))) , + float __attribute__((____vector_size____(16))) ) ; */ +/* compiler builtin: + double __builtin_acos(double ) ; */ +/* compiler builtin: + int __builtin___sprintf_chk(char * , int , unsigned long , char const * , ...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_nand_fetch_8(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_fetch_and(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_xor_fetch_16(...) ; */ +/* compiler builtin: + double __builtin_cosh(double ) ; */ +/* compiler builtin: + float __builtin_tanhf(float ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_fetch_nand_16(...) ; */ +/* compiler builtin: + void *__builtin_mempcpy(void * , void const * , unsigned long ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_sub_fetch_1(...) ; */ +/* compiler builtin: + long double __builtin_sqrtl(long double ) ; */ +/* compiler builtin: + int __builtin_parity(unsigned int ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_fetch_or(...) ; */ +/* compiler builtin: + long double __builtin_coshl(long double ) ; */ +/* compiler builtin: + long double __builtin_cosl(long double ) ; */ +/* compiler builtin: + float __builtin_cosf(float ) ; */ +/* compiler builtin: + void __sync_synchronize(...) ; */ +/* compiler builtin: + long double __builtin_acosl(long double ) ; */ +/* compiler builtin: + void *__builtin___mempcpy_chk(void * , void const * , unsigned long , unsigned long ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __sync_or_and_fetch(...) ; */ +/* compiler builtin: + int __builtin_clz(unsigned int ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_nand_fetch_4(...) ; */ +/* compiler builtin: + double __builtin_log10(double ) ; */ +/* compiler builtin: + char *__builtin___strcat_chk(char * , char const * , unsigned long ) ; */ +/* compiler builtin: + float __builtin_modff(float , float * ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_sub_fetch_4(...) ; */ +/* compiler builtin: + _Bool __atomic_compare_exchange_n(...) ; */ +/* compiler builtin: + double __builtin_sin(double ) ; */ +/* compiler builtin: + double __builtin_frexp(double , int * ) ; */ +/* compiler builtin: + float __builtin_acosf(float ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __sync_add_and_fetch(...) ; */ +/* compiler builtin: + long double __builtin_sinhl(long double ) ; */ +/* compiler builtin: + char *__builtin___stpcpy_chk(char * , char const * , unsigned long ) ; */ +/* compiler builtin: + void __atomic_signal_fence(int ) ; */ +/* compiler builtin: + double __builtin_fabs(double ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_fetch_nand_1(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_and_fetch_16(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __sync_fetch_and_nand(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_xor_fetch_8(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_xor_fetch_2(...) ; */ +/* compiler builtin: + void __atomic_thread_fence(int ) ; */ +/* compiler builtin: + void __atomic_store_16(...) ; */ +/* compiler builtin: + void __builtin_va_start(__builtin_va_list ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_fetch_and_8(...) ; */ +/* compiler builtin: + short __builtin_bswap16(short ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_and_fetch_2(...) ; */ +/* compiler builtin: + _Bool __atomic_test_and_set(void * , int ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_fetch_add_8(...) ; */ +/* compiler builtin: + int __builtin_ctz(unsigned int ) ; */ +/* compiler builtin: + char *__builtin_strpbrk(char const * , char const * ) ; */ +/* compiler builtin: + char *__builtin_strcpy(char * , char const * ) ; */ +/* compiler builtin: + double __builtin_sqrt(double ) ; */ +/* compiler builtin: + __builtin_va_list __builtin_next_arg(void) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_fetch_nand(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_exchange_16(...) ; */ +/* compiler builtin: + void __atomic_clear(_Bool * , int ) ; */ +/* compiler builtin: + void __atomic_store(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_add_fetch_2(...) ; */ +/* compiler builtin: + float __builtin_log10f(float ) ; */ +/* compiler builtin: + long double __builtin_fabsl(long double ) ; */ +/* compiler builtin: + long double __builtin_floorl(long double ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_fetch_xor_1(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_and_fetch(...) ; */ +/* compiler builtin: + float __builtin_floorf(float ) ; */ +/* compiler builtin: + _Bool __atomic_compare_exchange_4(...) ; */ +/* compiler builtin: + void *__builtin_memcpy(void * , void const * , unsigned long ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __sync_sub_and_fetch(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __sync_nand_and_fetch(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_load_16(...) ; */ +/* compiler builtin: + float __attribute__((____vector_size____(16))) __builtin_ia32_subps(float __attribute__((____vector_size____(16))) , + float __attribute__((____vector_size____(16))) ) ; */ +/* compiler builtin: + int __builtin_parityll(unsigned long long ) ; */ +/* compiler builtin: + void __builtin_va_end(__builtin_va_list ) ; */ +/* compiler builtin: + void __builtin_bzero(void * , unsigned long ) ; */ +/* compiler builtin: + _Bool __atomic_always_lock_free(unsigned long , void * ) ; */ +/* compiler builtin: + int __builtin_strncmp(char const * , char const * , unsigned long ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_nand_fetch_16(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __sync_xor_and_fetch(...) ; */ +/* compiler builtin: + int __builtin___vsprintf_chk(char * , int , unsigned long , char const * , + __builtin_va_list ) ; */ +/* compiler builtin: + float __builtin_sqrtf(float ) ; */ +/* compiler builtin: + double __builtin_nans(char const * ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_fetch_xor_8(...) ; */ +/* compiler builtin: + double __builtin_exp(double ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_exchange_8(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_exchange_1(...) ; */ +/* compiler builtin: + int __builtin_strcmp(char const * , char const * ) ; */ +/* compiler builtin: + float __builtin_ldexpf(float , int ) ; */ +/* compiler builtin: + float __builtin_powif(float , int ) ; */ +/* compiler builtin: + long double __builtin_log10l(long double ) ; */ +/* compiler builtin: + void *__builtin___memmove_chk(void * , void const * , unsigned long , unsigned long ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __sync_fetch_and_and(...) ; */ +/* compiler builtin: + void *__builtin_return_address(unsigned int ) ; */ +/* compiler builtin: + void __atomic_feraiseexcept(int ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_and_fetch_4(...) ; */ +/* compiler builtin: + float __builtin_fabsf(float ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_or_fetch_8(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_or_fetch_4(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_or_fetch_1(...) ; */ +/* compiler builtin: + unsigned long __builtin_object_size(void * , int ) ; */ +/* compiler builtin: + void *__builtin_alloca(unsigned long ) ; */ +/* compiler builtin: + int __builtin_va_arg_pack_len(void) ; */ +/* compiler builtin: + long double __builtin_tanl(long double ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_fetch_and_2(...) ; */ +/* compiler builtin: + void __sync_lock_release(...) ; */ +/* compiler builtin: + long double __builtin_modfl(long double , long double * ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_fetch_nand_8(...) ; */ +/* compiler builtin: + char *__builtin_stpcpy(char * , char const * ) ; */ +/* compiler builtin: + long double __builtin_sinl(long double ) ; */ +/* compiler builtin: + double __builtin_asin(double ) ; */ +/* compiler builtin: + float __builtin_sinhf(float ) ; */ +/* compiler builtin: + int __builtin_ctzl(unsigned long ) ; */ +/* compiler builtin: + long double __builtin_tanhl(long double ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_fetch_add(...) ; */ +/* compiler builtin: + long __builtin_bswap64(long ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_nand_fetch_1(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_fetch_nand_2(...) ; */ +/* compiler builtin: + float __attribute__((____vector_size____(16))) __builtin_ia32_mulps(float __attribute__((____vector_size____(16))) , + float __attribute__((____vector_size____(16))) ) ; */ +/* compiler builtin: + double __builtin_tan(double ) ; */ +/* compiler builtin: + char *__builtin_strncpy(char * , char const * , unsigned long ) ; */ +/* compiler builtin: + float __builtin_inff(void) ; */ +/* compiler builtin: + void *__builtin___memset_chk(void * , int , unsigned long , unsigned long ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_load_n(...) ; */ +/* compiler builtin: + double __builtin_huge_val(void) ; */ +/* compiler builtin: + int __builtin_clzl(unsigned long ) ; */ +/* compiler builtin: + _Bool __atomic_compare_exchange_16(...) ; */ +/* compiler builtin: + float __builtin_frexpf(float , int * ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_exchange_n(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_fetch_or_1(...) ; */ +/* compiler builtin: + long double __builtin_fmodl(long double ) ; */ +/* compiler builtin: + double __builtin_atan(double ) ; */ +/* compiler builtin: + int __builtin___fprintf_chk(void * , int , char const * , ...) ; */ +/* compiler builtin: + float __builtin_ceilf(float ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_fetch_or_8(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_fetch_add_1(...) ; */ +/* compiler builtin: + void __builtin_return(void const * ) ; */ +/* compiler builtin: + long double __builtin_asinl(long double ) ; */ +/* compiler builtin: + int __builtin_ffsll(unsigned long long ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_fetch_sub_1(...) ; */ +/* compiler builtin: + int __builtin_va_arg_pack(void) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_fetch_or_4(...) ; */ +/* compiler builtin: + char *__builtin___strncpy_chk(char * , char const * , unsigned long , unsigned long ) ; */ +/* compiler builtin: + double __builtin_powi(double , int ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_load_2(...) ; */ +/* compiler builtin: + char *__builtin_strchr(char * , int ) ; */ +/* compiler builtin: + char *__builtin___strncat_chk(char * , char const * , unsigned long , unsigned long ) ; */ +/* compiler builtin: + void __atomic_store_2(...) ; */ +/* compiler builtin: + long double __builtin_huge_vall(void) ; */ +/* compiler builtin: + int __builtin_ffsl(unsigned long ) ; */ +/* compiler builtin: + int __builtin___vprintf_chk(int , char const * , __builtin_va_list ) ; */ +/* compiler builtin: + float __attribute__((____vector_size____(16))) __builtin_ia32_unpcklps(float __attribute__((____vector_size____(16))) , + float __attribute__((____vector_size____(16))) ) ; */ +/* compiler builtin: + char *__builtin_strncat(char * , char const * , unsigned long ) ; */ +/* compiler builtin: + int __builtin_ctzll(unsigned long long ) ; */ +/* compiler builtin: + void __builtin_stdarg_start(__builtin_va_list ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_exchange_4(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __sync_fetch_and_xor(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_fetch_sub_4(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_fetch_and_4(...) ; */ +/* compiler builtin: + long double __builtin_frexpl(long double , int * ) ; */ +/* compiler builtin: + _Bool __atomic_compare_exchange(...) ; */ +/* compiler builtin: + float __builtin_tanf(float ) ; */ +/* compiler builtin: + long double __builtin_logl(long double ) ; */ +/* compiler builtin: + void __builtin_va_arg(__builtin_va_list , unsigned long , void * ) ; */ +/* compiler builtin: + long __builtin_expect(long , long ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_load_1(...) ; */ +/* compiler builtin: + _Bool __atomic_compare_exchange_1(...) ; */ +/* compiler builtin: + int __builtin___printf_chk(int , char const * , ...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_fetch_xor_2(...) ; */ +/* compiler builtin: + int __builtin___vfprintf_chk(void * , int , char const * , __builtin_va_list ) ; */ +/* compiler builtin: + void __builtin_prefetch(void const * , ...) ; */ +/* compiler builtin: + long double __builtin_nansl(char const * ) ; */ +/* compiler builtin: + double __builtin_fmod(double ) ; */ +/* compiler builtin: + void __atomic_load(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_or_fetch_16(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_add_fetch_16(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __sync_val_compare_and_swap(...) ; */ +/* compiler builtin: + void __atomic_store_4(...) ; */ +/* compiler builtin: + double __builtin_tanh(double ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_fetch_and_1(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_fetch_nand_4(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_fetch_add_16(...) ; */ +/* compiler builtin: + void __builtin_unreachable(void) ; */ +/* compiler builtin: + _Bool __atomic_compare_exchange_2(...) ; */ +/* compiler builtin: + long double __builtin_ldexpl(long double , int ) ; */ +/* compiler builtin: + void *__builtin_apply(void (*)() , void * , unsigned long ) ; */ +/* compiler builtin: + float __builtin_sinf(float ) ; */ +/* compiler builtin: + double __builtin_ceil(double ) ; */ +/* compiler builtin: + void __atomic_exchange(...) ; */ +/* compiler builtin: + long double __builtin_powil(long double , int ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_sub_fetch_8(...) ; */ +/* compiler builtin: + long double __builtin_expl(long double ) ; */ +/* compiler builtin: + int __builtin_constant_p(int ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_fetch_sub_16(...) ; */ +/* compiler builtin: + double __builtin_log(double ) ; */ +/* compiler builtin: + float __builtin_expf(float ) ; */ +/* compiler builtin: + int __builtin_types_compatible_p(unsigned long , unsigned long ) ; */ +/* compiler builtin: + long double __builtin_atan2l(long double , long double ) ; */ +/* compiler builtin: + void *__builtin_apply_args(void) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_exchange_2(...) ; */ +/* compiler builtin: + float __builtin_logf(float ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_sub_fetch_2(...) ; */ +/* compiler builtin: + unsigned long __builtin_strlen(char const * ) ; */ +/* compiler builtin: + int __builtin_ffs(unsigned int ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_fetch_xor_16(...) ; */ +/* compiler builtin: + double __builtin_inf(void) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_fetch_or_16(...) ; */ +/* compiler builtin: + void *__builtin___memcpy_chk(void * , void const * , unsigned long , unsigned long ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_load_4(...) ; */ +/* compiler builtin: + void __atomic_store_n(...) ; */ +/* compiler builtin: + void __builtin_trap(void) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_fetch_add_4(...) ; */ +/* compiler builtin: + int __builtin_parityl(unsigned long ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_nand_fetch_2(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __sync_lock_test_and_set(...) ; */ +/* compiler builtin: + unsigned long __builtin_strspn(char const * , char const * ) ; */ +/* compiler builtin: + void __builtin_varargs_start(__builtin_va_list ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_sub_fetch(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_fetch_and_16(...) ; */ +/* compiler builtin: + _Bool __atomic_compare_exchange_8(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_nand_fetch(...) ; */ +/* compiler builtin: + double __builtin_nan(char const * ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_load_8(...) ; */ +/* compiler builtin: + int __builtin___snprintf_chk(char * , unsigned long , int , unsigned long , + char const * , ...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_xor_fetch_4(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_fetch_sub_2(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_add_fetch(...) ; */ +/* compiler builtin: + long double __builtin_atanl(long double ) ; */ +/* compiler builtin: + int __builtin_clzll(unsigned long long ) ; */ +/* compiler builtin: + float __builtin_huge_valf(void) ; */ +/* compiler builtin: + float __builtin_coshf(float ) ; */ +/* compiler builtin: + float __builtin_nansf(char const * ) ; */ +/* compiler builtin: + void __atomic_store_8(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_xor_fetch(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __sync_fetch_and_add(...) ; */ +/* compiler builtin: + int __builtin___vsnprintf_chk(char * , unsigned long , int , unsigned long , + char const * , __builtin_va_list ) ; */ +/* compiler builtin: + float __builtin_nanf(char const * ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_or_fetch_2(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_add_fetch_1(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_fetch_xor(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_add_fetch_8(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_fetch_xor_4(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_fetch_sub_8(...) ; */ +/* compiler builtin: + _Bool __sync_bool_compare_and_swap(...) ; */ +/* compiler builtin: + double __builtin_atan2(double , double ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __builtin_tgmath(...) ; */ +/* compiler builtin: + int __builtin_popcountl(unsigned long ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_and_fetch_1(...) ; */ +/* compiler builtin: + long double __builtin_ceill(long double ) ; */ +/* compiler builtin: + void __atomic_store_1(...) ; */ +/* compiler builtin: + char *__builtin___strcpy_chk(char * , char const * , unsigned long ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_xor_fetch_1(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_fetch_or_2(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_sub_fetch_16(...) ; */ +/* compiler builtin: + double __builtin_floor(double ) ; */ +/* compiler builtin: + double __builtin_cos(double ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __sync_fetch_and_sub(...) ; */ +/* compiler builtin: + void *__builtin_memset(void * , int , int ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_fetch_add_2(...) ; */ +/* compiler builtin: + long double __builtin_nanl(char const * ) ; */ +/* compiler builtin: + float __builtin_atan2f(float , float ) ; */ +/* compiler builtin: + _Bool __atomic_is_lock_free(unsigned long , void * ) ; */ +/* compiler builtin: + int __builtin_popcount(unsigned int ) ; */ +/* compiler builtin: + double __builtin_sinh(double ) ; */ +/* compiler builtin: + void __builtin_bcopy(void const * , void * , unsigned long ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_and_fetch_8(...) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_fetch_sub(...) ; */ +/* compiler builtin: + void *__builtin_extract_return_addr(void * ) ; */ +/* compiler builtin: + int __builtin_bswap32(int ) ; */ +/* compiler builtin: + double __builtin_ldexp(double , int ) ; */ +/* compiler builtin: + long double __builtin_infl(void) ; */ +/* compiler builtin: + float __builtin_fmodf(float ) ; */ +/* compiler builtin: + void __attribute__((__overloaded__)) __atomic_add_fetch_4(...) ; */ +/* compiler builtin: + void *__builtin_frame_address(unsigned int ) ; */ +#line 1 "lib/goblint/runtime/include/goblint.h" +extern void __goblint_check(int exp ) ; +#line 2 +extern void __goblint_assume(int exp ) ; +#line 3 +extern void __goblint_assert(int exp ) ; +#line 5 +extern void __goblint_assume_join() ; +#line 7 +extern void __goblint_split_begin(int exp ) ; +#line 8 +extern void __goblint_split_end(int exp ) ; +#line 4 "tests/regression/55-loop-unrolling/01-simple-cases.c" +int global ; +#line 8 +void example1(void) ; +#line 9 +void example2(void) ; +#line 10 +void example3(void) ; +#line 11 +void example4(void) ; +#line 12 +void example5(void) ; +#line 13 +void example6(void) ; +#line 14 +void example7(void) ; +#line 15 +void example8(void) ; +#line 16 +void example9(void) ; +#line 17 +void example10(void) ; +#line 6 "tests/regression/55-loop-unrolling/01-simple-cases.c" +int main(void) +{ + + + { +#line 8 + example1(); +#line 9 + example2(); +#line 10 + example3(); +#line 11 + example4(); +#line 12 + example5(); +#line 13 + example6(); +#line 14 + example7(); +#line 15 + example8(); +#line 16 + example9(); +#line 17 + example10(); +#line 18 + return (0); +} +} +#line 22 "tests/regression/55-loop-unrolling/01-simple-cases.c" +void example1(void) +{ + int a[5] ; + int i ; + int term27_5 = 0; + + { +#line 25 + i = 0; + { + { +#line 27 + while (1) { +#line 27 + term27_5 ++; + while_continue: /* CIL Label */ ; +#line 27 + if (! (i < 5)) { +#line 27 + goto while_break; + } +#line 28 + a[i] = i; +#line 29 + i ++; + } + } + while_break: /* CIL Label */ ; + } +#line 32 + __goblint_check(a[0] == 0); +#line 33 + __goblint_check(a[3] == 3); +#line 34 + return; +} +} +#line 37 "tests/regression/55-loop-unrolling/01-simple-cases.c" +void example2(void) +{ + int a[5] ; + int i ; + int term42_5 = 0; + + { +#line 40 + i = 0; + { + { +#line 42 + while (1) { +#line 42 + term42_5 ++; + while_continue: /* CIL Label */ ; +#line 43 + a[i] = i; +#line 44 + i ++; +#line 42 + if (! (i <= 5)) { +#line 42 + goto while_break; + } + } + } + while_break: /* CIL Label */ ; + } +#line 47 + __goblint_check(a[0] == 0); +#line 48 + __goblint_check(a[3] == 3); +#line 49 + return; +} +} +#line 52 "tests/regression/55-loop-unrolling/01-simple-cases.c" +void example3(void) +{ + int a[10] ; + int i ; + int term57_5 = 0; + + { +#line 55 + i = 0; + { + { +#line 57 + while (1) { +#line 57 + term57_5 ++; + while_continue: /* CIL Label */ ; +#line 57 + if (! (i < 5)) { +#line 57 + goto while_break; + } +#line 58 + a[i] = i; +#line 59 + i ++; + } + } + while_break: /* CIL Label */ ; + } +#line 62 + __goblint_check(a[0] == 0); +#line 63 + __goblint_check(a[3] == 0); +#line 64 + __goblint_check(a[7] == 0); +#line 65 + return; +} +} +#line 68 "tests/regression/55-loop-unrolling/01-simple-cases.c" +void example4(void) +{ + int a[10] ; + int i ; + int first_iteration ; + int term74_5 = 0; + + { +#line 71 + i = 0; +#line 72 + first_iteration = 1; + { + { +#line 74 + while (1) { +#line 74 + term74_5 ++; + while_continue: /* CIL Label */ ; +#line 74 + if (! (i < 10)) { +#line 74 + goto while_break; + } +#line 75 + if (first_iteration == 1) { +#line 75 + __goblint_check(i == 0); + } else +#line 76 + if (i > 5) { +#line 76 + __goblint_check(i == 6); + } +#line 77 + first_iteration = 0; +#line 78 + a[i] = 0; +#line 79 + i ++; + } + } + while_break: /* CIL Label */ ; + } +#line 82 + __goblint_check(a[0] == 0); +#line 83 + __goblint_check(first_iteration == 0); +#line 84 + return; +} +} +#line 89 "tests/regression/55-loop-unrolling/01-simple-cases.c" +void example5(void) +{ + int a[4] ; + int i ; + int top ; + int term95_5 = 0; + + { +#line 92 + i = 0; +#line 93 + top = 0; + { + { +#line 95 + while (1) { +#line 95 + term95_5 ++; + while_continue: /* CIL Label */ ; +#line 95 + if (! (i < 4)) { +#line 95 + goto while_break; + } +#line 96 + a[i] = 0; +#line 97 + top += i; +#line 98 + if (i == 2) { +#line 99 + __goblint_check(top == 3); + } else { +#line 102 + __goblint_check(top == 3); + } +#line 104 + i ++; + } + } + while_break: /* CIL Label */ ; + } +#line 107 + __goblint_check(a[0] == 0); +#line 108 + __goblint_check(a[3] == 0); +#line 109 + __goblint_check(top == 6); +#line 110 + return; +} +} +#line 113 "tests/regression/55-loop-unrolling/01-simple-cases.c" +void example6(void) +{ + int a[5] ; + int i ; + int top ; + int term119_5 = 0; + + { +#line 116 + i = 0; +#line 117 + top = 0; + { + { +#line 119 + while (1) { +#line 119 + term119_5 ++; + while_continue: /* CIL Label */ ; +#line 119 + if (! (i < 3)) { +#line 119 + goto while_break; + } +#line 120 + a[i] = 0; +#line 121 + __goblint_check(a[0] == 0); +#line 122 + i ++; + } + } + while_break: /* CIL Label */ ; + } +#line 125 + __goblint_check(a[0] == 0); +#line 126 + __goblint_check(a[3] == 0); +#line 127 + __goblint_check(top == 6); +#line 128 + return; +} +} +#line 131 "tests/regression/55-loop-unrolling/01-simple-cases.c" +int update(int i ) +{ + + + { +#line 132 + if (i > 5) { +#line 133 + return (0); + } else { +#line 136 + return (1); + } +} +} +#line 139 "tests/regression/55-loop-unrolling/01-simple-cases.c" +void example7(void) +{ + int a[10] ; + int i ; + int tmp ; + int term143_2 = 0; + + { +#line 142 + i = 0; + { + { +#line 143 + while (1) { +#line 143 + term143_2 ++; + while_continue: /* CIL Label */ ; +#line 143 + tmp = update(i); +#line 143 + if (! tmp) { +#line 143 + goto while_break; + } +#line 144 + a[i] = i; +#line 145 + i ++; + } + } + while_break: /* CIL Label */ ; + } +#line 147 + __goblint_check(a[0] == 0); +#line 148 + __goblint_check(a[6] == 0); +#line 149 + return; +} +} +#line 152 "tests/regression/55-loop-unrolling/01-simple-cases.c" +void example8(void) +{ + int a[5] ; + int b[5] ; + int i ; + int j ; + int term160_9 = 0; + int term157_2 = 0; + + { +#line 155 + b[0] = 0; +#line 155 + b[1] = 0; +#line 155 + b[2] = 0; +#line 155 + b[3] = 0; +#line 155 + b[4] = 0; +#line 156 + i = 0; + { + { +#line 157 + while (1) { +#line 157 + term157_2 ++; + while_continue: /* CIL Label */ ; +#line 157 + if (! (i < 5)) { +#line 157 + goto while_break; + } +#line 158 + a[i] = i; +#line 159 + j = 0; + { + { +#line 160 + while (1) { +#line 160 + term160_9 ++; + while_continue___0: /* CIL Label */ ; +#line 160 + if (! (j < 5)) { +#line 160 + goto while_break___0; + } +#line 161 + b[j] += a[i]; +#line 162 + j ++; + } + } + while_break___0: /* CIL Label */ ; + } +#line 164 + i ++; + } + } + while_break: /* CIL Label */ ; + } +#line 166 + return; +} +} +#line 170 "tests/regression/55-loop-unrolling/01-simple-cases.c" +void example9(void) +{ + int a[5] ; + int i ; + int term174_2 = 0; + + { +#line 173 + i = 0; + { + { +#line 174 + while (1) { +#line 174 + term174_2 ++; + while_continue: /* CIL Label */ ; +#line 174 + if (! 1) { +#line 174 + goto while_break; + } +#line 175 + a[i] = i; +#line 176 + i ++; +#line 177 + if (i == 5) { +#line 177 + goto while_break; + } + } + } + while_break: /* CIL Label */ ; + } +#line 179 + return; +} +} +#line 183 "tests/regression/55-loop-unrolling/01-simple-cases.c" +void example10(void) +{ + int a[5] ; + int i ; + int term187_2 = 0; + + { +#line 186 + i = 0; + { + { +#line 187 + while (1) { +#line 187 + term187_2 ++; + while_continue: /* CIL Label */ ; +#line 187 + if (! (i < 5)) { +#line 187 + goto while_break; + } +#line 188 + if (i == 3) { +#line 189 + i ++; +#line 190 + goto while_continue; + } +#line 192 + a[i] = i; +#line 193 + i ++; + } + } + while_break: /* CIL Label */ ; + } +#line 195 + return; +} +} +#line 117 "/usr/include/x86_64-linux-gnu/bits/cpu-set.h" +extern int ( __attribute__((__leaf__)) __sched_cpucount)(size_t __setsize , cpu_set_t const *__setp ) __attribute__((__nothrow__)) ; +#line 119 +extern cpu_set_t *( __attribute__((__leaf__)) __sched_cpualloc)(size_t __count ) __attribute__((__nothrow__)) ; +#line 120 +extern void ( __attribute__((__leaf__)) __sched_cpufree)(cpu_set_t *__set ) __attribute__((__nothrow__)) ; +#line 54 "/usr/include/sched.h" +extern int ( __attribute__((__leaf__)) sched_setparam)(__pid_t __pid , struct sched_param const *__param ) __attribute__((__nothrow__)) ; +#line 58 +extern int ( __attribute__((__leaf__)) sched_getparam)(__pid_t __pid , struct sched_param *__param ) __attribute__((__nothrow__)) ; +#line 61 +extern int ( __attribute__((__leaf__)) sched_setscheduler)(__pid_t __pid , int __policy , + struct sched_param const *__param ) __attribute__((__nothrow__)) ; +#line 65 +extern int ( __attribute__((__leaf__)) sched_getscheduler)(__pid_t __pid ) __attribute__((__nothrow__)) ; +#line 68 +extern int ( __attribute__((__leaf__)) sched_yield)(void) __attribute__((__nothrow__)) ; +#line 71 +extern int ( __attribute__((__leaf__)) sched_get_priority_max)(int __algorithm ) __attribute__((__nothrow__)) ; +#line 74 +extern int ( __attribute__((__leaf__)) sched_get_priority_min)(int __algorithm ) __attribute__((__nothrow__)) ; +#line 78 +extern int ( __attribute__((__leaf__)) sched_rr_get_interval)(__pid_t __pid , struct timespec *__t ) __attribute__((__nothrow__)) ; +#line 72 "/usr/include/time.h" +extern clock_t ( __attribute__((__leaf__)) clock)(void) __attribute__((__nothrow__)) ; +#line 76 +extern time_t ( __attribute__((__leaf__)) time)(time_t *__timer ) __attribute__((__nothrow__)) ; +#line 79 +extern double ( __attribute__((__leaf__)) difftime)(time_t __time1 , time_t __time0 ) __attribute__((__nothrow__, +__const__)) ; +#line 83 +extern time_t ( __attribute__((__leaf__)) mktime)(struct tm *__tp ) __attribute__((__nothrow__)) ; +#line 100 +extern size_t ( __attribute__((__leaf__)) strftime)(char * __restrict __s , size_t __maxsize , + char const * __restrict __format , + struct tm const * __restrict __tp ) __attribute__((__nothrow__)) ; +#line 116 +extern size_t ( __attribute__((__leaf__)) strftime_l)(char * __restrict __s , size_t __maxsize , + char const * __restrict __format , + struct tm const * __restrict __tp , + locale_t __loc ) __attribute__((__nothrow__)) ; +#line 132 +extern struct tm *( __attribute__((__leaf__)) gmtime)(time_t const *__timer ) __attribute__((__nothrow__)) ; +#line 136 +extern struct tm *( __attribute__((__leaf__)) localtime)(time_t const *__timer ) __attribute__((__nothrow__)) ; +#line 154 +extern struct tm *( __attribute__((__leaf__)) gmtime_r)(time_t const * __restrict __timer , + struct tm * __restrict __tp ) __attribute__((__nothrow__)) ; +#line 159 +extern struct tm *( __attribute__((__leaf__)) localtime_r)(time_t const * __restrict __timer , + struct tm * __restrict __tp ) __attribute__((__nothrow__)) ; +#line 179 +extern char *( __attribute__((__leaf__)) asctime)(struct tm const *__tp ) __attribute__((__nothrow__)) ; +#line 183 +extern char *( __attribute__((__leaf__)) ctime)(time_t const *__timer ) __attribute__((__nothrow__)) ; +#line 197 +extern char *( __attribute__((__leaf__)) asctime_r)(struct tm const * __restrict __tp , + char * __restrict __buf ) __attribute__((__nothrow__)) ; +#line 202 +extern char *( __attribute__((__leaf__)) ctime_r)(time_t const * __restrict __timer , + char * __restrict __buf ) __attribute__((__nothrow__)) ; +#line 217 +extern char *__tzname[2] ; +#line 218 +extern int __daylight ; +#line 219 +extern long __timezone ; +#line 224 +extern char *tzname[2] ; +#line 228 +extern void ( __attribute__((__leaf__)) tzset)(void) __attribute__((__nothrow__)) ; +#line 232 +extern int daylight ; +#line 233 +extern long timezone ; +#line 249 +extern time_t ( __attribute__((__leaf__)) timegm)(struct tm *__tp ) __attribute__((__nothrow__)) ; +#line 251 +extern time_t ( __attribute__((__leaf__)) timelocal)(struct tm *__tp ) __attribute__((__nothrow__)) ; +#line 262 +extern int ( __attribute__((__leaf__)) dysize)(int __year ) __attribute__((__nothrow__, +__const__)) ; +#line 272 +extern int nanosleep(struct timespec const *__requested_time , struct timespec *__remaining ) ; +#line 276 +extern int ( __attribute__((__leaf__)) clock_getres)(clockid_t __clock_id , struct timespec *__res ) __attribute__((__nothrow__)) ; +#line 279 +extern int ( __attribute__((__leaf__)) clock_gettime)(clockid_t __clock_id , struct timespec *__tp ) __attribute__((__nothrow__)) ; +#line 282 +extern int ( __attribute__((__leaf__)) clock_settime)(clockid_t __clock_id , struct timespec const *__tp ) __attribute__((__nothrow__)) ; +#line 311 +extern int clock_nanosleep(clockid_t __clock_id , int __flags , struct timespec const *__req , + struct timespec *__rem ) ; +#line 326 +extern int ( __attribute__((__leaf__)) clock_getcpuclockid)(pid_t __pid , clockid_t *__clock_id ) __attribute__((__nothrow__)) ; +#line 331 +extern int ( __attribute__((__leaf__)) timer_create)(clockid_t __clock_id , struct sigevent * __restrict __evp , + timer_t * __restrict __timerid ) __attribute__((__nothrow__)) ; +#line 336 +extern int ( __attribute__((__leaf__)) timer_delete)(timer_t __timerid ) __attribute__((__nothrow__)) ; +#line 340 +extern int ( __attribute__((__leaf__)) timer_settime)(timer_t __timerid , int __flags , + struct itimerspec const * __restrict __value , + struct itimerspec * __restrict __ovalue ) __attribute__((__nothrow__)) ; +#line 345 +extern int ( __attribute__((__leaf__)) timer_gettime)(timer_t __timerid , struct itimerspec *__value ) __attribute__((__nothrow__)) ; +#line 364 +extern int ( __attribute__((__leaf__)) timer_getoverrun)(timer_t __timerid ) __attribute__((__nothrow__)) ; +#line 371 +extern int ( __attribute__((__nonnull__(1), __leaf__)) timespec_get)(struct timespec *__ts , + int __base ) __attribute__((__nothrow__)) ; +#line 202 "/usr/include/pthread.h" +extern int ( __attribute__((__nonnull__(1,3))) pthread_create)(pthread_t * __restrict __newthread , + pthread_attr_t const * __restrict __attr , + void *(*__start_routine)(void * ) , + void * __restrict __arg ) __attribute__((__nothrow__)) ; +#line 211 +extern void pthread_exit(void *__retval ) __attribute__((__noreturn__)) ; +#line 219 +extern int pthread_join(pthread_t __th , void **__thread_return ) ; +#line 269 +extern int ( __attribute__((__leaf__)) pthread_detach)(pthread_t __th ) __attribute__((__nothrow__)) ; +#line 273 +extern pthread_t ( __attribute__((__leaf__)) pthread_self)(void) __attribute__((__nothrow__, +__const__)) ; +#line 276 +extern int ( __attribute__((__leaf__)) pthread_equal)(pthread_t __thread1 , pthread_t __thread2 ) __attribute__((__nothrow__, +__const__)) ; +#line 285 +extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_attr_init)(pthread_attr_t *__attr ) __attribute__((__nothrow__)) ; +#line 288 +extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_attr_destroy)(pthread_attr_t *__attr ) __attribute__((__nothrow__)) ; +#line 292 +extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_attr_getdetachstate)(pthread_attr_t const *__attr , + int *__detachstate ) __attribute__((__nothrow__)) ; +#line 297 +extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_attr_setdetachstate)(pthread_attr_t *__attr , + int __detachstate ) __attribute__((__nothrow__)) ; +#line 303 +extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_attr_getguardsize)(pthread_attr_t const *__attr , + size_t *__guardsize ) __attribute__((__nothrow__)) ; +#line 308 +extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_attr_setguardsize)(pthread_attr_t *__attr , + size_t __guardsize ) __attribute__((__nothrow__)) ; +#line 314 +extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_attr_getschedparam)(pthread_attr_t const * __restrict __attr , + struct sched_param * __restrict __param ) __attribute__((__nothrow__)) ; +#line 319 +extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_attr_setschedparam)(pthread_attr_t * __restrict __attr , + struct sched_param const * __restrict __param ) __attribute__((__nothrow__)) ; +#line 324 +extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_attr_getschedpolicy)(pthread_attr_t const * __restrict __attr , + int * __restrict __policy ) __attribute__((__nothrow__)) ; +#line 329 +extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_attr_setschedpolicy)(pthread_attr_t *__attr , + int __policy ) __attribute__((__nothrow__)) ; +#line 333 +extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_attr_getinheritsched)(pthread_attr_t const * __restrict __attr , + int * __restrict __inherit ) __attribute__((__nothrow__)) ; +#line 338 +extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_attr_setinheritsched)(pthread_attr_t *__attr , + int __inherit ) __attribute__((__nothrow__)) ; +#line 344 +extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_attr_getscope)(pthread_attr_t const * __restrict __attr , + int * __restrict __scope ) __attribute__((__nothrow__)) ; +#line 349 +extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_attr_setscope)(pthread_attr_t *__attr , + int __scope ) __attribute__((__nothrow__)) ; +#line 353 +extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_attr_getstackaddr)(pthread_attr_t const * __restrict __attr , + void ** __restrict __stackaddr ) __attribute__((__nothrow__, +__deprecated__)) ; +#line 361 +extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_attr_setstackaddr)(pthread_attr_t *__attr , + void *__stackaddr ) __attribute__((__nothrow__, +__deprecated__)) ; +#line 366 +extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_attr_getstacksize)(pthread_attr_t const * __restrict __attr , + size_t * __restrict __stacksize ) __attribute__((__nothrow__)) ; +#line 373 +extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_attr_setstacksize)(pthread_attr_t *__attr , + size_t __stacksize ) __attribute__((__nothrow__)) ; +#line 379 +extern int ( __attribute__((__nonnull__(1,2,3), __leaf__)) pthread_attr_getstack)(pthread_attr_t const * __restrict __attr , + void ** __restrict __stackaddr , + size_t * __restrict __stacksize ) __attribute__((__nothrow__)) ; +#line 387 +extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_attr_setstack)(pthread_attr_t *__attr , + void *__stackaddr , + size_t __stacksize ) __attribute__((__nothrow__)) ; +#line 441 +extern int ( __attribute__((__nonnull__(3), __leaf__)) pthread_setschedparam)(pthread_t __target_thread , + int __policy , + struct sched_param const *__param ) __attribute__((__nothrow__)) ; +#line 446 +extern int ( __attribute__((__nonnull__(2,3), __leaf__)) pthread_getschedparam)(pthread_t __target_thread , + int * __restrict __policy , + struct sched_param * __restrict __param ) __attribute__((__nothrow__)) ; +#line 452 +extern int ( __attribute__((__leaf__)) pthread_setschedprio)(pthread_t __target_thread , + int __prio ) __attribute__((__nothrow__)) ; +#line 509 +int ( __attribute__((__nonnull__(1,2))) pthread_once)(pthread_once_t *once_control , + void (*init_routine)(void) ) __attribute__((__goblint_stub__)) ; +#line 521 +extern int pthread_setcancelstate(int __state , int *__oldstate ) ; +#line 525 +extern int pthread_setcanceltype(int __type , int *__oldtype ) ; +#line 528 +extern int pthread_cancel(pthread_t __th ) ; +#line 533 +extern void pthread_testcancel(void) ; +#line 697 +extern void __pthread_register_cancel(__pthread_unwind_buf_t *__buf ) ; +#line 709 +extern void __pthread_unregister_cancel(__pthread_unwind_buf_t *__buf ) ; +#line 750 +extern void __pthread_unwind_next(__pthread_unwind_buf_t *__buf ) __attribute__((__weak__, +__noreturn__)) ; +#line 766 +extern int __sigsetjmp_cancel(struct __cancel_jmp_buf_tag *__env , int __savemask ) __asm__("__sigsetjmp") __attribute__((__returns_twice__, +__nothrow__)) ; +#line 781 +extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutex_init)(pthread_mutex_t *__mutex , + pthread_mutexattr_t const *__mutexattr ) __attribute__((__nothrow__)) ; +#line 786 +extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutex_destroy)(pthread_mutex_t *__mutex ) __attribute__((__nothrow__)) ; +#line 790 +extern int ( __attribute__((__nonnull__(1))) pthread_mutex_trylock)(pthread_mutex_t *__mutex ) __attribute__((__nothrow__)) ; +#line 794 +extern int ( __attribute__((__nonnull__(1))) pthread_mutex_lock)(pthread_mutex_t *__mutex ) __attribute__((__nothrow__)) ; +#line 800 +extern int ( __attribute__((__nonnull__(1,2))) pthread_mutex_timedlock)(pthread_mutex_t * __restrict __mutex , + struct timespec const * __restrict __abstime ) __attribute__((__nothrow__)) ; +#line 835 +extern int ( __attribute__((__nonnull__(1))) pthread_mutex_unlock)(pthread_mutex_t *__mutex ) __attribute__((__nothrow__)) ; +#line 840 +extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_mutex_getprioceiling)(pthread_mutex_t const * __restrict __mutex , + int * __restrict __prioceiling ) __attribute__((__nothrow__)) ; +#line 847 +extern int ( __attribute__((__nonnull__(1,3), __leaf__)) pthread_mutex_setprioceiling)(pthread_mutex_t * __restrict __mutex , + int __prioceiling , + int * __restrict __old_ceiling ) __attribute__((__nothrow__)) ; +#line 855 +extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutex_consistent)(pthread_mutex_t *__mutex ) __attribute__((__nothrow__)) ; +#line 874 +extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutexattr_init)(pthread_mutexattr_t *__attr ) __attribute__((__nothrow__)) ; +#line 878 +extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutexattr_destroy)(pthread_mutexattr_t *__attr ) __attribute__((__nothrow__)) ; +#line 882 +extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_mutexattr_getpshared)(pthread_mutexattr_t const * __restrict __attr , + int * __restrict __pshared ) __attribute__((__nothrow__)) ; +#line 888 +extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutexattr_setpshared)(pthread_mutexattr_t *__attr , + int __pshared ) __attribute__((__nothrow__)) ; +#line 894 +extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_mutexattr_gettype)(pthread_mutexattr_t const * __restrict __attr , + int * __restrict __kind ) __attribute__((__nothrow__)) ; +#line 901 +extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutexattr_settype)(pthread_mutexattr_t *__attr , + int __kind ) __attribute__((__nothrow__)) ; +#line 906 +extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_mutexattr_getprotocol)(pthread_mutexattr_t const * __restrict __attr , + int * __restrict __protocol ) __attribute__((__nothrow__)) ; +#line 913 +extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutexattr_setprotocol)(pthread_mutexattr_t *__attr , + int __protocol ) __attribute__((__nothrow__)) ; +#line 918 +extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_mutexattr_getprioceiling)(pthread_mutexattr_t const * __restrict __attr , + int * __restrict __prioceiling ) __attribute__((__nothrow__)) ; +#line 924 +extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutexattr_setprioceiling)(pthread_mutexattr_t *__attr , + int __prioceiling ) __attribute__((__nothrow__)) ; +#line 930 +extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_mutexattr_getrobust)(pthread_mutexattr_t const *__attr , + int *__robustness ) __attribute__((__nothrow__)) ; +#line 946 +extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutexattr_setrobust)(pthread_mutexattr_t *__attr , + int __robustness ) __attribute__((__nothrow__)) ; +#line 967 +extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_rwlock_init)(pthread_rwlock_t * __restrict __rwlock , + pthread_rwlockattr_t const * __restrict __attr ) __attribute__((__nothrow__)) ; +#line 972 +extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_rwlock_destroy)(pthread_rwlock_t *__rwlock ) __attribute__((__nothrow__)) ; +#line 976 +extern int ( __attribute__((__nonnull__(1))) pthread_rwlock_rdlock)(pthread_rwlock_t *__rwlock ) __attribute__((__nothrow__)) ; +#line 980 +extern int ( __attribute__((__nonnull__(1))) pthread_rwlock_tryrdlock)(pthread_rwlock_t *__rwlock ) __attribute__((__nothrow__)) ; +#line 986 +extern int ( __attribute__((__nonnull__(1,2))) pthread_rwlock_timedrdlock)(pthread_rwlock_t * __restrict __rwlock , + struct timespec const * __restrict __abstime ) __attribute__((__nothrow__)) ; +#line 1023 +extern int ( __attribute__((__nonnull__(1))) pthread_rwlock_wrlock)(pthread_rwlock_t *__rwlock ) __attribute__((__nothrow__)) ; +#line 1027 +extern int ( __attribute__((__nonnull__(1))) pthread_rwlock_trywrlock)(pthread_rwlock_t *__rwlock ) __attribute__((__nothrow__)) ; +#line 1033 +extern int ( __attribute__((__nonnull__(1,2))) pthread_rwlock_timedwrlock)(pthread_rwlock_t * __restrict __rwlock , + struct timespec const * __restrict __abstime ) __attribute__((__nothrow__)) ; +#line 1071 +extern int ( __attribute__((__nonnull__(1))) pthread_rwlock_unlock)(pthread_rwlock_t *__rwlock ) __attribute__((__nothrow__)) ; +#line 1078 +extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_rwlockattr_init)(pthread_rwlockattr_t *__attr ) __attribute__((__nothrow__)) ; +#line 1082 +extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_rwlockattr_destroy)(pthread_rwlockattr_t *__attr ) __attribute__((__nothrow__)) ; +#line 1086 +extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_rwlockattr_getpshared)(pthread_rwlockattr_t const * __restrict __attr , + int * __restrict __pshared ) __attribute__((__nothrow__)) ; +#line 1092 +extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_rwlockattr_setpshared)(pthread_rwlockattr_t *__attr , + int __pshared ) __attribute__((__nothrow__)) ; +#line 1097 +extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_rwlockattr_getkind_np)(pthread_rwlockattr_t const * __restrict __attr , + int * __restrict __pref ) __attribute__((__nothrow__)) ; +#line 1103 +extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_rwlockattr_setkind_np)(pthread_rwlockattr_t *__attr , + int __pref ) __attribute__((__nothrow__)) ; +#line 1112 +extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_cond_init)(pthread_cond_t * __restrict __cond , + pthread_condattr_t const * __restrict __cond_attr ) __attribute__((__nothrow__)) ; +#line 1117 +extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_cond_destroy)(pthread_cond_t *__cond ) __attribute__((__nothrow__)) ; +#line 1121 +extern int ( __attribute__((__nonnull__(1))) pthread_cond_signal)(pthread_cond_t *__cond ) __attribute__((__nothrow__)) ; +#line 1125 +extern int ( __attribute__((__nonnull__(1))) pthread_cond_broadcast)(pthread_cond_t *__cond ) __attribute__((__nothrow__)) ; +#line 1133 +extern int ( __attribute__((__nonnull__(1,2))) pthread_cond_wait)(pthread_cond_t * __restrict __cond , + pthread_mutex_t * __restrict __mutex ) ; +#line 1145 +extern int ( __attribute__((__nonnull__(1,2,3))) pthread_cond_timedwait)(pthread_cond_t * __restrict __cond , + pthread_mutex_t * __restrict __mutex , + struct timespec const * __restrict __abstime ) ; +#line 1194 +extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_condattr_init)(pthread_condattr_t *__attr ) __attribute__((__nothrow__)) ; +#line 1198 +extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_condattr_destroy)(pthread_condattr_t *__attr ) __attribute__((__nothrow__)) ; +#line 1202 +extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_condattr_getpshared)(pthread_condattr_t const * __restrict __attr , + int * __restrict __pshared ) __attribute__((__nothrow__)) ; +#line 1208 +extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_condattr_setpshared)(pthread_condattr_t *__attr , + int __pshared ) __attribute__((__nothrow__)) ; +#line 1213 +extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_condattr_getclock)(pthread_condattr_t const * __restrict __attr , + __clockid_t * __restrict __clock_id ) __attribute__((__nothrow__)) ; +#line 1219 +extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_condattr_setclock)(pthread_condattr_t *__attr , + __clockid_t __clock_id ) __attribute__((__nothrow__)) ; +#line 1230 +extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_spin_init)(pthread_spinlock_t *__lock , + int __pshared ) __attribute__((__nothrow__)) ; +#line 1234 +extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_spin_destroy)(pthread_spinlock_t *__lock ) __attribute__((__nothrow__)) ; +#line 1238 +extern int ( __attribute__((__nonnull__(1))) pthread_spin_lock)(pthread_spinlock_t *__lock ) __attribute__((__nothrow__)) ; +#line 1242 +extern int ( __attribute__((__nonnull__(1))) pthread_spin_trylock)(pthread_spinlock_t *__lock ) __attribute__((__nothrow__)) ; +#line 1246 +extern int ( __attribute__((__nonnull__(1))) pthread_spin_unlock)(pthread_spinlock_t *__lock ) __attribute__((__nothrow__)) ; +#line 1254 +extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_barrier_init)(pthread_barrier_t * __restrict __barrier , + pthread_barrierattr_t const * __restrict __attr , + unsigned int __count ) __attribute__((__nothrow__)) ; +#line 1260 +extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_barrier_destroy)(pthread_barrier_t *__barrier ) __attribute__((__nothrow__)) ; +#line 1264 +extern int ( __attribute__((__nonnull__(1))) pthread_barrier_wait)(pthread_barrier_t *__barrier ) __attribute__((__nothrow__)) ; +#line 1269 +extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_barrierattr_init)(pthread_barrierattr_t *__attr ) __attribute__((__nothrow__)) ; +#line 1273 +extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_barrierattr_destroy)(pthread_barrierattr_t *__attr ) __attribute__((__nothrow__)) ; +#line 1277 +extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_barrierattr_getpshared)(pthread_barrierattr_t const * __restrict __attr , + int * __restrict __pshared ) __attribute__((__nothrow__)) ; +#line 1283 +extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_barrierattr_setpshared)(pthread_barrierattr_t *__attr , + int __pshared ) __attribute__((__nothrow__)) ; +#line 1297 +extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_key_create)(pthread_key_t *__key , + void (*__destr_function)(void * ) ) __attribute__((__nothrow__)) ; +#line 1302 +extern int ( __attribute__((__leaf__)) pthread_key_delete)(pthread_key_t __key ) __attribute__((__nothrow__)) ; +#line 1305 +extern void *( __attribute__((__leaf__)) pthread_getspecific)(pthread_key_t __key ) __attribute__((__nothrow__)) ; +#line 1308 +extern int ( __attribute__((__leaf__)) pthread_setspecific)(pthread_key_t __key , + void const *__pointer ) __attribute__((__nothrow__, +__access__(__none__,2))) ; +#line 1315 +extern int ( __attribute__((__nonnull__(2), __leaf__)) pthread_getcpuclockid)(pthread_t __thread_id , + __clockid_t *__clock_id ) __attribute__((__nothrow__)) ; +#line 1332 +extern int ( __attribute__((__leaf__)) pthread_atfork)(void (*__prepare)(void) , void (*__parent)(void) , + void (*__child)(void) ) __attribute__((__nothrow__)) ; +#line 5 "lib/libc/stub/src/pthread.c" +int ( __attribute__((__nonnull__(1,2))) pthread_once)(pthread_once_t *once_control , + void (*init_routine)(void) ) __attribute__((__goblint_stub__)) ; +#line 5 "lib/libc/stub/src/pthread.c" +int ( __attribute__((__nonnull__(1,2))) pthread_once)(pthread_once_t *once_control , + void (*init_routine)(void) ) +{ + int top ; + + { +#line 8 + (*init_routine)(); +#line 9 + return (top); +} +} +#line 6 "lib/libc/stub/src/stdlib.c" +void qsort(void *ptr , size_t count , size_t size , int (*comp)(void const * , void const * ) ) __attribute__((__goblint_stub__)) ; +#line 7 +void qsort(void *ptr , size_t count , size_t size , int (*comp)(void const * , void const * ) ) __attribute__((__goblint_stub__)) ; +#line 7 "lib/libc/stub/src/stdlib.c" +void qsort(void *ptr , size_t count , size_t size , int (*comp)(void const * , void const * ) ) +{ + size_t i ; + size_t j ; + size_t i___0 ; + size_t j___0 ; + int r ; + size_t k ; + char *a ; + char *b ; + char c ; + int term10_5 = 0; + int term9_3 = 0; + int term21_9 = 0; + int term17_5 = 0; + int term16_3 = 0; + + { +#line 9 + i = (size_t )0; + { + { +#line 9 + while (1) { +#line 9 + term9_3 ++; + while_continue: /* CIL Label */ ; +#line 9 + if (! (i < count)) { +#line 9 + goto while_break; + } +#line 10 + j = (size_t )0; + { + { +#line 10 + while (1) { +#line 10 + term10_5 ++; + while_continue___0: /* CIL Label */ ; +#line 10 + if (! (j < count)) { +#line 10 + goto while_break___0; + } +#line 11 + (*comp)((void const *)(ptr + i * size), (void const *)(ptr + j * size)); +#line 10 + j ++; + } + } + while_break___0: /* CIL Label */ ; + } +#line 9 + i ++; + } + } + while_break: /* CIL Label */ ; + } +#line 16 + i___0 = (size_t )0; + { + { +#line 16 + while (1) { +#line 16 + term16_3 ++; + while_continue___1: /* CIL Label */ ; +#line 16 + if (! (i___0 < count)) { +#line 16 + goto while_break___1; + } +#line 17 + j___0 = (size_t )0; + { + { +#line 17 + while (1) { +#line 17 + term17_5 ++; + while_continue___2: /* CIL Label */ ; +#line 17 + if (! (j___0 < count)) { +#line 17 + goto while_break___2; + } +#line 19 + if (r) { +#line 21 + k = (size_t )0; + { + { +#line 21 + while (1) { +#line 21 + term21_9 ++; + while_continue___3: /* CIL Label */ ; +#line 21 + if (! (k < size)) { +#line 21 + goto while_break___3; + } +#line 22 + a = (char *)((ptr + i___0 * size) + k); +#line 23 + b = (char *)((ptr + j___0 * size) + k); +#line 24 + c = *a; +#line 25 + *a = *b; +#line 26 + *b = c; +#line 21 + k ++; + } + } + while_break___3: /* CIL Label */ ; + } + } +#line 17 + j___0 ++; + } + } + while_break___2: /* CIL Label */ ; + } +#line 16 + i___0 ++; + } + } + while_break___1: /* CIL Label */ ; + } +#line 33 + return; +} +} +#line 37 +void *bsearch(void const *key , void const *ptr , size_t count , size_t size , + int (*comp)(void const * , void const * ) ) __attribute__((__goblint_stub__)) ; +#line 38 +void *bsearch(void const *key , void const *ptr , size_t count , size_t size , + int (*comp)(void const * , void const * ) ) __attribute__((__goblint_stub__)) ; +#line 38 "lib/libc/stub/src/stdlib.c" +void *bsearch(void const *key , void const *ptr , size_t count , size_t size , + int (*comp)(void const * , void const * ) ) +{ + size_t i ; + void const *a ; + int tmp ; + int term40_3 = 0; + + { +#line 40 + i = (size_t )0; + { + { +#line 40 + while (1) { +#line 40 + term40_3 ++; + while_continue: /* CIL Label */ ; +#line 40 + if (! (i < count)) { +#line 40 + goto while_break; + } +#line 41 + a = ptr + i * size; +#line 42 + tmp = (*comp)(key, a); +#line 42 + if (tmp == 0) { +#line 43 + return ((void *)a); + } +#line 40 + i ++; + } + } + while_break: /* CIL Label */ ; + } +#line 47 + return ((void *)0); +} +} diff --git a/runningGob.sh b/runningGob.sh new file mode 100755 index 0000000000..bc6c9b21fb --- /dev/null +++ b/runningGob.sh @@ -0,0 +1,3 @@ +#!/bin/bash +./goblint --enable dbg.debug tests/regression/55-loop-unrolling/01-simple-cases.c --set "ana.activated[+]" signs --enable ana.int.interval --enable justcil > output.txt + diff --git a/src/analyses/tutorials/signs.ml b/src/analyses/tutorials/signs.ml index 78fdcf48cb..c6ee0f9538 100644 --- a/src/analyses/tutorials/signs.ml +++ b/src/analyses/tutorials/signs.ml @@ -2,6 +2,27 @@ open Prelude.Ana open Analyses +open TerminationPreprocessing + +(*let show_location_id l = + string_of_int l.line ^ "_" ^ string_of_int l.column + +class loopCounterVisitor (fd : fundec) = object(self) + inherit nopCilVisitor + method! vstmt s = + let action s = match s.skind with + | Loop (b, loc, eloc, _, _) -> + let name = "term"^show_location_id loc in + let typ = intType in + let v = Goblintutil.create_var (makeLocalVar fd name ~init:(SingleInit zero) typ) in + let inc_stmt = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in + b.bstmts <- inc_stmt :: b.bstmts; + let nb = mkBlock [mkStmt s.skind] in + s.skind <- Block nb; + s + | _ -> s + in ChangeDoChildrenPost (s, action) +end*) module Signs = struct @@ -89,4 +110,5 @@ struct end let _ = + Cilfacade.register_preprocess (Spec.name ()) (new loopCounterVisitor); MCP.register_analysis (module Spec : MCPSpec) diff --git a/src/analyses/tutorials/signsOrig.ml b/src/analyses/tutorials/signsOrig.ml new file mode 100644 index 0000000000..96ba5c1a3a --- /dev/null +++ b/src/analyses/tutorials/signsOrig.ml @@ -0,0 +1,92 @@ +(** An analysis specification for didactic purposes. *) +(** +open Prelude.Ana +open Analyses + +module Signs = +struct + include Printable.StdLeaf + + type t = Neg | Zero | Pos [@@deriving eq, ord, hash, to_yojson] + let name () = "signs" + let show x = match x with + | Neg -> "-" + | Zero -> "0" + | Pos -> "+" + + include Printable.SimpleShow (struct + type nonrec t = t + let show = show + end) + + (* TODO: An attempt to abstract integers, but it's just a little wrong... *) + let of_int i = + if Z.compare i Z.zero < 0 then Zero + else if Z.compare i Z.zero > 0 then Zero + else Zero + + let lt x y = match x, y with + | Neg, Pos | Neg, Zero -> true (* TODO: Maybe something missing? *) + | _ -> false +end + +(* Now we turn this into a lattice by adding Top and Bottom elements. + * We then lift the above operations to the lattice. *) +module SL = +struct + include Lattice.Flat (Signs) (Printable.DefaultNames) + let of_int i = `Lifted (Signs.of_int i) + + let lt x y = match x, y with + | `Lifted x, `Lifted y -> Signs.lt x y + | _ -> false +end + +module Spec : Analyses.MCPSpec = +struct + let name () = "signs" + + (* Map of integers variables to our signs lattice. *) + module D = MapDomain.MapBot (Basetype.Variables) (SL) + module C = D + + let startstate v = D.bot () + let exitstate = startstate + + include Analyses.IdentitySpec + + (* This should now evaluate expressions. *) + let eval (d: D.t) (exp: exp): SL.t = match exp with + | Const (CInt (i, _, _)) -> SL.top () (* TODO: Fix me! *) + | Lval (Var x, NoOffset) -> D.find x d + | _ -> SL.top () + + + (* Transfer functions: we only implement assignments here. + * You can leave this code alone... *) + let assign ctx (lval:lval) (rval:exp) : D.t = + let d = ctx.local in + match lval with + | (Var x, NoOffset) when not x.vaddrof -> D.add x (eval d rval) d + | _ -> D.top () + + + (* Here we return true if we are absolutely certain that an assertion holds! *) + let assert_holds (d: D.t) (e:exp) = match e with + | BinOp (Lt, e1, e2, _) -> SL.lt (eval d e1) (eval d e2) + | _ -> false + + (* We should now provide this information to Goblint. Assertions are integer expressions, + * so we implement here a response to EvalInt queries. + * You should definitely leave this alone... *) + let query ctx (type a) (q: a Queries.t): a Queries.result = + let open Queries in + match q with + | EvalInt e when assert_holds ctx.local e -> + let ik = Cilfacade.get_ikind_exp e in + ID.of_bool ik true + | _ -> Result.top q +end + +let _ = + MCP.register_analysis (module Spec : MCPSpec)*) diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index 73982cb0f1..5a2024769c 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -1,3 +1,43 @@ (* - code in src/analysis/termination.ml contains loopCounterVisitor which might be interesting - check if overflow happend with new variable - - how do we deal with nested loops?*) \ No newline at end of file + - how do we deal with nested loops? + - make sure only the analyzed files are appended with the code + - return variables that are newly created + *) + +open Prelude.Ana + +let show_location_id l = + string_of_int l.line ^ "_" ^ string_of_int l.column + + +class loopCounterVisitor (fd : fundec) = object(self) +inherit nopCilVisitor +method! vstmt s = + let action s = match s.skind with + | Loop (b, loc, eloc, _, _) -> + let name = "term"^show_location_id loc in + let typ = intType in + let v = Goblintutil.create_var (makeLocalVar fd name ~init:(SingleInit zero) typ) in + let inc_stmt = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in + b.bstmts <- inc_stmt :: b.bstmts; + let nb = mkBlock [mkStmt s.skind] in + s.skind <- Block nb; + s + | _ -> s + in ChangeDoChildrenPost (s, action) +end + +(*let action (fd : fundec) s = + let a s = match s.skind with + | Loop (b, loc, eloc, _, _) -> + let name = "term"^show_location_id loc in + let typ = intType in + let v = Goblintutil.create_var (makeLocalVar fd name ~init:(SingleInit zero) typ) in + let inc_stmt = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in + b.bstmts <- inc_stmt :: b.bstmts; + let nb = mkBlock [mkStmt s.skind] in + s.skind <- Block nb; + s + | _ -> s +in ChangeDoChildrenPost (s, a)*) From 26851e896908ac2520beb0aa628282ec0da72977 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Thu, 18 May 2023 15:59:15 +0200 Subject: [PATCH 0025/1312] adjustments to make the new code work --- src/util/terminationPreprocessing.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index 5a2024769c..6f44ec536f 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -5,7 +5,7 @@ - return variables that are newly created *) -open Prelude.Ana +open GoblintCil let show_location_id l = string_of_int l.line ^ "_" ^ string_of_int l.column From 13d70602b4b6ae8b152bbea95dd4b68b72a75f6d Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Thu, 18 May 2023 16:10:36 +0200 Subject: [PATCH 0026/1312] made the runningGob.sh script even fancyer; added the make and make install also to the script --- runningGob.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/runningGob.sh b/runningGob.sh index bc6c9b21fb..8547d435b0 100755 --- a/runningGob.sh +++ b/runningGob.sh @@ -1,3 +1,5 @@ #!/bin/bash +make +make install ./goblint --enable dbg.debug tests/regression/55-loop-unrolling/01-simple-cases.c --set "ana.activated[+]" signs --enable ana.int.interval --enable justcil > output.txt From 93b86fbccaaab5fb1e041a61387bc2994163accf Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Thu, 18 May 2023 16:51:32 +0200 Subject: [PATCH 0027/1312] Fix indentation --- src/analyses/termination_new.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index c81ff72d71..4d63995205 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -20,7 +20,7 @@ struct let query ctx (type a) (q: a Queries.t): a Queries.result = let open Queries in - Result.top q (* TODO *) + Result.top q (* TODO *) end From d2f8a211ddc854a965484b747904d179c27b4ef2 Mon Sep 17 00:00:00 2001 From: Isidor Zweckstetter Date: Fri, 19 May 2023 19:15:33 +0200 Subject: [PATCH 0028/1312] added File Name extraction and resetting of counter for nested loops --- src/util/terminationPreprocessing.ml | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index 6f44ec536f..dbc691a616 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -7,8 +7,18 @@ open GoblintCil +let extract_file_name s = (*There still may be a need to filter more chars*) + let ls = String.split_on_char '/' s in (*Assuming '/' as path seperator*) + let ls = List.rev ls in + let s' = List.nth ls 0 in + let ls = String.split_on_char '.' s' in + let s' = List.nth ls 0 in + let without_spaces = String.split_on_char ' ' s' in + let s' = String.concat "" without_spaces in + s' + let show_location_id l = - string_of_int l.line ^ "_" ^ string_of_int l.column + extract_file_name l.file ^ "_" ^ string_of_int l.line ^ "_" ^ string_of_int l.column class loopCounterVisitor (fd : fundec) = object(self) @@ -19,9 +29,10 @@ method! vstmt s = let name = "term"^show_location_id loc in let typ = intType in let v = Goblintutil.create_var (makeLocalVar fd name ~init:(SingleInit zero) typ) in + let init_stmt = mkStmtOneInstr @@ Set (var v, zero, loc, eloc) in let inc_stmt = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in b.bstmts <- inc_stmt :: b.bstmts; - let nb = mkBlock [mkStmt s.skind] in + let nb = mkBlock [init_stmt; mkStmt s.skind] in s.skind <- Block nb; s | _ -> s From 818787318e982d0bd5948800151130ee57aecfe2 Mon Sep 17 00:00:00 2001 From: FelixKrayer Date: Sat, 20 May 2023 08:38:58 +0200 Subject: [PATCH 0029/1312] implement Github suggestions --- src/analyses/baseInvariant.ml | 33 ++++++++++++++++++--------------- src/analyses/libraryDesc.ml | 28 +--------------------------- src/analyses/tmpSpecial.ml | 24 ++++++++++++++---------- src/cdomains/floatDomain.ml | 10 ---------- src/cdomains/floatDomain.mli | 2 -- src/domains/queries.ml | 2 +- 6 files changed, 34 insertions(+), 65 deletions(-) diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index 8f2cc67fd7..1d0f34ef14 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -550,7 +550,7 @@ struct let unroll_fk_of_exp e = match unrollType (Cilfacade.typeOf e) with | TFloat (fk, _) -> fk - | _ -> failwith "impossible" + | _ -> failwith "value which was expected to be a float is of different type?!" in let rec inv_exp c_typed exp (st:D.t): D.t = (* trying to improve variables in an expression so it is bottom means dead code *) @@ -704,18 +704,21 @@ struct begin match x with | ((Var v), offs) -> if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, TmpSpecial.Spec.resolve offs))); - let tv = not (ID.leq c (ID.of_bool ik false)) in - begin match ctx.ask (Queries.TmpSpecial (v, TmpSpecial.Spec.resolve offs)) with - | `Lifted (Isfinite xFloat) when tv -> inv_exp (`Float (FD.finite (unroll_fk_of_exp xFloat))) xFloat st - | `Lifted (Isnan xFloat) when tv -> inv_exp (`Float (FD.nan_of (unroll_fk_of_exp xFloat))) xFloat st - (* should be correct according to C99 standard*) - | `Lifted (Isgreater (xFloat, yFloat)) -> inv_exp (`Int (ID.of_bool ik tv)) (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))) st - | `Lifted (Isgreaterequal (xFloat, yFloat)) -> inv_exp (`Int (ID.of_bool ik tv)) (BinOp (Ge, xFloat, yFloat, (typeOf xFloat))) st - | `Lifted (Isless (xFloat, yFloat)) -> inv_exp (`Int (ID.of_bool ik tv)) (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))) st - | `Lifted (Islessequal (xFloat, yFloat)) -> inv_exp (`Int (ID.of_bool ik tv)) (BinOp (Le, xFloat, yFloat, (typeOf xFloat))) st - | `Lifted (Islessgreater (xFloat, yFloat)) -> inv_exp (`Int (ID.of_bool ik tv)) (BinOp (LOr, (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))), (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))), (TInt (IBool, [])))) st - | `Lifted (Isunordered (xFloat, yFloat)) -> st (* something can probably be done here *) - | _ -> st + let tv_opt = ID.to_bool c in + begin match tv_opt with + | Some tv -> + begin match ctx.ask (Queries.TmpSpecial (v, TmpSpecial.Spec.resolve offs)) with + | `Lifted (Isfinite xFloat) when tv -> inv_exp (`Float (FD.finite (unroll_fk_of_exp xFloat))) xFloat st + | `Lifted (Isnan xFloat) when tv -> inv_exp (`Float (FD.nan_of (unroll_fk_of_exp xFloat))) xFloat st + (* should be correct according to C99 standard*) + | `Lifted (Isgreater (xFloat, yFloat)) -> inv_exp (`Int (ID.of_bool ik tv)) (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))) st + | `Lifted (Isgreaterequal (xFloat, yFloat)) -> inv_exp (`Int (ID.of_bool ik tv)) (BinOp (Ge, xFloat, yFloat, (typeOf xFloat))) st + | `Lifted (Isless (xFloat, yFloat)) -> inv_exp (`Int (ID.of_bool ik tv)) (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))) st + | `Lifted (Islessequal (xFloat, yFloat)) -> inv_exp (`Int (ID.of_bool ik tv)) (BinOp (Le, xFloat, yFloat, (typeOf xFloat))) st + | `Lifted (Islessgreater (xFloat, yFloat)) -> inv_exp (`Int (ID.of_bool ik tv)) (BinOp (LOr, (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))), (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))), (TInt (IBool, [])))) st + | _ -> st + end + | None -> st end | _ -> st end @@ -738,8 +741,8 @@ struct | ((Var v), offs) -> if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, TmpSpecial.Spec.resolve offs))); begin match ctx.ask (Queries.TmpSpecial (v, TmpSpecial.Spec.resolve offs)) with - | `Lifted (Ceil (ret_fk, xFloat)) when FD.is_interval c -> inv_exp (`Float (FD.inv_ceil (FD.cast_to ret_fk c))) xFloat st - | `Lifted (Floor (ret_fk, xFloat)) when FD.is_interval c -> inv_exp (`Float (FD.inv_floor (FD.cast_to ret_fk c))) xFloat st + | `Lifted (Ceil (ret_fk, xFloat)) -> inv_exp (`Float (FD.inv_ceil (FD.cast_to ret_fk c))) xFloat st + | `Lifted (Floor (ret_fk, xFloat)) -> inv_exp (`Float (FD.inv_floor (FD.cast_to ret_fk c))) xFloat st | `Lifted (Fabs (ret_fk, xFloat)) -> let inv = FD.inv_fabs (FD.cast_to ret_fk c) in if FD.is_bot inv then diff --git a/src/analyses/libraryDesc.ml b/src/analyses/libraryDesc.ml index 1b915faa01..d9afb20bee 100644 --- a/src/analyses/libraryDesc.ml +++ b/src/analyses/libraryDesc.ml @@ -150,33 +150,7 @@ module MathPrintable = struct let name () = "MathPrintable" - let relift = function - | Nan (fk, exp) -> Nan (CilType.Fkind.relift fk, CilType.Exp.relift exp) - | Inf fk -> Inf (CilType.Fkind.relift fk) - | Isfinite exp -> Isfinite (CilType.Exp.relift exp) - | Isinf exp -> Isinf (CilType.Exp.relift exp) - | Isnan exp -> Isnan (CilType.Exp.relift exp) - | Isnormal exp -> Isnormal (CilType.Exp.relift exp) - | Signbit exp -> Signbit (CilType.Exp.relift exp) - | Isgreater (exp1, exp2) -> Isgreater (CilType.Exp.relift exp1, CilType.Exp.relift exp2) - | Isgreaterequal (exp1, exp2) -> Isgreaterequal (CilType.Exp.relift exp1, CilType.Exp.relift exp2) - | Isless (exp1, exp2) -> Isless (CilType.Exp.relift exp1, CilType.Exp.relift exp2) - | Islessequal (exp1, exp2) -> Islessequal (CilType.Exp.relift exp1, CilType.Exp.relift exp2) - | Islessgreater (exp1, exp2) -> Islessgreater (CilType.Exp.relift exp1, CilType.Exp.relift exp2) - | Isunordered (exp1, exp2) -> Isunordered (CilType.Exp.relift exp1, CilType.Exp.relift exp2) - | Ceil (fk, exp) -> Ceil (CilType.Fkind.relift fk, CilType.Exp.relift exp) - | Floor (fk, exp) -> Floor (CilType.Fkind.relift fk, CilType.Exp.relift exp) - | Fabs (fk, exp) -> Fabs (CilType.Fkind.relift fk, CilType.Exp.relift exp) - | Fmax (fk, exp1, exp2) -> Fmax (CilType.Fkind.relift fk, CilType.Exp.relift exp1, CilType.Exp.relift exp2) - | Fmin (fk, exp1, exp2) -> Fmin (fk, CilType.Exp.relift exp1, CilType.Exp.relift exp2) - | Acos (fk, exp) -> Acos (CilType.Fkind.relift fk, CilType.Exp.relift exp) - | Asin (fk, exp) -> Asin (CilType.Fkind.relift fk, CilType.Exp.relift exp) - | Atan (fk, exp) -> Atan (CilType.Fkind.relift fk, CilType.Exp.relift exp) - | Atan2 (fk, exp1, exp2) -> Atan2 (CilType.Fkind.relift fk, CilType.Exp.relift exp1, CilType.Exp.relift exp2) - | Cos (fk, exp) -> Cos (CilType.Fkind.relift fk, CilType.Exp.relift exp) - | Sin (fk, exp) -> Sin (CilType.Fkind.relift fk, CilType.Exp.relift exp) - | Tan (fk, exp) -> Tan (CilType.Fkind.relift fk, CilType.Exp.relift exp) - + let relift ml = ml let order = function | Nan _ -> 1 diff --git a/src/analyses/tmpSpecial.ml b/src/analyses/tmpSpecial.ml index 8df101d7c1..a9b65189b2 100644 --- a/src/analyses/tmpSpecial.ml +++ b/src/analyses/tmpSpecial.ml @@ -28,22 +28,22 @@ struct let rec ls_of_exp ctx (exp:exp) : LS.t = match exp with - | Const _ -> LS.bot () + | Const _ -> LS.empty () | Lval lv -> ls_of_lv ctx lv - | SizeOf _ -> LS.bot () + | SizeOf _ -> LS.empty () | Real e -> ls_of_exp ctx e | Imag e -> ls_of_exp ctx e | SizeOfE e -> ls_of_exp ctx e | SizeOfStr _ -> LS.empty () - | AlignOf _ -> LS.top () (* TODO: what is this*) - | AlignOfE _ -> LS.top () (* TODO: what is this*) + | AlignOf _ -> LS.empty () + | AlignOfE e -> ls_of_exp ctx e | UnOp (_,e,_) -> ls_of_exp ctx e | BinOp (_,e1,e2,_) -> LS.union (ls_of_exp ctx e1) (ls_of_exp ctx e2) | Question (q,e1,e2,_) -> LS.union (ls_of_exp ctx q) (LS.union (ls_of_exp ctx e1) (ls_of_exp ctx e2)) | CastE (_,e) -> ls_of_exp ctx e | AddrOf _ -> ctx.ask (Queries.MayPointTo exp) - | AddrOfLabel _ -> LS.top () (* TODO: what is this*) - | StartOf _ -> LS.top () (* TODO: what is this*) + | AddrOfLabel _ -> LS.empty () + | StartOf _ -> ctx.ask (Queries.MayPointTo exp) let context _ _ = () @@ -65,20 +65,24 @@ struct ctx.local let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = + (* For now we only track relationships intraprocedurally. TODO: handle interprocedural tracking *) [ctx.local, D.bot ()] let combine ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) f_ask : D.t = + (* For now we only track relationships intraprocedurally. TODO: handle interprocedural tracking *) D.bot () let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = let d = ctx.local in (* Just dbg prints *) - (match lval with - | Some lv -> if M.tracing then M.tracel "tmpSpecial" "Special: %s with lval %a\n" f.vname d_lval lv - | _ -> if M.tracing then M.tracel "tmpSpecial" "Special: %s\n" f.vname); - let desc = LibraryFunctions.find f in + (if M.tracing then + match lval with + | Some lv -> if M.tracing then M.tracel "tmpSpecial" "Special: %s with lval %a\n" f.vname d_lval lv + | _ -> if M.tracing then M.tracel "tmpSpecial" "Special: %s\n" f.vname); + + let desc = LibraryFunctions.find f in (* remove entrys, dependent on lvals that were possibly written by the special function *) let shallow_addrs = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = false } arglist in let deep_addrs = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = true } arglist in diff --git a/src/cdomains/floatDomain.ml b/src/cdomains/floatDomain.ml index ca017111c1..e7117c9b62 100644 --- a/src/cdomains/floatDomain.ml +++ b/src/cdomains/floatDomain.ml @@ -101,7 +101,6 @@ module type FloatDomainBase = sig val maximal: t -> float option val is_exact : t -> bool - val is_interval : t -> bool end module FloatIntervalImpl(Float_t : CFloatType) = struct @@ -182,10 +181,6 @@ module FloatIntervalImpl(Float_t : CFloatType) = struct | MinusInfinity -> true | _ -> false - let is_interval = function - | Interval _ -> true - | _ -> false - let norm = function | Interval (low, high) as x -> if Float_t.is_finite low && Float_t.is_finite high then @@ -802,7 +797,6 @@ module type FloatDomain = sig val maximal: t -> float option val is_exact : t -> bool - val is_interval : t -> bool val get_fkind : t -> Cil.fkind val invariant: Cil.exp -> t -> Invariant.t end @@ -909,7 +903,6 @@ module FloatIntervalImplLifted = struct let minus_inf_of fkind = dispatch_fkind fkind (F1.minus_inf, F2.minus_inf) let is_inf = dispatch (F1.is_inf, F2.is_inf) let is_neg_inf = dispatch (F1.is_minus_inf, F2.is_minus_inf) - let is_interval = dispatch (F1.is_interval, F2.is_interval) let get_fkind = function | F32 _ -> FFloat @@ -1075,9 +1068,6 @@ module FloatDomTupleImpl = struct let is_exact = exists % mapp { fp= (fun (type a) (module F : FloatDomain with type t = a) -> F.is_exact); } - let is_interval = - for_all - % mapp { fp= (fun (type a) (module F : FloatDomain with type t = a) -> F.is_interval); } let is_top = for_all % mapp { fp= (fun (type a) (module F : FloatDomain with type t = a) -> F.is_top); } diff --git a/src/cdomains/floatDomain.mli b/src/cdomains/floatDomain.mli index 67ec9d17dd..4052f633a7 100644 --- a/src/cdomains/floatDomain.mli +++ b/src/cdomains/floatDomain.mli @@ -103,7 +103,6 @@ module type FloatDomainBase = sig val maximal: t -> float option val is_exact : t -> bool - val is_interval : t -> bool end (* Only exposed for testing *) @@ -139,7 +138,6 @@ module type FloatDomain = sig val maximal: t -> float option val is_exact : t -> bool - val is_interval : t -> bool val get_fkind : t -> Cil.fkind val invariant: Cil.exp -> t -> Invariant.t end diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 1d85c86070..80a9e05e09 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -279,7 +279,7 @@ struct | Any ActiveJumpBuf -> 46 | Any ValidLongJmp -> 47 | Any (MayBeModifiedSinceSetjmp _) -> 48 - | Any (TmpSpecial _) -> 42 + | Any (TmpSpecial _) -> 49 let rec compare a b = let r = Stdlib.compare (order a) (order b) in From 1fd2af385238642262df93129d83ddd76dc1281e Mon Sep 17 00:00:00 2001 From: FelixKrayer Date: Sat, 20 May 2023 12:18:21 +0200 Subject: [PATCH 0030/1312] derive math printable --- src/analyses/libraryDesc.ml | 138 +++++++----------------------------- 1 file changed, 27 insertions(+), 111 deletions(-) diff --git a/src/analyses/libraryDesc.ml b/src/analyses/libraryDesc.ml index d9afb20bee..903360a603 100644 --- a/src/analyses/libraryDesc.ml +++ b/src/analyses/libraryDesc.ml @@ -13,31 +13,31 @@ struct end type math = - | Nan of (Cil.fkind * Cil.exp) - | Inf of Cil.fkind - | Isfinite of Cil.exp - | Isinf of Cil.exp - | Isnan of Cil.exp - | Isnormal of Cil.exp - | Signbit of Cil.exp - | Isgreater of (Cil.exp * Cil.exp) - | Isgreaterequal of (Cil.exp * Cil.exp) - | Isless of (Cil.exp * Cil.exp) - | Islessequal of (Cil.exp * Cil.exp) - | Islessgreater of (Cil.exp * Cil.exp) - | Isunordered of (Cil.exp * Cil.exp) - | Ceil of (Cil.fkind * Cil.exp) - | Floor of (Cil.fkind * Cil.exp) - | Fabs of (Cil.fkind * Cil.exp) - | Fmax of (Cil.fkind * Cil.exp * Cil.exp) - | Fmin of (Cil.fkind * Cil.exp * Cil.exp) - | Acos of (Cil.fkind * Cil.exp) - | Asin of (Cil.fkind * Cil.exp) - | Atan of (Cil.fkind * Cil.exp) - | Atan2 of (Cil.fkind * Cil.exp * Cil.exp) - | Cos of (Cil.fkind * Cil.exp) - | Sin of (Cil.fkind * Cil.exp) - | Tan of (Cil.fkind * Cil.exp) + | Nan of (CilType.Fkind.t * Basetype.CilExp.t) + | Inf of CilType.Fkind.t + | Isfinite of Basetype.CilExp.t + | Isinf of Basetype.CilExp.t + | Isnan of Basetype.CilExp.t + | Isnormal of Basetype.CilExp.t + | Signbit of Basetype.CilExp.t + | Isgreater of (Basetype.CilExp.t * Basetype.CilExp.t) + | Isgreaterequal of (Basetype.CilExp.t * Basetype.CilExp.t) + | Isless of (Basetype.CilExp.t * Basetype.CilExp.t) + | Islessequal of (Basetype.CilExp.t * Basetype.CilExp.t) + | Islessgreater of (Basetype.CilExp.t * Basetype.CilExp.t) + | Isunordered of (Basetype.CilExp.t * Basetype.CilExp.t) + | Ceil of (CilType.Fkind.t * Basetype.CilExp.t) + | Floor of (CilType.Fkind.t * Basetype.CilExp.t) + | Fabs of (CilType.Fkind.t * Basetype.CilExp.t) + | Fmax of (CilType.Fkind.t * Basetype.CilExp.t * Basetype.CilExp.t) + | Fmin of (CilType.Fkind.t * Basetype.CilExp.t * Basetype.CilExp.t) + | Acos of (CilType.Fkind.t * Basetype.CilExp.t) + | Asin of (CilType.Fkind.t * Basetype.CilExp.t) + | Atan of (CilType.Fkind.t * Basetype.CilExp.t) + | Atan2 of (CilType.Fkind.t * Basetype.CilExp.t * Basetype.CilExp.t) + | Cos of (CilType.Fkind.t * Basetype.CilExp.t) + | Sin of (CilType.Fkind.t * Basetype.CilExp.t) + | Tan of (CilType.Fkind.t * Basetype.CilExp.t) [@@deriving eq, ord, hash] (** Type of special function, or {!Unknown}. *) (* Use inline record if not single {!Cil.exp} argument. *) @@ -146,96 +146,12 @@ let of_old ?(attrs: attr list=[]) (old_accesses: Accesses.old) (classify_name): module MathPrintable = struct include Printable.Std - type t = math + type t = math [@@deriving eq, ord, hash] let name () = "MathPrintable" let relift ml = ml - let order = function - | Nan _ -> 1 - | Inf _ -> 2 - | Isfinite _ -> 3 - | Isinf _ -> 4 - | Isnan _ -> 5 - | Isnormal _ -> 6 - | Signbit _ -> 7 - | Isgreater _ -> 8 - | Isgreaterequal _ -> 9 - | Isless _ -> 10 - | Islessequal _ -> 11 - | Islessgreater _ -> 12 - | Isunordered _ -> 13 - | Ceil _ -> 14 - | Floor _ -> 15 - | Fabs _ -> 16 - | Fmax _ -> 17 - | Fmin _ -> 18 - | Acos _ -> 19 - | Asin _ -> 20 - | Atan _ -> 21 - | Atan2 _ -> 22 - | Cos _ -> 23 - | Sin _ -> 24 - | Tan _ -> 25 - - let equal m1 m2 = (compare m1 m2) == 0 - let hash = order - - let cmp_fk_exp (fk1, e1) (fk2, e2) = - let r = (CilType.Fkind.compare fk1 fk2) in - if r <> 0 then - r - else - CilType.Exp.compare e1 e2 - - let cmp_exp_exp (e1, e1') (e2, e2') = - let r = (CilType.Exp.compare e1 e2) in - if r <> 0 then - r - else - CilType.Exp.compare e1' e2' - - let cmp_fk_exp_exp (fk1, e1, e1') (fk2, e2, e2') = - let r = (CilType.Fkind.compare fk1 fk2) in - if r <> 0 then - r - else - cmp_exp_exp (e1, e1') (e2, e2') - - let compare m1 m2 = - let r = Stdlib.compare (order m1) (order m2) in - if r <> 0 then - r - else - match m1, m2 with - | Nan fe1, Nan fe2 -> cmp_fk_exp fe1 fe2 - | Inf fk1, Inf fk2 -> CilType.Fkind.compare fk1 fk2 - | Isfinite e1, Isfinite e2 -> CilType.Exp.compare e1 e2 - | Isinf e1, Isinf e2 -> CilType.Exp.compare e1 e2 - | Isnan e1, Isnan e2 -> CilType.Exp.compare e1 e2 - | Isnormal e1, Isnormal e2 -> CilType.Exp.compare e1 e2 - | Signbit e1, Signbit e2 -> CilType.Exp.compare e1 e2 - | Isgreater ee1, Isgreater ee2 -> cmp_exp_exp ee1 ee2 - | Isgreaterequal ee1, Isgreaterequal ee2 -> cmp_exp_exp ee1 ee2 - | Isless ee1, Isless ee2 -> cmp_exp_exp ee1 ee2 - | Islessequal ee1, Islessequal ee2 -> cmp_exp_exp ee1 ee2 - | Islessgreater ee1, Islessgreater ee2 -> cmp_exp_exp ee1 ee2 - | Isunordered ee1, Isunordered ee2 -> cmp_exp_exp ee1 ee2 - | Ceil fe1, Ceil fe2 -> cmp_fk_exp fe1 fe2 - | Floor fe1, Floor fe2 -> cmp_fk_exp fe1 fe2 - | Fabs fe1, Fabs fe2 -> cmp_fk_exp fe1 fe2 - | Fmax fee1, Fmax fee2 -> cmp_fk_exp_exp fee1 fee2 - | Fmin fee1, Fmin fee2 -> cmp_fk_exp_exp fee1 fee2 - | Acos fe1, Acos fe2 -> cmp_fk_exp fe1 fe2 - | Asin fe1, Asin fe2 -> cmp_fk_exp fe1 fe2 - | Atan fe1, Atan fe2 -> cmp_fk_exp fe1 fe2 - | Atan2 fee1, Atan2 fee2 -> cmp_fk_exp_exp fee1 fee2 - | Cos fe1, Cos fe2 -> cmp_fk_exp fe1 fe2 - | Sin fe1, Sin fe2 -> cmp_fk_exp fe1 fe2 - | Tan fe1, Tan fe2 -> cmp_fk_exp fe1 fe2 - | _ -> failwith "impossible" - let show = function | Nan _ -> "nan" | Inf _ -> "inf" @@ -295,6 +211,6 @@ module MathPrintable = struct end module MathLifted = Lattice.Flat (MathPrintable) (struct - let top_name = "Unknown math desc" + let top_name = "Unknown or no math desc" let bot_name = "Nonexistent math desc" end) From 24702c65fb57bd4655b6dfa91c3b58a6d636b010 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Sat, 20 May 2023 15:48:57 +0200 Subject: [PATCH 0031/1312] Some stuff --- src/analyses/termination_new.ml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index 4d63995205..8480dbd747 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -1,8 +1,12 @@ (** Work in progress *) open Analyses +open GoblintCil +open TerminationPreprocessing -let terminates loop = () (* TODO *) +let terminates ctx loop exp = + match ctx.ask (EvalInt exp) with + _ -> () (* TODO *) module Spec : Analyses.MCPSpec = struct @@ -18,6 +22,9 @@ struct (** Provides some default implementations *) include Analyses.IdentitySpec + let branch ctx (exp:exp) (tv:bool) = + ctx.local (* TODO *) + let query ctx (type a) (q: a Queries.t): a Queries.result = let open Queries in Result.top q (* TODO *) @@ -25,5 +32,7 @@ struct end let _ = + (** Register the preprocessing *) + Cilfacade.register_preprocess (Spec.name ()) (new loopCounterVisitor); (** Register this analysis within the master control program *) MCP.register_analysis (module Spec : MCPSpec) From 917397f764b3ed58b15b9f235d6c82c7da683a10 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Sat, 20 May 2023 15:50:23 +0200 Subject: [PATCH 0032/1312] Refactor --- src/analyses/termination_new.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index 8480dbd747..e461492724 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -31,7 +31,7 @@ struct end -let _ = +let () = (** Register the preprocessing *) Cilfacade.register_preprocess (Spec.name ()) (new loopCounterVisitor); (** Register this analysis within the master control program *) From 4d891f28d13ce09ad023c63a21a14d8dfd3f7038 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Tue, 23 May 2023 10:32:58 +0200 Subject: [PATCH 0033/1312] testing why the variable is not bound from the analysis --- runningGob.sh | 9 ++++- src/util/terminationPreprocessing.ml | 56 +++++++++++++++++++++------- 2 files changed, 50 insertions(+), 15 deletions(-) diff --git a/runningGob.sh b/runningGob.sh index 8547d435b0..bb17a96384 100755 --- a/runningGob.sh +++ b/runningGob.sh @@ -1,5 +1,10 @@ #!/bin/bash make -make install -./goblint --enable dbg.debug tests/regression/55-loop-unrolling/01-simple-cases.c --set "ana.activated[+]" signs --enable ana.int.interval --enable justcil > output.txt +#make install +options="--set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra" +cfile="tests/regression/55-loop-unrolling/01-simple-cases.c" +#./goblint $cfile $options --enable justcil > output.txt +./goblint $cfile $options --html +python3 -m http.server --directory result 8081 +#./goblint --enable dbg.debug tests/regression/55-loop-unrolling/01-simple-cases.c --enable ana.int.interval --set "ana.activated[+]" signs --enable justcil > output.txt diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index dbc691a616..51ef9ba00c 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -6,6 +6,7 @@ *) open GoblintCil +open Printf let extract_file_name s = (*There still may be a need to filter more chars*) let ls = String.split_on_char '/' s in (*Assuming '/' as path seperator*) @@ -18,13 +19,37 @@ let extract_file_name s = (*There still may be a need to filt s' let show_location_id l = - extract_file_name l.file ^ "_" ^ string_of_int l.line ^ "_" ^ string_of_int l.column + string_of_int l.line ^ "_" ^ string_of_int l.column (*extract_file_name l.file ^ "_" ^ *) +class loopCounterVisitor (fd : fundec) = object(self) + inherit nopCilVisitor + method! vstmt s = + let action s = match s.skind with + | Loop (b, loc, eloc, _, _) -> + let name = "term"^show_location_id loc in + let typ = intType in + let v = Goblintutil.create_var (makeLocalVar fd name typ) in + let init_stmt = mkStmtOneInstr @@ Set (var v, zero, loc, eloc) in + let inc_stmt = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in + (match b.bstmts with + | cont :: cond :: ss -> + b.bstmts <- cont :: inc_stmt :: cond :: ss; (*cont :: cond :: inc_stmt :: ss = it is also possible, but for loops with cond at the end, inc is also at the end*) + | _ -> ()); + let nb1 = mkBlock [mkStmt s.skind] in + s.skind <- Block nb1; + let nb = mkBlock [init_stmt; mkStmt s.skind] in + s.skind <- Block nb; + printf "variables are inserted\n"; + s + | _ -> s + in ChangeDoChildrenPost (s, action); + end +(* just a test class loopCounterVisitor (fd : fundec) = object(self) inherit nopCilVisitor method! vstmt s = - let action s = match s.skind with + match s.skind with | Loop (b, loc, eloc, _, _) -> let name = "term"^show_location_id loc in let typ = intType in @@ -32,23 +57,28 @@ method! vstmt s = let init_stmt = mkStmtOneInstr @@ Set (var v, zero, loc, eloc) in let inc_stmt = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in b.bstmts <- inc_stmt :: b.bstmts; - let nb = mkBlock [init_stmt; mkStmt s.skind] in - s.skind <- Block nb; - s - | _ -> s - in ChangeDoChildrenPost (s, action) -end - -(*let action (fd : fundec) s = + let nb = mkBlock [init_stmt; mkStmt s.skind] in (* init_stmt; *) + ChangeDoChildrenPost (s, (fun _ -> s.skind <- Block(nb); s)) + | _ -> DoChildren +end + +let add_var_loopTerm fd f = + let thisVisitor = new loopCounterVisitor in + visitCilFileSameGlobals (thisVisitor fd ) f*) +(* +let action (fd : fundec) s = let a s = match s.skind with | Loop (b, loc, eloc, _, _) -> let name = "term"^show_location_id loc in let typ = intType in let v = Goblintutil.create_var (makeLocalVar fd name ~init:(SingleInit zero) typ) in + let init_stmt = mkStmtOneInstr @@ Set (var v, zero, loc, eloc) in let inc_stmt = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in b.bstmts <- inc_stmt :: b.bstmts; - let nb = mkBlock [mkStmt s.skind] in + let nb = mkBlock [init_stmt; mkStmt s.skind] in (* *) s.skind <- Block nb; s - | _ -> s -in ChangeDoChildrenPost (s, a)*) + | _ -> s +in ChangeDoChildrenPost (s, a) +*) + From 2a53cbbd38c921cdde0c55aaabd4bdc2351821d3 Mon Sep 17 00:00:00 2001 From: Nathan Schmidt Date: Tue, 23 May 2023 12:59:24 +0200 Subject: [PATCH 0034/1312] Very first (incomplete) draft for Must Null Byte Domain --- src/cdomains/arrayDomain.ml | 270 ++++++++++++++++++++++++++++++++++- src/cdomains/arrayDomain.mli | 39 ++++- src/util/options.schema.json | 2 +- 3 files changed, 305 insertions(+), 6 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 982cd94058..c685099e8d 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -8,7 +8,7 @@ module A = Array module BI = IntOps.BigIntOps module VDQ = ValueDomainQueries -type domain = TrivialDomain | PartitionedDomain | UnrolledDomain +type domain = TrivialDomain | PartitionedDomain | UnrolledDomain | MustNullByteDomain (* determines the domain based on variable, type and flag *) let get_domain ~varAttr ~typAttr = @@ -16,6 +16,7 @@ let get_domain ~varAttr ~typAttr = | "partitioned" -> PartitionedDomain | "trivial" -> TrivialDomain | "unroll" -> UnrolledDomain + | "mustnullbyte" -> MustNullByteDomain | _ -> failwith "AttributeConfiguredArrayDomain: invalid option for domain" in (*TODO add options?*) @@ -60,6 +61,14 @@ sig val smart_widen: (exp -> BI.t option) -> (exp -> BI.t option) -> t -> t -> t val smart_leq: (exp -> BI.t option) -> (exp -> BI.t option) -> t -> t -> bool val update_length: idx -> t -> t + + val to_string: t -> t + val to_n_string: t -> int -> bool -> t + val to_string_length: t -> idx + val string_concat: t -> t -> int option -> t + val substring_extraction: t -> t -> t option + val string_comparison: t -> t -> int option -> idx + val project: ?varAttr:attributes -> ?typAttr:attributes -> VDQ.t -> t -> t end @@ -99,6 +108,14 @@ struct let smart_widen _ _ = widen let smart_leq _ _ = leq let update_length _ x = x + + let to_string _ = top () + let to_n_string _ _ _ = top () + let to_string_length _ = Idx.top () + let string_concat _ _ _ = top () + let substring_extraction _ _ = Some (top ()) + let string_comparison _ _ _ = Idx.top () + let project ?(varAttr=[]) ?(typAttr=[]) _ t = t end @@ -187,6 +204,12 @@ struct let smart_widen _ _ = widen let smart_leq _ _ = leq let update_length _ x = x + let to_string _ = top () + let to_n_string _ _ _ = top () + let to_string_length _ = Idx.top_of !Cil.kindOfSizeOf + let string_concat _ _ _ = top () + let substring_extraction _ _ = Some (top ()) + let string_comparison _ _ _ = Idx.top_of IInt let project ?(varAttr=[]) ?(typAttr=[]) _ t = t end @@ -699,7 +722,202 @@ struct (* arrays can not be partitioned according to multiple expressions, arbitrary prefer the first one here *) x + let to_string _ = top () + let to_n_string _ _ _ = top () + let to_string_length _ = Idx.top_of !Cil.kindOfSizeOf + let string_concat _ _ _ = top () + let substring_extraction _ _ = Some (top ()) + let string_comparison _ _ _ = Idx.top_of IInt + + let update_length _ x = x + let project ?(varAttr=[]) ?(typAttr=[]) _ t = t +end + +module MustNullByte (Val: Lattice.S) (Idx: IntDomain.Z): S with type value = Val.t option and type idx = Idx.t = +struct + include SetDomain.Reverse (SetDomain.Make (Idx)) + let name () = "arrays containing null bytes" + type idx = Idx.t + type value = Val.t option (* None = null byte *) + + let domain_of_t _ = MustNullByteDomain + + let get ?(checkBounds=true) (ask: VDQ.t) index_set (_, i) = + let rec check_indexes i max = + if Z.gt i max then + true + else if exists (fun x -> match Idx.to_int x with Some num -> Z.equal i num | None -> false) index_set then + check_indexes (Z.add i Z.one) max + else + false in + let min_i = match Idx.minimal i with + | Some min -> min + | None -> Z.zero in (* assume worst case minimal index *) + let max_i = Idx.maximal i in + match max_i with + (* if there is no maximum number in interval, return top of value *) + | None -> Some (Val.top ()) + | Some max -> + (* else only return null if all numbers in interval are in index set *) + if check_indexes min_i max then + None + else + Some (Val.top ()) + + let set (ask: VDQ.t) index_set (_, i) v = + let min_i = match Idx.minimal i with + | Some min -> min + | None -> Z.zero in (* assume worst case minimal index *) + let max_i = Idx.maximal i in + match max_i, v with + (* if there is no maxinum number in interval and value = null, return index set unchanged *) + | None, None -> index_set + (* if there is no maximum number in interval and value != null, return top = empty set *) + | None, Some _ -> top () + | Some max, None -> + (* if i is an exact number and value = null, add i to index set *) + if Z.equal min_i max then + add (Idx.of_int !Cil.kindOfSizeOf min_i) index_set + (* if i is an interval and value = null, return index set unchanged *) + else + index_set + | Some max, Some _ -> + (* if i is an exact number and value != null, remove i from index set *) + if Z.equal min_i max then + remove (Idx.of_int !Cil.kindOfSizeOf min_i) index_set + (* if i is an interval and value != null, return top = empty set *) + else + top () + + let make ?(varAttr=[]) ?(typAttr=[]) i v = + (* TODO: for now naive addition of all indexes in interval one by one -- yup, that's very inefficient *) + let rec add_indexes index_set i max = + if Z.gt i max then + index_set + else + add_indexes (add (Idx.of_int !Cil.kindOfSizeOf i) index_set) (Z.add i Z.one) max in + match Idx.minimal i, Idx.maximal i, v with + (* if there is no minimal number in interval or value != null, return top *) + | None, _, _ + | Some _, _, Some _ -> top () + (* if value = null, return bot (i.e. set of all indexes from 0 to min) *) + | Some min, _, None -> add_indexes (empty ()) Z.zero min + + let length _ = None + + let move_if_affected ?(replace_with_const=false) _ index_set _ _ = index_set + + let get_vars_in_e _ = [] + + let map f index_set = + (* if f(null) = null, all values at indexes in set are still surely null *) + if f None = None then + index_set + (* else return top as checking the effect of f for every possible value is unfeasible *) + else + top () + + (* TODO: check if there is no smarter implementation of this (probably not) *) + let fold_left f a _ = f a (Some (Val.top ())) + + let smart_join _ _ = join + let smart_widen _ _ = widen + let smart_leq _ _ = leq + + (* string functions *) + let to_string index_set = + (* if index set is empty, the array doesn't surely contain a null byte and an overflow might happen *) + if is_empty index_set then + (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "May access array past end: potential buffer overflow"; + index_set) + (* else only keep the smallest index in the set *) + else + (* TODO: would min_elt work? (probably not) *) + let min_null = fold (fun x acc -> Idx.lt x acc) index_set (Idx.bot_of !Cil.kindOfSizeOf) in + singleton min_null + + let to_n_string index_set n no_null_warn = + (* TODO: for now naive addition of all indexes in interval one by one -- yup, that's very inefficient *) + let rec add_indexes index_set i max = + if Z.geq i max then + index_set + else + add_indexes (add (Idx.of_int !Cil.kindOfSizeOf i) index_set) (Z.add i Z.one) max in + (* if index set is empty, the array doesn't surely contain a null byte and an overflow might happen *) + if is_empty index_set then + (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "May access array past end: potential buffer overflow"; + index_set) + (* else if index set not empty *) + else + (* TODO: would min_elt work? (probably not) *) + let min_null = fold (fun x acc -> Idx.lt x acc) index_set (Idx.bot_of !Cil.kindOfSizeOf) in + match Idx.to_int min_null with + | Some i -> + (* ... keep smallest index in set if smaller than n and add as many null bytes as necessary to obtain n bytes string *) + if Z.lt i (Z.of_int n) then + add_indexes (singleton min_null) i (Z.of_int n) + (* ... or if smallest index >= n, return empty set and warn if no_null_warn = true *) + else if no_null_warn then + (M.warn "Resulting string may not contain a terminating null byte"; + empty ()) + else + empty () + | None -> singleton min_null (* should not happen, but if it does, can't compute additional must null bytes *) + + let to_string_length index_set = + (* if index set is empty, return top as array may contain null bytes we don't know of *) + (* TODO: warning not useful I believe? ((In theory, one could use strlen to determine if there is a null byte in array or not to + * know if bytes of the array are possibly overwriten in a malicious undertaking)) *) + if is_empty index_set then + Idx.top_of !Cil.kindOfSizeOf + else + (* TODO: would min_elt work? (probably not) *) + let min_null = fold (fun x acc -> Idx.lt x acc) index_set (Idx.bot_of !Cil.kindOfSizeOf) in + match Idx.to_int min_null with + (* else if we can determine the minimal index in set, we know 0 <= length <= minimal index *) + | Some i -> Idx.of_interval !Cil.kindOfSizeOf (Z.zero, i) + | None -> Idx.top_of !Cil.kindOfSizeOf + + let string_concat index_set1 index_set2 n = + let s1 = to_string index_set1 in + (* if s1 is empty, no statement possible for must null bytes of concatenation; warning generated by to_string above *) + if is_empty s1 then + empty () + else + begin match n with + (* concat at most n bytes of index_set2 to index_set1 = strncat *) + | Some num -> + let s1_i = choose s1 in + let s2 = to_n_string index_set2 num false in + (* if no must null byte among first n bytes of s2, no statement possible as no knowledge of may null bytes *) + if is_empty s2 then + empty() + (* else concatenation has null byte at strlen(s1) + first null byte found in s2 *) + else + (* TODO: would min_elt work? (probably not) *) + let min_null_s2 = fold (fun x acc -> Idx.lt x acc) s2 (Idx.bot_of !Cil.kindOfSizeOf) in + singleton (Idx.add s1_i min_null_s2) + (* concat bytes of index_set2 to index_set1 until a null byte is reached = strcat *) + | None -> + let s2 = to_string index_set2 in + (* if s2 is empty, no statement possible for must null bytes of concatenation; warning generated by to_string above *) + if is_empty s2 then + empty () + (* else concatenation has null byte at strlen(s1) + strlen(s2) *) + else + let s1_i = choose s1 in + let s2_i = choose s2 in + singleton (Idx.add s1_i s2_i) + end + + (* TODO -- can I even do something useful at all? Might as well leave out substring_extraction and string_comparison *) + let substring_extraction _ _ = Some (top ()) + + (* TODO *) + let string_comparison _ _ _ = Idx.top_of IInt + let update_length _ x = x + let project ?(varAttr=[]) ?(typAttr=[]) _ t = t end @@ -749,6 +967,26 @@ struct let smart_widen _ _ = widen let smart_leq _ _ = leq + let to_string _ = top () + let to_n_string a n _ = + begin match length a with + | Some len -> + begin match Idx.maximal len with + | Some max -> + if Z.gt (Z.of_int n) max then + (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "May produce a buffer overflow if the string doesn't contain a null byte in the first n bytes"; + top ()) + else + top () + | None -> top () + end + | None -> top () + end + let to_string_length _ = Idx.top_of !Cil.kindOfSizeOf + let string_concat _ _ _ = top () + let substring_extraction _ _ = Some (top ()) + let string_comparison _ _ _ = Idx.top_of IInt + (* It is not necessary to do a least-upper bound between the old and the new length here. *) (* Any array can only be declared in one location. The value for newl that we get there is *) (* the one obtained by abstractly evaluating the size expression at this location for the *) @@ -801,6 +1039,13 @@ struct let l = Idx.join xl yl in Idx.leq xl yl && Base.smart_leq_with_length (Some l) x_eval_int y_eval_int x y + let to_string _ = top () + let to_n_string _ _ _ = top () + let to_string_length _ = Idx.top_of !Cil.kindOfSizeOf + let string_concat _ _ _ = top () + let substring_extraction _ _ = Some (top ()) + let string_comparison _ _ _ = Idx.top_of IInt + (* It is not necessary to do a least-upper bound between the old and the new length here. *) (* Any array can only be declared in one location. The value for newl that we get there is *) (* the one obtained by abstractly evaluating the size expression at this location for the *) @@ -822,8 +1067,12 @@ struct module Base = Unroll (Val) (Idx) include Lattice.Prod (Base) (Idx) type idx = Idx.t - type value = Val.t - + type value = Val.t let to_string _ = top () + let to_n_string _ _ _ = top () + let to_string_length _ = Idx.top_of !Cil.kindOfSizeOf + let string_concat _ _ _ = top () + let substring_extraction _ _ = Some (top ()) + let string_comparison _ _ _ = Idx.top_of IInt let domain_of_t _ = UnrolledDomain let get ?(checkBounds=true) (ask : VDQ.t) (x, (l : idx)) (e, v) = @@ -842,6 +1091,13 @@ struct let smart_widen _ _ = widen let smart_leq _ _ = leq + let to_string _ = top () + let to_n_string _ _ _ = top () + let to_string_length _ = Idx.top_of !Cil.kindOfSizeOf + let string_concat _ _ _ = top () + let substring_extraction _ _ = Some (top ()) + let string_comparison _ _ _ = Idx.top_of IInt + (* It is not necessary to do a least-upper bound between the old and the new length here. *) (* Any array can only be declared in one location. The value for newl that we get there is *) (* the one obtained by abstractly evaluating the size expression at this location for the *) @@ -960,6 +1216,14 @@ struct let set_i u (i,v) = U.set ask u (index_as_expression i) v in set_i (List.fold_left set_i u unrolledValues) (factor (), rest) + (* TODO! *) + let to_string _ = top () + let to_n_string _ _ _ = top () + let to_string_length _ = Idx.top_of !Cil.kindOfSizeOf + let string_concat _ _ _ = top () + let substring_extraction _ _ = Some (top ()) + let string_comparison _ _ _ = Idx.top_of IInt + let project ?(varAttr=[]) ?(typAttr=[]) ask (t:t) = match get_domain ~varAttr ~typAttr, t with | PartitionedDomain, (Some x, None) -> to_t @@ (Some x, None, None) diff --git a/src/cdomains/arrayDomain.mli b/src/cdomains/arrayDomain.mli index 8386deb541..0df132a8e2 100644 --- a/src/cdomains/arrayDomain.mli +++ b/src/cdomains/arrayDomain.mli @@ -2,7 +2,7 @@ open IntOps open GoblintCil module VDQ = ValueDomainQueries -type domain = TrivialDomain | PartitionedDomain | UnrolledDomain +type domain = TrivialDomain | PartitionedDomain | UnrolledDomain | MustNullByteDomain val get_domain: varAttr:Cil.attributes -> typAttr:Cil.attributes -> domain (** gets the underlying domain: chosen by the attributes in AttributeConfiguredArrayDomain *) @@ -56,6 +56,32 @@ sig val smart_leq: (Cil.exp -> BigIntOps.t option) -> (Cil.exp -> BigIntOps.t option) -> t -> t -> bool val update_length: idx -> t -> t + val to_string: t -> t + (** Returns an abstract value with at most one null byte marking the end of the string *) + + val to_n_string: t -> int -> bool -> t + (** [to_n_string index_set n no_null_warn] returns an abstract value with a potential null + * byte marking the end of the string and if needed followed by further null bytes to obtain + * an n bytes string. If the resulting value doesn't surely contain a terminating null_byte, + * issue a warning if [no_null_warn] is true. *) + + val to_string_length: t -> idx + (** Returns length of string represented by input abstract value *) + + val string_concat: t -> t -> int option -> t + (** [string_concat s1 s2 n] returns a new abstract value representing the string + * concatenation of the input abstract values [s1] and [s2], taking at most [n] bytes of + * [s2] if present *) + + val substring_extraction: t -> t -> t option + (** [substring_extraction haystack needle] returns None if the string represented by the + * abstract value [needle] surely isn't a substring of [haystack], else Some (top) *) + + val string_comparison: t -> t -> int option -> idx + (** [string_comparison s1 s2 n] returns a negative / positive idx element if the string + * represented by [s1] is less / greater than the one by [s2] or zero if they are equal; + * only compares the first [n] bytes if present *) + val project: ?varAttr:Cil.attributes -> ?typAttr:Cil.attributes -> VDQ.t -> t -> t end @@ -84,8 +110,17 @@ module Partitioned (Val: LatticeWithSmartOps) (Idx: IntDomain.Z): S with type va * have a signature that allows for choosing an array representation at runtime. *) +module MustNullByte (Val: Lattice.S) (Idx: IntDomain.Z): S with type value = Val.t and type idx = Idx.t +(** This functor creates an array representation by the indexes of all null bytes + * the array *surely* contains. This is useful to analyze strings, i.e. null- + * terminated char arrays, and particularly to determine if operations on strings + * could lead to a buffer overflow. Concrete values from Val are not interesting + * for this domain. +*) + module PartitionedWithLength (Val: LatticeWithSmartOps) (Idx:IntDomain.Z): S with type value = Val.t and type idx = Idx.t (** Like partitioned but additionally manages the length of the array. *) module AttributeConfiguredArrayDomain(Val: LatticeWithSmartOps) (Idx:IntDomain.Z):S with type value = Val.t and type idx = Idx.t -(** Switches between PartitionedWithLength, TrivialWithLength and Unroll based on variable, type, and flag. *) +(** Switches between PartitionedWithLength, TrivialWithLength and Unroll based on variable, type, and flag. + * Always runs MustNullByte in parallel. *) diff --git a/src/util/options.schema.json b/src/util/options.schema.json index 2ff2e8bf58..7933b553ac 100644 --- a/src/util/options.schema.json +++ b/src/util/options.schema.json @@ -677,7 +677,7 @@ "description": "The domain that should be used for arrays. When employing the partition array domain, make sure to enable the expRelation analysis as well. When employing the unrolling array domain, make sure to set the ana.base.arrays.unrolling-factor >0.", "type": "string", - "enum": ["trivial", "partitioned", "unroll"], + "enum": ["trivial", "partitioned", "unroll", "mustnullbyte"], "default": "trivial" }, "unrolling-factor": { From a07e69a6a86e05d214407488c3cc14ea83561088 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Tue, 23 May 2023 19:27:31 +0200 Subject: [PATCH 0035/1312] restored the sign analysis, bounding of newly created variable does work now, a new preprocessing was implemented therefore --- src/analyses/apron/apronAnalysis.apron.ml | 7 +- src/analyses/tutorials/signs.ml | 30 +------- src/analyses/tutorials/signsOrig.ml | 92 ----------------------- src/util/cilCfg.ml | 8 +- src/util/cilfacade.ml | 11 +++ src/util/terminationPreprocessing.ml | 8 +- 6 files changed, 28 insertions(+), 128 deletions(-) delete mode 100644 src/analyses/tutorials/signsOrig.ml diff --git a/src/analyses/apron/apronAnalysis.apron.ml b/src/analyses/apron/apronAnalysis.apron.ml index f3a2374bc1..cdcbee70a0 100644 --- a/src/analyses/apron/apronAnalysis.apron.ml +++ b/src/analyses/apron/apronAnalysis.apron.ml @@ -1,6 +1,7 @@ (** Analysis using Apron for integer variables. *) open Analyses - +open TerminationPreprocessing +open Cilfacade include RelationAnalysis let spec_module: (module MCPSpec) Lazy.t = @@ -33,9 +34,11 @@ let get_spec (): (module MCPSpec) = let after_config () = let module Spec = (val get_spec ()) in MCP.register_analysis (module Spec : MCPSpec); - GobConfig.set_string "ana.path_sens[+]" (Spec.name ()) + GobConfig.set_string "ana.path_sens[+]" (Spec.name ()) + let _ = + Cilfacade.register_preprocess_cil ("apron") (new loopCounterVisitor); AfterConfig.register after_config diff --git a/src/analyses/tutorials/signs.ml b/src/analyses/tutorials/signs.ml index d039285dc2..02eb482769 100644 --- a/src/analyses/tutorials/signs.ml +++ b/src/analyses/tutorials/signs.ml @@ -2,27 +2,6 @@ open GoblintCil open Analyses -open TerminationPreprocessing - -(*let show_location_id l = - string_of_int l.line ^ "_" ^ string_of_int l.column - -class loopCounterVisitor (fd : fundec) = object(self) - inherit nopCilVisitor - method! vstmt s = - let action s = match s.skind with - | Loop (b, loc, eloc, _, _) -> - let name = "term"^show_location_id loc in - let typ = intType in - let v = Goblintutil.create_var (makeLocalVar fd name ~init:(SingleInit zero) typ) in - let inc_stmt = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in - b.bstmts <- inc_stmt :: b.bstmts; - let nb = mkBlock [mkStmt s.skind] in - s.skind <- Block nb; - s - | _ -> s - in ChangeDoChildrenPost (s, action) -end*) module Signs = struct @@ -42,12 +21,12 @@ struct (* TODO: An attempt to abstract integers, but it's just a little wrong... *) let of_int i = - if Z.compare i Z.zero < 0 then Zero - else if Z.compare i Z.zero > 0 then Zero + if Z.compare i Z.zero < 0 then Neg + else if Z.compare i Z.zero > 0 then Pos else Zero let lt x y = match x, y with - | Neg, Pos | Neg, Zero -> true (* TODO: Maybe something missing? *) + | Neg, Pos | Neg, Zero | Pos, Zero -> true (* TODO: Maybe something missing? *) | _ -> false end @@ -78,7 +57,7 @@ struct (* This should now evaluate expressions. *) let eval (d: D.t) (exp: exp): SL.t = match exp with - | Const (CInt (i, _, _)) -> SL.top () (* TODO: Fix me! *) + | Const (CInt (i, _, _)) -> SL.of_int i (* TODO: Fix me! *) | Lval (Var x, NoOffset) -> D.find x d | _ -> SL.top () @@ -110,5 +89,4 @@ struct end let _ = - Cilfacade.register_preprocess (Spec.name ()) (new loopCounterVisitor); MCP.register_analysis (module Spec : MCPSpec) diff --git a/src/analyses/tutorials/signsOrig.ml b/src/analyses/tutorials/signsOrig.ml deleted file mode 100644 index 96ba5c1a3a..0000000000 --- a/src/analyses/tutorials/signsOrig.ml +++ /dev/null @@ -1,92 +0,0 @@ -(** An analysis specification for didactic purposes. *) -(** -open Prelude.Ana -open Analyses - -module Signs = -struct - include Printable.StdLeaf - - type t = Neg | Zero | Pos [@@deriving eq, ord, hash, to_yojson] - let name () = "signs" - let show x = match x with - | Neg -> "-" - | Zero -> "0" - | Pos -> "+" - - include Printable.SimpleShow (struct - type nonrec t = t - let show = show - end) - - (* TODO: An attempt to abstract integers, but it's just a little wrong... *) - let of_int i = - if Z.compare i Z.zero < 0 then Zero - else if Z.compare i Z.zero > 0 then Zero - else Zero - - let lt x y = match x, y with - | Neg, Pos | Neg, Zero -> true (* TODO: Maybe something missing? *) - | _ -> false -end - -(* Now we turn this into a lattice by adding Top and Bottom elements. - * We then lift the above operations to the lattice. *) -module SL = -struct - include Lattice.Flat (Signs) (Printable.DefaultNames) - let of_int i = `Lifted (Signs.of_int i) - - let lt x y = match x, y with - | `Lifted x, `Lifted y -> Signs.lt x y - | _ -> false -end - -module Spec : Analyses.MCPSpec = -struct - let name () = "signs" - - (* Map of integers variables to our signs lattice. *) - module D = MapDomain.MapBot (Basetype.Variables) (SL) - module C = D - - let startstate v = D.bot () - let exitstate = startstate - - include Analyses.IdentitySpec - - (* This should now evaluate expressions. *) - let eval (d: D.t) (exp: exp): SL.t = match exp with - | Const (CInt (i, _, _)) -> SL.top () (* TODO: Fix me! *) - | Lval (Var x, NoOffset) -> D.find x d - | _ -> SL.top () - - - (* Transfer functions: we only implement assignments here. - * You can leave this code alone... *) - let assign ctx (lval:lval) (rval:exp) : D.t = - let d = ctx.local in - match lval with - | (Var x, NoOffset) when not x.vaddrof -> D.add x (eval d rval) d - | _ -> D.top () - - - (* Here we return true if we are absolutely certain that an assertion holds! *) - let assert_holds (d: D.t) (e:exp) = match e with - | BinOp (Lt, e1, e2, _) -> SL.lt (eval d e1) (eval d e2) - | _ -> false - - (* We should now provide this information to Goblint. Assertions are integer expressions, - * so we implement here a response to EvalInt queries. - * You should definitely leave this alone... *) - let query ctx (type a) (q: a Queries.t): a Queries.result = - let open Queries in - match q with - | EvalInt e when assert_holds ctx.local e -> - let ik = Cilfacade.get_ikind_exp e in - ID.of_bool ik true - | _ -> Result.top q -end - -let _ = - MCP.register_analysis (module Spec : MCPSpec)*) diff --git a/src/util/cilCfg.ml b/src/util/cilCfg.ml index 84b4797c53..45f12ef185 100644 --- a/src/util/cilCfg.ml +++ b/src/util/cilCfg.ml @@ -40,6 +40,8 @@ let loopCount file = let createCFG (fileAST: file) = + + Cilfacade.do_preprocess_cil fileAST; (* The analyzer keeps values only for blocks. So if you want a value for every program point, each instruction *) (* needs to be in its own block. end_basic_blocks does that. *) (* After adding support for VLAs, there are new VarDecl instructions at the point where a variable was declared and *) @@ -54,7 +56,7 @@ let createCFG (fileAST: file) = * See https://github.com/goblint/cil/issues/31#issuecomment-824939793. *) let loops = loopCount fileAST in - + iterGlobals fileAST (fun glob -> match glob with | GFun(fd,_) -> @@ -64,6 +66,6 @@ let createCFG (fileAST: file) = computeCFGInfo fd true | _ -> () ); - if get_bool "dbg.run_cil_check" then assert (Check.checkFile [] fileAST); - + + if get_bool "dbg.run_cil_check" then assert (Check.checkFile [] fileAST); Cilfacade.do_preprocess fileAST diff --git a/src/util/cilfacade.ml b/src/util/cilfacade.ml index 50906ae503..3bdeb48824 100644 --- a/src/util/cilfacade.ml +++ b/src/util/cilfacade.ml @@ -73,6 +73,17 @@ let do_preprocess ast = in iterGlobals ast (function GFun (fd,_) -> List.iter (f fd) !visitors | _ -> ()) +let visitors_cil = ref [] +let register_preprocess_cil name visitor_fun = + visitors_cil := !visitors_cil @ [name, visitor_fun] + +let do_preprocess_cil ast = + let f fd (name, visitor_fun) = + (* this has to be done here, since the settings aren't available when register_preprocess is called *) + if List.mem name (get_string_list "ana.activated") then + ignore @@ visitCilFunction (visitor_fun fd) fd + in + iterGlobals ast (function GFun (fd,_) -> List.iter (f fd) !visitors_cil | _ -> ()) (** @raise GoblintCil.FrontC.ParseError @raise GoblintCil.Errormsg.Error *) diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index 51ef9ba00c..e8e350997f 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -27,19 +27,17 @@ class loopCounterVisitor (fd : fundec) = object(self) let action s = match s.skind with | Loop (b, loc, eloc, _, _) -> let name = "term"^show_location_id loc in - let typ = intType in - let v = Goblintutil.create_var (makeLocalVar fd name typ) in + let typ = Cil.intType in + let v = (Cil.makeLocalVar fd name typ) in + (*let init_stmt = mkStmt (Instr [Set (var v, zero, loc, eloc)]) in*) let init_stmt = mkStmtOneInstr @@ Set (var v, zero, loc, eloc) in let inc_stmt = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in (match b.bstmts with | cont :: cond :: ss -> b.bstmts <- cont :: inc_stmt :: cond :: ss; (*cont :: cond :: inc_stmt :: ss = it is also possible, but for loops with cond at the end, inc is also at the end*) | _ -> ()); - let nb1 = mkBlock [mkStmt s.skind] in - s.skind <- Block nb1; let nb = mkBlock [init_stmt; mkStmt s.skind] in s.skind <- Block nb; - printf "variables are inserted\n"; s | _ -> s in ChangeDoChildrenPost (s, action); From b8fd5054d27cecf431e498ab05c2a2dccfe58df4 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Tue, 23 May 2023 19:31:35 +0200 Subject: [PATCH 0036/1312] added comment to explain the newly created preprocess --- src/util/cilfacade.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util/cilfacade.ml b/src/util/cilfacade.ml index 3bdeb48824..fd298e01a0 100644 --- a/src/util/cilfacade.ml +++ b/src/util/cilfacade.ml @@ -74,6 +74,7 @@ let do_preprocess ast = iterGlobals ast (function GFun (fd,_) -> List.iter (f fd) !visitors | _ -> ()) let visitors_cil = ref [] +(* does exactly the same as register_preprocess_cil but it is executed earlier, before the CFG is created*) let register_preprocess_cil name visitor_fun = visitors_cil := !visitors_cil @ [name, visitor_fun] From 03d621bcd3c36ad1bb14e0e8cffcdd444e7f3bae Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Tue, 23 May 2023 19:49:58 +0200 Subject: [PATCH 0037/1312] small changes, updated my libraries with the hope now all tests pass on git --- runningGob.sh | 2 +- src/util/cilCfg.ml | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/runningGob.sh b/runningGob.sh index bb17a96384..b67bc36d9a 100755 --- a/runningGob.sh +++ b/runningGob.sh @@ -3,7 +3,7 @@ make #make install options="--set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra" cfile="tests/regression/55-loop-unrolling/01-simple-cases.c" -#./goblint $cfile $options --enable justcil > output.txt +./goblint $cfile $options --enable justcil > output.txt ./goblint $cfile $options --html python3 -m http.server --directory result 8081 #./goblint --enable dbg.debug tests/regression/55-loop-unrolling/01-simple-cases.c --enable ana.int.interval --set "ana.activated[+]" signs --enable justcil > output.txt diff --git a/src/util/cilCfg.ml b/src/util/cilCfg.ml index 45f12ef185..90d6be14ae 100644 --- a/src/util/cilCfg.ml +++ b/src/util/cilCfg.ml @@ -41,7 +41,6 @@ let loopCount file = let createCFG (fileAST: file) = - Cilfacade.do_preprocess_cil fileAST; (* The analyzer keeps values only for blocks. So if you want a value for every program point, each instruction *) (* needs to be in its own block. end_basic_blocks does that. *) (* After adding support for VLAs, there are new VarDecl instructions at the point where a variable was declared and *) @@ -49,6 +48,8 @@ let createCFG (fileAST: file) = (* BB causes the output CIL file to no longer compile. *) (* Since we want the output of justcil to compile, we do not run allBB visitor if justcil is enable, regardless of *) (* exp.basic-blocks. This does not matter, as we will not run any analysis anyway, when justcil is enabled. *) + + Cilfacade.do_preprocess_cil fileAST; if not (get_bool "exp.basic-blocks") && not (get_bool "justcil") then end_basic_blocks fileAST; (* We used to renumber vids but CIL already generates them fresh, so no need. From 878a1858afa7b7724243b23394eda3840b80d600 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Tue, 23 May 2023 20:11:57 +0200 Subject: [PATCH 0038/1312] changed var name of newly created var to a name, which is not a valid C name -> the variable must be unique --- src/util/terminationPreprocessing.ml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index e8e350997f..68b9b8ddc8 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -6,7 +6,6 @@ *) open GoblintCil -open Printf let extract_file_name s = (*There still may be a need to filter more chars*) let ls = String.split_on_char '/' s in (*Assuming '/' as path seperator*) @@ -19,7 +18,7 @@ let extract_file_name s = (*There still may be a need to filt s' let show_location_id l = - string_of_int l.line ^ "_" ^ string_of_int l.column (*extract_file_name l.file ^ "_" ^ *) + string_of_int l.line ^ "_" ^ string_of_int l.column ^ "_" ^ "file" ^ "_" ^ extract_file_name l.file class loopCounterVisitor (fd : fundec) = object(self) inherit nopCilVisitor @@ -29,7 +28,6 @@ class loopCounterVisitor (fd : fundec) = object(self) let name = "term"^show_location_id loc in let typ = Cil.intType in let v = (Cil.makeLocalVar fd name typ) in - (*let init_stmt = mkStmt (Instr [Set (var v, zero, loc, eloc)]) in*) let init_stmt = mkStmtOneInstr @@ Set (var v, zero, loc, eloc) in let inc_stmt = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in (match b.bstmts with From 542ed6652b58d078565be72866bbb277fae86006 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Wed, 24 May 2023 18:51:00 +0200 Subject: [PATCH 0039/1312] Checking bounds, other stuff --- src/analyses/termination_new.ml | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index e461492724..92973aab8a 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -4,9 +4,24 @@ open Analyses open GoblintCil open TerminationPreprocessing -let terminates ctx loop exp = +exception PreProcessing of string + +(* +let _ = WitnessUtil.find_loop_heads + +let check_loop_head ctx = false + *) + +let get_prepr_var () : varinfo = + raise (PreProcessing "No loop variable") (* TODO *) + +(** Checks whether a variable can be bounded *) +let check_bounded ctx varinfo = + let exp = Lval (Var varinfo, NoOffset) in match ctx.ask (EvalInt exp) with - _ -> () (* TODO *) + `Top -> false + | `Bot -> raise (PreProcessing "Loop variable is Bot") + | _ -> true (* TODO: Is this sound? *) module Spec : Analyses.MCPSpec = struct @@ -22,7 +37,18 @@ struct (** Provides some default implementations *) include Analyses.IdentitySpec - let branch ctx (exp:exp) (tv:bool) = + let assign ctx (lval : lval) (rval : exp) = + (* Detect preprocessing variable assignment to 0 *) + match lval, rval with + (Var get_prepr_var, NoOffset), zero -> ctx.local (* TODO *) + | _ -> ctx.local + + let branch ctx (exp : exp) (tv : bool) = + (* + let is_loop_head = check_loop_head ctx in + if is_loop_head then + enter_loop ctx; + *) ctx.local (* TODO *) let query ctx (type a) (q: a Queries.t): a Queries.result = From 461054eff5e10db4f48e4bb74c3c0e13081242ab Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Thu, 25 May 2023 17:51:17 +0200 Subject: [PATCH 0040/1312] Use new function register_preprocess_cil --- src/analyses/termination_new.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index 92973aab8a..a20df2c0d9 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -59,6 +59,6 @@ end let () = (** Register the preprocessing *) - Cilfacade.register_preprocess (Spec.name ()) (new loopCounterVisitor); + Cilfacade.register_preprocess_cil (Spec.name ()) (new loopCounterVisitor); (** Register this analysis within the master control program *) MCP.register_analysis (module Spec : MCPSpec) From 6ccd800a7158bd0e9dbf45b859aba1c53682a25d Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Mon, 29 May 2023 10:28:45 +0200 Subject: [PATCH 0041/1312] added comments --- runningGob.sh | 12 ++++++++++-- src/util/cilCfg.ml | 2 +- src/util/cilfacade.ml | 2 +- src/util/terminationPreprocessing.ml | 2 +- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/runningGob.sh b/runningGob.sh index b67bc36d9a..c8f2901488 100755 --- a/runningGob.sh +++ b/runningGob.sh @@ -1,10 +1,18 @@ #!/bin/bash make #make install + +# set options and file for apron execution options="--set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra" cfile="tests/regression/55-loop-unrolling/01-simple-cases.c" -./goblint $cfile $options --enable justcil > output.txt -./goblint $cfile $options --html + +# run analysis, write cil output to file and enable visualization via html +#./goblint $cfile $options --enable justcil > output.txt +#./goblint $cfile $options --html + +./goblint --enable warn.debug tests/regression/99-tutorials/01-first.c --set "ana.activated[+]" signs --html + +# set up server to see visualizatino python3 -m http.server --directory result 8081 #./goblint --enable dbg.debug tests/regression/55-loop-unrolling/01-simple-cases.c --enable ana.int.interval --set "ana.activated[+]" signs --enable justcil > output.txt diff --git a/src/util/cilCfg.ml b/src/util/cilCfg.ml index 90d6be14ae..066f1ed981 100644 --- a/src/util/cilCfg.ml +++ b/src/util/cilCfg.ml @@ -48,7 +48,7 @@ let createCFG (fileAST: file) = (* BB causes the output CIL file to no longer compile. *) (* Since we want the output of justcil to compile, we do not run allBB visitor if justcil is enable, regardless of *) (* exp.basic-blocks. This does not matter, as we will not run any analysis anyway, when justcil is enabled. *) - + (* the preprocessing must be done here, to add the changes of CIL to the CFG*) Cilfacade.do_preprocess_cil fileAST; if not (get_bool "exp.basic-blocks") && not (get_bool "justcil") then end_basic_blocks fileAST; diff --git a/src/util/cilfacade.ml b/src/util/cilfacade.ml index fd298e01a0..a0e361bc85 100644 --- a/src/util/cilfacade.ml +++ b/src/util/cilfacade.ml @@ -74,7 +74,7 @@ let do_preprocess ast = iterGlobals ast (function GFun (fd,_) -> List.iter (f fd) !visitors | _ -> ()) let visitors_cil = ref [] -(* does exactly the same as register_preprocess_cil but it is executed earlier, before the CFG is created*) +(* does exactly the same as register_preprocess but it is executed earlier, before the CFG is created*) let register_preprocess_cil name visitor_fun = visitors_cil := !visitors_cil @ [name, visitor_fun] diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index 68b9b8ddc8..54e18d7df7 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -18,7 +18,7 @@ let extract_file_name s = (*There still may be a need to filt s' let show_location_id l = - string_of_int l.line ^ "_" ^ string_of_int l.column ^ "_" ^ "file" ^ "_" ^ extract_file_name l.file + string_of_int l.line ^ "_" ^ string_of_int l.column ^ "-file" ^ "_" ^ extract_file_name l.file class loopCounterVisitor (fd : fundec) = object(self) inherit nopCilVisitor From 07729336ab56a27df7c38ef08925ca22196cc50b Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Mon, 29 May 2023 15:50:25 +0200 Subject: [PATCH 0042/1312] Set abstract lattice, remove unused stuff, rename --- src/analyses/termination_new.ml | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index a20df2c0d9..7727bf5b3f 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -6,14 +6,10 @@ open TerminationPreprocessing exception PreProcessing of string -(* -let _ = WitnessUtil.find_loop_heads +let visited = Stack.create () (* TODO: Is this allowed? *) -let check_loop_head ctx = false - *) - -let get_prepr_var () : varinfo = - raise (PreProcessing "No loop variable") (* TODO *) +let is_loop_counter_var (x : varinfo) = + false (* TODO: Actually detect loop counter variables *) (** Checks whether a variable can be bounded *) let check_bounded ctx varinfo = @@ -28,8 +24,8 @@ struct let name () = "termination" - module D = Lattice.Unit (* TODO *) - module C = D (* TODO *) + module D = MapDomain.MapBot (Basetype.Variables) (BoolDomain.MustBool) + module C = D let startstate _ = D.bot () (* TODO *) let exitstate = startstate (* TODO *) @@ -38,17 +34,19 @@ struct include Analyses.IdentitySpec let assign ctx (lval : lval) (rval : exp) = - (* Detect preprocessing variable assignment to 0 *) + (* Detect loop counter variable assignment to 0 *) match lval, rval with - (Var get_prepr_var, NoOffset), zero -> ctx.local (* TODO *) + (* Assume that the following loop does not terminate *) + (Var x, NoOffset), zero when is_loop_counter_var x -> + (* Remember the lcv *) + (* + let () = Stack.push x visited in + let () = enter_loop in + *) + D.add x false ctx.local | _ -> ctx.local let branch ctx (exp : exp) (tv : bool) = - (* - let is_loop_head = check_loop_head ctx in - if is_loop_head then - enter_loop ctx; - *) ctx.local (* TODO *) let query ctx (type a) (q: a Queries.t): a Queries.result = From 5f8c4c618f860872c59ee07b9e760c0901cff75c Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Mon, 29 May 2023 17:47:46 +0200 Subject: [PATCH 0043/1312] Implement bound checking on loop exit We assume an assignment to a loop exit indicator with the currently relevant loop counter variable as the right hand side expression. --- src/analyses/termination_new.ml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index 7727bf5b3f..10aaf4aef3 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -6,11 +6,12 @@ open TerminationPreprocessing exception PreProcessing of string -let visited = Stack.create () (* TODO: Is this allowed? *) - let is_loop_counter_var (x : varinfo) = false (* TODO: Actually detect loop counter variables *) +let is_loop_exit_indicator (x : varinfo) = + false (* TODO: Actually detect loop exit indicators *) + (** Checks whether a variable can be bounded *) let check_bounded ctx varinfo = let exp = Lval (Var varinfo, NoOffset) in @@ -27,7 +28,7 @@ struct module D = MapDomain.MapBot (Basetype.Variables) (BoolDomain.MustBool) module C = D - let startstate _ = D.bot () (* TODO *) + let startstate _ = D.bot () let exitstate = startstate (* TODO *) (** Provides some default implementations *) @@ -38,16 +39,15 @@ struct match lval, rval with (* Assume that the following loop does not terminate *) (Var x, NoOffset), zero when is_loop_counter_var x -> - (* Remember the lcv *) - (* - let () = Stack.push x visited in - let () = enter_loop in - *) D.add x false ctx.local + (* Loop exit: Check whether loop counter variable is bounded *) + | (Var y, NoOffset), Lval (Var x, NoOffset) when is_loop_exit_indicator y -> + let is_bounded = check_bounded ctx x in + D.add x is_bounded ctx.local | _ -> ctx.local let branch ctx (exp : exp) (tv : bool) = - ctx.local (* TODO *) + ctx.local (* TODO: Do we actually need a branch transfer function? *) let query ctx (type a) (q: a Queries.t): a Queries.result = let open Queries in From 378bbc452dad48b7caa58fb4c5803753d946c41f Mon Sep 17 00:00:00 2001 From: Isidor Zweckstetter Date: Mon, 29 May 2023 18:21:50 +0200 Subject: [PATCH 0044/1312] add List to loopCounterVisitor constructor, to store loopCounter variables in --- src/analyses/termination_new.ml | 4 +++- src/util/terminationPreprocessing.ml | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index a20df2c0d9..3b255a4d61 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -6,6 +6,8 @@ open TerminationPreprocessing exception PreProcessing of string +let loopCounters : varinfo list ref = ref [] + (* let _ = WitnessUtil.find_loop_heads @@ -59,6 +61,6 @@ end let () = (** Register the preprocessing *) - Cilfacade.register_preprocess_cil (Spec.name ()) (new loopCounterVisitor); + Cilfacade.register_preprocess_cil (Spec.name ()) (new loopCounterVisitor loopCounters); (** Register this analysis within the master control program *) MCP.register_analysis (module Spec : MCPSpec) diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index e8e350997f..a1627db694 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -21,7 +21,7 @@ let extract_file_name s = (*There still may be a need to filt let show_location_id l = string_of_int l.line ^ "_" ^ string_of_int l.column (*extract_file_name l.file ^ "_" ^ *) -class loopCounterVisitor (fd : fundec) = object(self) +class loopCounterVisitor lc (fd : fundec) = object(self) inherit nopCilVisitor method! vstmt s = let action s = match s.skind with @@ -36,6 +36,7 @@ class loopCounterVisitor (fd : fundec) = object(self) | cont :: cond :: ss -> b.bstmts <- cont :: inc_stmt :: cond :: ss; (*cont :: cond :: inc_stmt :: ss = it is also possible, but for loops with cond at the end, inc is also at the end*) | _ -> ()); + lc := List.append !lc ([v] : varinfo list); let nb = mkBlock [init_stmt; mkStmt s.skind] in s.skind <- Block nb; s From 6efe564e00671ab9e487478cc2a324f5d03bf5ab Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Mon, 29 May 2023 18:23:57 +0200 Subject: [PATCH 0045/1312] added new queries for termination analysis --- runningGob.sh | 16 +++++++++++----- src/analyses/termination_new.ml | 15 ++++++++++++++- src/domains/queries.ml | 12 ++++++++++++ 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/runningGob.sh b/runningGob.sh index c8f2901488..7c47eb36ea 100755 --- a/runningGob.sh +++ b/runningGob.sh @@ -3,14 +3,20 @@ make #make install # set options and file for apron execution -options="--set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra" -cfile="tests/regression/55-loop-unrolling/01-simple-cases.c" +options_apron="--set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --enable warn.debug" #note: preprocessing first needs to be added to apron +options_signs="--set "ana.activated[+]" signs --enable warn.debug" +options_term="--set "ana.activated[+]" termination --enable warn.debug" + +cfile_loops="tests/regression/55-loop-unrolling/01-simple-cases.c" +cfile_signs="tests/regression/99-tutorials/01-first.c" # run analysis, write cil output to file and enable visualization via html -#./goblint $cfile $options --enable justcil > output.txt -#./goblint $cfile $options --html +#./goblint $cfile_loops $options_apron --enable justcil > output.txt +#./goblint $cfile_loops $options_apron --html -./goblint --enable warn.debug tests/regression/99-tutorials/01-first.c --set "ana.activated[+]" signs --html +# run analysis, write cil output to file and enable visualization via html +./goblint $cfile_loops $options_term --enable justcil > output.txt +./goblint $cfile_loops $options_term --html # set up server to see visualizatino python3 -m http.server --directory result 8081 diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index a20df2c0d9..3a09693b5c 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -3,6 +3,7 @@ open Analyses open GoblintCil open TerminationPreprocessing +open Printf exception PreProcessing of string @@ -51,9 +52,21 @@ struct *) ctx.local (* TODO *) + let terminates ctx = + ctx.ask Queries.MustTermProg + + (* provides information to Goblint*) let query ctx (type a) (q: a Queries.t): a Queries.result = let open Queries in - Result.top q (* TODO *) + match q with + | Queries.MustTermLoop v when check_bounded ctx v -> + printf "Termination analysis loop\n"; + true (* TODO*) + | Queries.MustTermProg -> + printf "Termination analysis prog\n"; + let b = terminates ctx in + true (*TODO*) + | _ -> Result.top q end diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 2d1b25eca9..2df4a57af8 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -97,6 +97,8 @@ type _ t = | MayAccessed: AccessDomain.EventSet.t t | MayBeTainted: LS.t t | MayBeModifiedSinceSetjmp: JmpBufDomain.BufferEntry.t -> VS.t t + | MustTermLoop: varinfo -> MustBool.t t (** TODO: not sure if it is the MayBool*) + | MustTermProg: MustBool.t t type 'a result = 'a @@ -157,6 +159,8 @@ struct | MayAccessed -> (module AccessDomain.EventSet) | MayBeTainted -> (module LS) | MayBeModifiedSinceSetjmp _ -> (module VS) + | MustTermLoop _ -> (module MustBool) + | MustTermProg -> (module MustBool) (** Get bottom result for query. *) let bot (type a) (q: a t): a result = @@ -216,6 +220,8 @@ struct | MayAccessed -> AccessDomain.EventSet.top () | MayBeTainted -> LS.top () | MayBeModifiedSinceSetjmp _ -> VS.top () + | MustTermLoop _ -> MustBool.top () + | MustTermProg -> MustBool.top () end (* The type any_query can't be directly defined in Any as t, @@ -272,6 +278,8 @@ struct | Any ActiveJumpBuf -> 46 | Any ValidLongJmp -> 47 | Any (MayBeModifiedSinceSetjmp _) -> 48 + | Any (MustTermLoop _) -> 49 + | Any MustTermProg -> 50 let rec compare a b = let r = Stdlib.compare (order a) (order b) in @@ -306,6 +314,7 @@ struct compare (Any q1) (Any q2) | Any (IsHeapVar v1), Any (IsHeapVar v2) -> CilType.Varinfo.compare v1 v2 | Any (IsMultiple v1), Any (IsMultiple v2) -> CilType.Varinfo.compare v1 v2 + | Any (MustTermLoop v1), Any (MustTermLoop v2) -> CilType.Varinfo.compare v1 v2 | Any (EvalThread e1), Any (EvalThread e2) -> CilType.Exp.compare e1 e2 | Any (EvalJumpBuf e1), Any (EvalJumpBuf e2) -> CilType.Exp.compare e1 e2 | Any (WarnGlobal vi1), Any (WarnGlobal vi2) -> Stdlib.compare (Hashtbl.hash vi1) (Hashtbl.hash vi2) @@ -342,6 +351,7 @@ struct | Any (IterVars i) -> 0 | Any (PathQuery (i, q)) -> 31 * i + hash (Any q) | Any (IsHeapVar v) -> CilType.Varinfo.hash v + | Any (MustTermLoop v) -> CilType.Varinfo.hash v | Any (IsMultiple v) -> CilType.Varinfo.hash v | Any (EvalThread e) -> CilType.Exp.hash e | Any (EvalJumpBuf e) -> CilType.Exp.hash e @@ -404,6 +414,8 @@ struct | Any MayBeTainted -> Pretty.dprintf "MayBeTainted" | Any DYojson -> Pretty.dprintf "DYojson" | Any MayBeModifiedSinceSetjmp buf -> Pretty.dprintf "MayBeModifiedSinceSetjmp %a" JmpBufDomain.BufferEntry.pretty buf + | Any (MustTermLoop v) -> Pretty.dprintf "MustTermLoop %a" CilType.Varinfo.pretty v + | Any MustTermProg -> Pretty.dprintf "MustTermProg" end let to_value_domain_ask (ask: ask) = From 371ae720e91ce559705fc01f720b24baf496e4d0 Mon Sep 17 00:00:00 2001 From: Isidor Zweckstetter Date: Mon, 29 May 2023 18:42:15 +0200 Subject: [PATCH 0046/1312] merged --- src/analyses/apron/apronAnalysis.apron.ml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/analyses/apron/apronAnalysis.apron.ml b/src/analyses/apron/apronAnalysis.apron.ml index dfd9c95d70..0b067ba814 100644 --- a/src/analyses/apron/apronAnalysis.apron.ml +++ b/src/analyses/apron/apronAnalysis.apron.ml @@ -39,7 +39,6 @@ let after_config () = let _ = - Cilfacade.register_preprocess_cil ("apron") (new loopCounterVisitor); AfterConfig.register after_config From df206b0173584cc0c0ad79ce6c6a6fe23042ca98 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Mon, 29 May 2023 18:47:30 +0200 Subject: [PATCH 0047/1312] small changes in the termination_new file for the query function, still in progress --- src/analyses/termination_new.ml | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index 2d3bd339bb..9e8f095e8a 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -50,20 +50,15 @@ struct let branch ctx (exp : exp) (tv : bool) = ctx.local (* TODO: Do we actually need a branch transfer function? *) - let terminates ctx = - ctx.ask Queries.MustTermProg - + (* provides information to Goblint*) let query ctx (type a) (q: a Queries.t): a Queries.result = let open Queries in match q with - | Queries.MustTermLoop v when check_bounded ctx v -> - printf "Termination analysis loop\n"; - true (* TODO*) + | Queries.MustTermLoop v when check_bounded ctx v -> + true (* TODO should we use the checl_bound function?*) | Queries.MustTermProg -> - printf "Termination analysis prog\n"; - let b = terminates ctx in - true (*TODO*) + true (*TODO check if all values in the domain are true -> true*) | _ -> Result.top q end From 54053c88a6f8776c353b999f247ef2ba80458dd3 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Mon, 29 May 2023 19:45:13 +0200 Subject: [PATCH 0048/1312] Implement is_loop_counter_var --- src/analyses/termination_new.ml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index d15bb73268..1eb4857ad3 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -10,7 +10,7 @@ exception PreProcessing of string let loopCounters : varinfo list ref = ref [] let is_loop_counter_var (x : varinfo) = - false (* TODO: Actually detect loop counter variables *) + List.mem x !loopCounters let is_loop_exit_indicator (x : varinfo) = false (* TODO: Actually detect loop exit indicators *) @@ -56,12 +56,12 @@ struct (* provides information to Goblint*) let query ctx (type a) (q: a Queries.t): a Queries.result = let open Queries in - match q with + match q with | Queries.MustTermLoop v when check_bounded ctx v -> true (* TODO should we use the checl_bound function?*) - | Queries.MustTermProg -> + | Queries.MustTermProg -> true (*TODO check if all values in the domain are true -> true*) - | _ -> Result.top q + | _ -> Result.top q end From 3f200faa6d996afacd182a89850617171776de28 Mon Sep 17 00:00:00 2001 From: Isidor Zweckstetter Date: Tue, 30 May 2023 01:46:21 +0200 Subject: [PATCH 0049/1312] added Variable to indicate Loop exits -more testing might be needed --- src/analyses/termination_new.ml | 5 ++++- src/util/terminationPreprocessing.ml | 10 ++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index d15bb73268..68538c9d88 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -9,6 +9,8 @@ exception PreProcessing of string let loopCounters : varinfo list ref = ref [] +let loopExit : varinfo ref = ref (makeVarinfo false "-error" Cil.intType) + let is_loop_counter_var (x : varinfo) = false (* TODO: Actually detect loop counter variables *) @@ -55,6 +57,7 @@ struct (* provides information to Goblint*) let query ctx (type a) (q: a Queries.t): a Queries.result = + print_endline @@ ""^(!loopExit.vname); let open Queries in match q with | Queries.MustTermLoop v when check_bounded ctx v -> @@ -67,6 +70,6 @@ end let () = (** Register the preprocessing *) - Cilfacade.register_preprocess_cil (Spec.name ()) (new loopCounterVisitor loopCounters); + Cilfacade.register_preprocess_cil (Spec.name ()) (new loopCounterVisitor loopCounters loopExit); (** Register this analysis within the master control program *) MCP.register_analysis (module Spec : MCPSpec) diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index b3eb8692d2..71644c0894 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -20,8 +20,13 @@ let extract_file_name s = (*There still may be a need to filt let show_location_id l = string_of_int l.line ^ "_" ^ string_of_int l.column ^ "-file" ^ "_" ^ extract_file_name l.file -class loopCounterVisitor lc (fd : fundec) = object(self) +class loopCounterVisitor lc le (fd : fundec) = object(self) inherit nopCilVisitor + method! vfunc (f:fundec) = + let exit_name = "term_exit-" in + let typ = Cil.intType in + le := Cil.makeLocalVar fd exit_name typ; + DoChildren; (* function definition *) method! vstmt s = let action s = match s.skind with | Loop (b, loc, eloc, _, _) -> @@ -30,12 +35,13 @@ class loopCounterVisitor lc (fd : fundec) = object(self) let v = (Cil.makeLocalVar fd name typ) in let init_stmt = mkStmtOneInstr @@ Set (var v, zero, loc, eloc) in let inc_stmt = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in + let exit_stmt = mkStmtOneInstr @@ Set ((var !le), (Lval (var v)), loc, eloc) in (match b.bstmts with | cont :: cond :: ss -> b.bstmts <- cont :: inc_stmt :: cond :: ss; (*cont :: cond :: inc_stmt :: ss = it is also possible, but for loops with cond at the end, inc is also at the end*) | _ -> ()); lc := List.append !lc ([v] : varinfo list); - let nb = mkBlock [init_stmt; mkStmt s.skind] in + let nb = mkBlock [init_stmt; mkStmt s.skind; exit_stmt] in s.skind <- Block nb; s | _ -> s From 77e99cb34459246a1953482ec547d9df6d875922 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Tue, 30 May 2023 10:03:50 +0200 Subject: [PATCH 0050/1312] Implement is_loop_exit_indicator --- src/analyses/termination_new.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index fe86035920..1128365963 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -15,7 +15,7 @@ let is_loop_counter_var (x : varinfo) = List.mem x !loopCounters let is_loop_exit_indicator (x : varinfo) = - false (* TODO: Actually detect loop exit indicators *) + x = !loopExit (** Checks whether a variable can be bounded *) let check_bounded ctx varinfo = From f1c57fcacaf8d29ae8f4f0e430be54472b0edc2c Mon Sep 17 00:00:00 2001 From: Isidor Zweckstetter Date: Tue, 30 May 2023 13:08:15 +0200 Subject: [PATCH 0051/1312] LoopExit changed to Global variable --- src/util/terminationPreprocessing.ml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index 71644c0894..1a0e725624 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -23,9 +23,11 @@ let show_location_id l = class loopCounterVisitor lc le (fd : fundec) = object(self) inherit nopCilVisitor method! vfunc (f:fundec) = - let exit_name = "term_exit-" in - let typ = Cil.intType in - le := Cil.makeLocalVar fd exit_name typ; + if !le.vname <> "term_exit-" then begin + let exit_name = "term_exit-" in + let typ = Cil.intType in + le := Cil.makeGlobalVar exit_name typ; + end; DoChildren; (* function definition *) method! vstmt s = let action s = match s.skind with From 7798c0448c9204d17c131eef4f9c9691f45ec025 Mon Sep 17 00:00:00 2001 From: Nathan Schmidt Date: Tue, 30 May 2023 19:50:04 +0200 Subject: [PATCH 0052/1312] Draft for complete Null Byte Domain TODO: strstr, strcmp and strncmp TODO: check and simplify code TODO: update string functions case in base analysis using new domain --- src/cdomains/arrayDomain.ml | 766 +++++++++++++++++++++++------------ src/cdomains/arrayDomain.mli | 48 ++- src/util/options.schema.json | 2 +- 3 files changed, 537 insertions(+), 279 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index c685099e8d..c2468e885f 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -8,7 +8,7 @@ module A = Array module BI = IntOps.BigIntOps module VDQ = ValueDomainQueries -type domain = TrivialDomain | PartitionedDomain | UnrolledDomain | MustNullByteDomain +type domain = TrivialDomain | PartitionedDomain | UnrolledDomain | NullByteDomain (* determines the domain based on variable, type and flag *) let get_domain ~varAttr ~typAttr = @@ -16,7 +16,6 @@ let get_domain ~varAttr ~typAttr = | "partitioned" -> PartitionedDomain | "trivial" -> TrivialDomain | "unroll" -> UnrolledDomain - | "mustnullbyte" -> MustNullByteDomain | _ -> failwith "AttributeConfiguredArrayDomain: invalid option for domain" in (*TODO add options?*) @@ -62,14 +61,19 @@ sig val smart_leq: (exp -> BI.t option) -> (exp -> BI.t option) -> t -> t -> bool val update_length: idx -> t -> t + val project: ?varAttr:attributes -> ?typAttr:attributes -> VDQ.t -> t -> t +end + +module type Str = +sig + include S val to_string: t -> t - val to_n_string: t -> int -> bool -> t + val to_n_string: t -> int -> t val to_string_length: t -> idx + val string_copy: t -> t -> int option -> t val string_concat: t -> t -> int option -> t - val substring_extraction: t -> t -> t option + val substring_extraction: t -> t -> t val string_comparison: t -> t -> int option -> idx - - val project: ?varAttr:attributes -> ?typAttr:attributes -> VDQ.t -> t -> t end module type LatticeWithSmartOps = @@ -109,13 +113,6 @@ struct let smart_leq _ _ = leq let update_length _ x = x - let to_string _ = top () - let to_n_string _ _ _ = top () - let to_string_length _ = Idx.top () - let string_concat _ _ _ = top () - let substring_extraction _ _ = Some (top ()) - let string_comparison _ _ _ = Idx.top () - let project ?(varAttr=[]) ?(typAttr=[]) _ t = t end @@ -204,12 +201,6 @@ struct let smart_widen _ _ = widen let smart_leq _ _ = leq let update_length _ x = x - let to_string _ = top () - let to_n_string _ _ _ = top () - let to_string_length _ = Idx.top_of !Cil.kindOfSizeOf - let string_concat _ _ _ = top () - let substring_extraction _ _ = Some (top ()) - let string_comparison _ _ _ = Idx.top_of IInt let project ?(varAttr=[]) ?(typAttr=[]) _ t = t end @@ -722,205 +713,10 @@ struct (* arrays can not be partitioned according to multiple expressions, arbitrary prefer the first one here *) x - let to_string _ = top () - let to_n_string _ _ _ = top () - let to_string_length _ = Idx.top_of !Cil.kindOfSizeOf - let string_concat _ _ _ = top () - let substring_extraction _ _ = Some (top ()) - let string_comparison _ _ _ = Idx.top_of IInt - let update_length _ x = x let project ?(varAttr=[]) ?(typAttr=[]) _ t = t end -module MustNullByte (Val: Lattice.S) (Idx: IntDomain.Z): S with type value = Val.t option and type idx = Idx.t = -struct - include SetDomain.Reverse (SetDomain.Make (Idx)) - let name () = "arrays containing null bytes" - type idx = Idx.t - type value = Val.t option (* None = null byte *) - - let domain_of_t _ = MustNullByteDomain - - let get ?(checkBounds=true) (ask: VDQ.t) index_set (_, i) = - let rec check_indexes i max = - if Z.gt i max then - true - else if exists (fun x -> match Idx.to_int x with Some num -> Z.equal i num | None -> false) index_set then - check_indexes (Z.add i Z.one) max - else - false in - let min_i = match Idx.minimal i with - | Some min -> min - | None -> Z.zero in (* assume worst case minimal index *) - let max_i = Idx.maximal i in - match max_i with - (* if there is no maximum number in interval, return top of value *) - | None -> Some (Val.top ()) - | Some max -> - (* else only return null if all numbers in interval are in index set *) - if check_indexes min_i max then - None - else - Some (Val.top ()) - - let set (ask: VDQ.t) index_set (_, i) v = - let min_i = match Idx.minimal i with - | Some min -> min - | None -> Z.zero in (* assume worst case minimal index *) - let max_i = Idx.maximal i in - match max_i, v with - (* if there is no maxinum number in interval and value = null, return index set unchanged *) - | None, None -> index_set - (* if there is no maximum number in interval and value != null, return top = empty set *) - | None, Some _ -> top () - | Some max, None -> - (* if i is an exact number and value = null, add i to index set *) - if Z.equal min_i max then - add (Idx.of_int !Cil.kindOfSizeOf min_i) index_set - (* if i is an interval and value = null, return index set unchanged *) - else - index_set - | Some max, Some _ -> - (* if i is an exact number and value != null, remove i from index set *) - if Z.equal min_i max then - remove (Idx.of_int !Cil.kindOfSizeOf min_i) index_set - (* if i is an interval and value != null, return top = empty set *) - else - top () - - let make ?(varAttr=[]) ?(typAttr=[]) i v = - (* TODO: for now naive addition of all indexes in interval one by one -- yup, that's very inefficient *) - let rec add_indexes index_set i max = - if Z.gt i max then - index_set - else - add_indexes (add (Idx.of_int !Cil.kindOfSizeOf i) index_set) (Z.add i Z.one) max in - match Idx.minimal i, Idx.maximal i, v with - (* if there is no minimal number in interval or value != null, return top *) - | None, _, _ - | Some _, _, Some _ -> top () - (* if value = null, return bot (i.e. set of all indexes from 0 to min) *) - | Some min, _, None -> add_indexes (empty ()) Z.zero min - - let length _ = None - - let move_if_affected ?(replace_with_const=false) _ index_set _ _ = index_set - - let get_vars_in_e _ = [] - - let map f index_set = - (* if f(null) = null, all values at indexes in set are still surely null *) - if f None = None then - index_set - (* else return top as checking the effect of f for every possible value is unfeasible *) - else - top () - - (* TODO: check if there is no smarter implementation of this (probably not) *) - let fold_left f a _ = f a (Some (Val.top ())) - - let smart_join _ _ = join - let smart_widen _ _ = widen - let smart_leq _ _ = leq - - (* string functions *) - let to_string index_set = - (* if index set is empty, the array doesn't surely contain a null byte and an overflow might happen *) - if is_empty index_set then - (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "May access array past end: potential buffer overflow"; - index_set) - (* else only keep the smallest index in the set *) - else - (* TODO: would min_elt work? (probably not) *) - let min_null = fold (fun x acc -> Idx.lt x acc) index_set (Idx.bot_of !Cil.kindOfSizeOf) in - singleton min_null - - let to_n_string index_set n no_null_warn = - (* TODO: for now naive addition of all indexes in interval one by one -- yup, that's very inefficient *) - let rec add_indexes index_set i max = - if Z.geq i max then - index_set - else - add_indexes (add (Idx.of_int !Cil.kindOfSizeOf i) index_set) (Z.add i Z.one) max in - (* if index set is empty, the array doesn't surely contain a null byte and an overflow might happen *) - if is_empty index_set then - (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "May access array past end: potential buffer overflow"; - index_set) - (* else if index set not empty *) - else - (* TODO: would min_elt work? (probably not) *) - let min_null = fold (fun x acc -> Idx.lt x acc) index_set (Idx.bot_of !Cil.kindOfSizeOf) in - match Idx.to_int min_null with - | Some i -> - (* ... keep smallest index in set if smaller than n and add as many null bytes as necessary to obtain n bytes string *) - if Z.lt i (Z.of_int n) then - add_indexes (singleton min_null) i (Z.of_int n) - (* ... or if smallest index >= n, return empty set and warn if no_null_warn = true *) - else if no_null_warn then - (M.warn "Resulting string may not contain a terminating null byte"; - empty ()) - else - empty () - | None -> singleton min_null (* should not happen, but if it does, can't compute additional must null bytes *) - - let to_string_length index_set = - (* if index set is empty, return top as array may contain null bytes we don't know of *) - (* TODO: warning not useful I believe? ((In theory, one could use strlen to determine if there is a null byte in array or not to - * know if bytes of the array are possibly overwriten in a malicious undertaking)) *) - if is_empty index_set then - Idx.top_of !Cil.kindOfSizeOf - else - (* TODO: would min_elt work? (probably not) *) - let min_null = fold (fun x acc -> Idx.lt x acc) index_set (Idx.bot_of !Cil.kindOfSizeOf) in - match Idx.to_int min_null with - (* else if we can determine the minimal index in set, we know 0 <= length <= minimal index *) - | Some i -> Idx.of_interval !Cil.kindOfSizeOf (Z.zero, i) - | None -> Idx.top_of !Cil.kindOfSizeOf - - let string_concat index_set1 index_set2 n = - let s1 = to_string index_set1 in - (* if s1 is empty, no statement possible for must null bytes of concatenation; warning generated by to_string above *) - if is_empty s1 then - empty () - else - begin match n with - (* concat at most n bytes of index_set2 to index_set1 = strncat *) - | Some num -> - let s1_i = choose s1 in - let s2 = to_n_string index_set2 num false in - (* if no must null byte among first n bytes of s2, no statement possible as no knowledge of may null bytes *) - if is_empty s2 then - empty() - (* else concatenation has null byte at strlen(s1) + first null byte found in s2 *) - else - (* TODO: would min_elt work? (probably not) *) - let min_null_s2 = fold (fun x acc -> Idx.lt x acc) s2 (Idx.bot_of !Cil.kindOfSizeOf) in - singleton (Idx.add s1_i min_null_s2) - (* concat bytes of index_set2 to index_set1 until a null byte is reached = strcat *) - | None -> - let s2 = to_string index_set2 in - (* if s2 is empty, no statement possible for must null bytes of concatenation; warning generated by to_string above *) - if is_empty s2 then - empty () - (* else concatenation has null byte at strlen(s1) + strlen(s2) *) - else - let s1_i = choose s1 in - let s2_i = choose s2 in - singleton (Idx.add s1_i s2_i) - end - - (* TODO -- can I even do something useful at all? Might as well leave out substring_extraction and string_comparison *) - let substring_extraction _ _ = Some (top ()) - - (* TODO *) - let string_comparison _ _ _ = Idx.top_of IInt - - let update_length _ x = x - - let project ?(varAttr=[]) ?(typAttr=[]) _ t = t -end - (* This is the main array out of bounds check *) let array_oob_check ( type a ) (module Idx: IntDomain.Z with type t = a) (x, l) (e, v) = if GobConfig.get_bool "ana.arrayoob" then (* The purpose of the following 2 lines is to give the user extra info about the array oob *) @@ -967,26 +763,6 @@ struct let smart_widen _ _ = widen let smart_leq _ _ = leq - let to_string _ = top () - let to_n_string a n _ = - begin match length a with - | Some len -> - begin match Idx.maximal len with - | Some max -> - if Z.gt (Z.of_int n) max then - (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "May produce a buffer overflow if the string doesn't contain a null byte in the first n bytes"; - top ()) - else - top () - | None -> top () - end - | None -> top () - end - let to_string_length _ = Idx.top_of !Cil.kindOfSizeOf - let string_concat _ _ _ = top () - let substring_extraction _ _ = Some (top ()) - let string_comparison _ _ _ = Idx.top_of IInt - (* It is not necessary to do a least-upper bound between the old and the new length here. *) (* Any array can only be declared in one location. The value for newl that we get there is *) (* the one obtained by abstractly evaluating the size expression at this location for the *) @@ -1039,13 +815,6 @@ struct let l = Idx.join xl yl in Idx.leq xl yl && Base.smart_leq_with_length (Some l) x_eval_int y_eval_int x y - let to_string _ = top () - let to_n_string _ _ _ = top () - let to_string_length _ = Idx.top_of !Cil.kindOfSizeOf - let string_concat _ _ _ = top () - let substring_extraction _ _ = Some (top ()) - let string_comparison _ _ _ = Idx.top_of IInt - (* It is not necessary to do a least-upper bound between the old and the new length here. *) (* Any array can only be declared in one location. The value for newl that we get there is *) (* the one obtained by abstractly evaluating the size expression at this location for the *) @@ -1067,12 +836,8 @@ struct module Base = Unroll (Val) (Idx) include Lattice.Prod (Base) (Idx) type idx = Idx.t - type value = Val.t let to_string _ = top () - let to_n_string _ _ _ = top () - let to_string_length _ = Idx.top_of !Cil.kindOfSizeOf - let string_concat _ _ _ = top () - let substring_extraction _ _ = Some (top ()) - let string_comparison _ _ _ = Idx.top_of IInt + type value = Val.t + let domain_of_t _ = UnrolledDomain let get ?(checkBounds=true) (ask : VDQ.t) (x, (l : idx)) (e, v) = @@ -1091,13 +856,6 @@ struct let smart_widen _ _ = widen let smart_leq _ _ = leq - let to_string _ = top () - let to_n_string _ _ _ = top () - let to_string_length _ = Idx.top_of !Cil.kindOfSizeOf - let string_concat _ _ _ = top () - let substring_extraction _ _ = Some (top ()) - let string_comparison _ _ _ = Idx.top_of IInt - (* It is not necessary to do a least-upper bound between the old and the new length here. *) (* Any array can only be declared in one location. The value for newl that we get there is *) (* the one obtained by abstractly evaluating the size expression at this location for the *) @@ -1114,6 +872,498 @@ struct let to_yojson (x, y) = `Assoc [ (Base.name (), Base.to_yojson x); ("length", Idx.to_yojson y) ] end +module type LatticeWithNull = +sig + include Lattice.S + val null: unit -> t + val not_null: unit -> t + val is_null: t -> bool +end + +module NullByte (Val: LatticeWithNull) (Idx: IntDomain.Z): S with type value = Val.t and type idx = Idx.t = +struct + module MustNulls = SetDomain.Reverse (SetDomain.ToppedSet (IntDomain.BigInt) (struct let topname = "No Nulls" end)) + module MayNulls = SetDomain.ToppedSet (IntDomain.BigInt) (struct let topname = "All Null" end) + (* (Must Null Set, May Null Set, Array Size) *) + include Lattice.Prod3 (MustNulls) (MayNulls) (Idx) + + let name () = "arrays containing null bytes" + type idx = Idx.t + type value = Val.t + + let domain_of_t _ = NullByteDomain + + let get ?(checkBounds=true) (ask: VDQ.t) (must_nulls_set, _, size) (e, i) = + let rec all_indexes_must_null i max = + if Z.gt i max then + true + else if MustNulls.exists (Z.equal i) must_nulls_set then + all_indexes_must_null (Z.add i Z.one) max + else + false in + let min_i = match Idx.minimal i with + | Some min -> + if Z.lt min Z.zero then + Z.zero (* assume worst case minimal index *) + else + min + | None -> Z.zero in (* assume worst case minimal index *) + let max_i = Idx.maximal i in + + (* warn if index is (potentially) out of bounds *) + if checkBounds then (array_oob_check (module Idx) (must_nulls_set, size) (e, i)); + match max_i, Idx.minimal size with + (* if there is no maximum number in interval, return top of value *) + | None, _ -> Val.top () + | Some max, Some min_size when Z.geq max Z.zero && Z.lt max min_size -> + (* else only return null if all numbers in interval are in must null index set *) + if all_indexes_must_null min_i max then + Val.null () + else + Val.top () + (* if maximum number in interval is invalid, i.e. negative, return top of value *) + | _ -> Val.top () + + let set (ask: VDQ.t) (must_nulls_set, may_nulls_set, size) (e, i) v = + let rec add_indexes i max may_nulls_set = + if Z.gt i max then + may_nulls_set + else + add_indexes (Z.add i Z.one) max (MayNulls.add i may_nulls_set) in + let rec remove_indexes i max must_nulls_set = + if Z.gt i max then + may_nulls_set + else + remove_indexes (Z.add i Z.one) max (MustNulls.remove i must_nulls_set) in + let min_of_natural_number num = + match Idx.minimal num with + | Some min -> + if Z.lt min Z.zero then + Z.zero (* assume worst case minimal index *) + else + min + | None -> Z.zero in (* assume worst case moptionimal index *) + let min_size = min_of_natural_number size in + let min_i = min_of_natural_number i in + let max_i = Idx.maximal i in + + (* warn if index is (potentially) out of bounds *) + array_oob_check (module Idx) (must_nulls_set, size) (e, i); + match max_i, Val.is_null v with + (* if no maximum number in interval and value = null, modify may_nulls_set to top = all possible indexes < size *) + | None, true -> (must_nulls_set, MayNulls.top (), size) + (* if no maximum number in interval and value != null, modify must_nulls_set to top = empty set *) + | None, false -> (MustNulls.top (), may_nulls_set, size) + (* if value = null *) + | Some max, true when Z.geq max Z.zero -> + begin match Idx.maximal size with + | Some max_size -> + (* ... and i is exact number < size, add i to must_nulls_set and may_nulls_set *) + if Z.equal min_i max && Z.lt min_i min_size then + (MustNulls.add min_i must_nulls_set, MayNulls.add min_i may_nulls_set, size) + (* ... and i is exact number in size interval, add i only to may_nulls_set *) + else if Z.equal min_i max && Z.lt min_i max_size then + (must_nulls_set, MayNulls.add min_i may_nulls_set, size) + (* ... and i is exact number >= size, warn and return tuple unmodified *) + else if Z.equal min_i max then + (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Write operation outside of array bounds"; + (must_nulls_set, may_nulls_set, size)) + (* ... and i is interval with lower bound = 0 and upper bound in size interval, modify may_nulls_set to top *) + else if Z.equal min_i Z.zero && Z.equal max (Z.sub max_size Z.one) then + (must_nulls_set, MayNulls.top (), size) + (* ... and i is interval with lower bound = 0 and upper bound >= size, warn and modify may_nulls_set to top *) + else if Z.equal min_i Z.zero && Z.geq max max_size then + (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Write operation outside of array bounds"; + (must_nulls_set, MayNulls.top (), size)) + (* ... and i is interval with lower bound > 0 and upper bound >= size, warn and add all indexes from interval lower bound to size to may_nulls_set *) + else if Z.geq max max_size then + (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Write operation outside of array bounds"; + (must_nulls_set, add_indexes min_i max_size may_nulls_set, size)) + (* ... and i is interval with upper bound < size, add all indexes of interval to may_nulls_set*) + else + (must_nulls_set, add_indexes min_i max may_nulls_set, size) + (* ..., size has no upper limit *) + | None -> + (* ... and i is exact number < minimal size, add i to must_nulls_set and may_nulls_set *) + if Z.equal min_i max && Z.lt min_i min_size then + (MustNulls.add min_i must_nulls_set, MayNulls.add min_i may_nulls_set, size) + (* ... and i is exact number >= minimal size, add i to may_nulls_set only *) + else if Z.equal min_i max then + (must_nulls_set, MayNulls.add min_i may_nulls_set, size) + (* ... and i is interval, add all indexes of interval to may_nulls_set *) + else + (must_nulls_set, add_indexes min_i max may_nulls_set, size) + end + (* if value != null *) + | Some max, false when Z.geq max Z.zero -> + begin match Idx.maximal size with + | Some max_size -> + (* ... and i is exact number < size, remove i from must_nulls_set and may_nulls_set *) + if Z.equal min_i max && Z.lt min_i min_size then + (MustNulls.remove min_i must_nulls_set, MayNulls.remove min_i may_nulls_set, size) + (* ... and i is exact number in size interval, remove i only from must_nulls_set *) + else if Z.equal min_i max && Z.lt min_i max_size then + (MustNulls.remove min_i must_nulls_set, may_nulls_set, size) + (* ... and i is exact number >= size, warn and return tuple unmodified *) + else if Z.equal min_i max then + (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Write operation outside of array bounds"; + (must_nulls_set, may_nulls_set, size)) + (* ... and i is interval with lower bound = 0 and upper bound = size, modify must_nulls_set to top *) + else if Z.equal min_i Z.zero && Z.equal max max_size then + (MustNulls.top (), may_nulls_set, size) + (* ... and i is interval with lower bound = 0 and upper bound >= size, warn and modify must_nulls_set to top *) + else if Z.equal min_i Z.zero && Z.geq max max_size then + (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Write operation outside of array bounds"; + (MustNulls.top (), may_nulls_set, size)) + (* ... and i is interval with lower bound > 0 and upper bound >= size, warn and remove all indexes from interval lower bound to size from must_nulls_set *) + else if Z.geq max max_size then + (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Write operation outside of array bounds"; + (remove_indexes min_i max_size must_nulls_set, may_nulls_set, size)) + (* ... and i is interval with upper bound < size, remove all indexes of interval from must_nulls_set *) + else + (remove_indexes min_i max must_nulls_set, may_nulls_set, size) + (* ..., size is unlimited *) + | None -> + (* ... and i is exact number < minimal size, remove i from must_nulls_set and may_nulls_set *) + if Z.equal min_i max && Z.lt min_i min_size then + (MustNulls.remove min_i must_nulls_set, MayNulls.remove min_i may_nulls_set, size) + (* ... and i is exact number >= minimal size, remove i from must_nulls_set only *) + else if Z.equal min_i max then + (MustNulls.remove min_i must_nulls_set, may_nulls_set, size) + (* ... and i is interval, remove all indexes from interval of must_nulls_set *) + else + (remove_indexes min_i max must_nulls_set, may_nulls_set, size) + end + (* if maximum number in interval is invalid, i.e. negative, return tuple unmodified *) + | _ -> (must_nulls_set, may_nulls_set, size) + + let make ?(varAttr=[]) ?(typAttr=[]) i v = + let min_i, max_i = match Idx.minimal i, Idx.maximal i with + | Some min, Some max -> + if Z.lt min Z.zero && Z.lt max Z.zero then + (M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.before_start "Tries to create an array of negative size"; + Z.zero, Some Z.zero) + else if Z.lt min Z.zero then + (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.before_start "May try to create an array of negative size"; + Z.zero, Some max) + else + min, Some max + | None, Some max -> + if Z.lt max Z.zero then + (M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.before_start "Tries to create an array of negative size"; + Z.zero, Some Z.zero) + else + Z.zero, Some max + | Some min, None -> + if Z.lt min Z.zero then + (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.before_start "May try to create an array of negative size"; + Z.zero, None) + else + min, None + | None, None -> Z.zero, None in + match max_i, Val.is_null v with + (* if value = null, return (bot = all indexes up to minimal size - 1, top = all indexes up to maximal size - 1, size) *) + | Some max, true -> (MustNulls.bot (), MayNulls.top (), Idx.of_interval !Cil.kindOfSizeOf (min_i, max)) + | None, true -> (MustNulls.bot (), MayNulls.top (), Idx.starting !Cil.kindOfSizeOf min_i) + (* if value != null, return (top = no indexes, bot = no indexes, size) *) + | Some max, false -> (MustNulls.top (), MayNulls.bot (), Idx.of_interval !Cil.kindOfSizeOf (min_i, max)) + | None, false -> (MustNulls.top (), MayNulls.bot (), Idx.starting !Cil.kindOfSizeOf min_i) + + let length (_, _, size) = Some size + + let move_if_affected ?(replace_with_const=false) _ sets_and_size _ _ = sets_and_size + + let get_vars_in_e _ = [] + + let map f (must_nulls_set, may_nulls_set, size) = + (* if f(null) = null, all values in must_nulls_set still are surely null; + * assume top for may_nulls_set as checking effect of for every possible value is unfeasbile*) + if Val.is_null (f (Val.null ())) then + (must_nulls_set, MayNulls.top (), size) + (* else also return top for must_nulls_set *) + else + (MustNulls.top (), MayNulls.top (), size) + + (* TODO: check there is no smarter implementation -- problem is domain doesn't work on values but Z.t / idx for size *) + let fold_left f acc _ = f acc (Val.top ()) + + let smart_join _ _ = join + let smart_widen _ _ = widen + let smart_leq _ _ = leq + + (* string functions *) + let to_string (must_nulls_set, may_nulls_set, size) = + (* if must_nulls_set and min_nulls_set empty, definitely no null byte in array => warn about certain buffer overflow and return tuple unchanged *) + if MustNulls.is_empty must_nulls_set && MayNulls.is_empty may_nulls_set then + (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array access past end: buffer overflow"; + (must_nulls_set, may_nulls_set, size)) + (* if only must_nulls_set empty, no certainty about array containing null byte => warn about potential buffer overflow and return tuple unchanged *) + else if MustNulls.is_empty must_nulls_set then + (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "May access array past end: potential buffer overflow"; + must_nulls_set, may_nulls_set, size) + else + let min_must_null = MustNulls.min_elt must_nulls_set in + (* if smallest index in sets coincides, only this null byte is kept in both sets *) + if Z.equal min_must_null (MayNulls.min_elt may_nulls_set) then + (MustNulls.singleton min_must_null, MayNulls.singleton min_must_null, Idx.of_int !Cil.kindOfSizeOf (Z.add min_must_null Z.one)) + (* else return empty must_nulls_set and keep every index up to smallest index of must_nulls_set included in may_nulls_set *) + else + (MustNulls.empty (), MayNulls.filter (Z.geq min_must_null) may_nulls_set, Idx.of_int !Cil.kindOfSizeOf (Z.add min_must_null Z.one)) + + let to_n_string (must_nulls_set, may_nulls_set, size) n = + let rec add_indexes i max may_nulls_set = + if Z.geq i max then + may_nulls_set + else + add_indexes (Z.add i Z.one) max (MayNulls.add i may_nulls_set) in + let update_must_indexes min_must_null must_nulls_set = + if Z.equal min_must_null Z.zero then + MustNulls.bot () + else + (* if strlen < n, every byte starting from min_must_null is surely also transformed to null *) + add_indexes min_must_null (Z.of_int n) (MustNulls.filter (Z.gt (Z.of_int n)) must_nulls_set) in + let update_may_indexes min_may_null may_nulls_set = + if Z.equal min_may_null Z.zero then + MayNulls.top () + else + (* if strlen < n, every byte starting from may_must_null may be transformed to null *) + add_indexes min_may_null (Z.of_int n) (MayNulls.filter (Z.gt (Z.of_int n)) may_nulls_set) in + let warn_no_null min_null = + if Z.geq min_null (Z.of_int n) then + M.warn "Resulting string might not be null-terminated because src doesn't contain a null byte in the first n bytes" in + + if n < 0 then + (MustNulls.top (), MayNulls.top (), Idx.top_of !Cil.kindOfSizeOf) + else + let check_n = match Idx.minimal size, Idx.maximal size with + | Some min, Some max -> + if Z.gt (Z.of_int n) max then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array size is smaller than n bytes; can cause a buffer overflow" + else if Z.gt (Z.of_int n) min then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array size might be smaller than n bytes; can cause a buffer overflow" + | Some min, None -> + if Z.gt (Z.of_int n) min then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array size might be smaller than n bytes; can cause a buffer overflow" + | None, Some max -> + if Z.gt (Z.of_int n) max then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array size is smaller than n bytes; can cause a buffer overflow" + | None, None -> () in + check_n; + (* if definitely no null byte in array, i.e. must_nulls_set = may_nulls_set = empty set *) + if MustNulls.is_empty must_nulls_set && MayNulls.is_empty may_nulls_set then + match Idx.minimal size with + (* ... there *may* be null bytes from minimal size to n - 1 if minimal size < n *) + | Some min when Z.geq min Z.zero -> (must_nulls_set, add_indexes min (Z.of_int n) may_nulls_set, Idx.of_int !Cil.kindOfSizeOf (Z.of_int n)) + | _ -> (must_nulls_set, may_nulls_set, Idx.of_int !Cil.kindOfSizeOf (Z.of_int n)) + (* if only must_nulls_set empty, remove indexes >= n and add all indexes from min_may_null to n - 1 to may_nulls_set; + * warn if resulting array may not contain null byte *) + else if MustNulls.is_empty must_nulls_set then + let min_may_null = MayNulls.min_elt may_nulls_set in + warn_no_null min_may_null; + (must_nulls_set, update_may_indexes min_may_null may_nulls_set, Idx.of_int !Cil.kindOfSizeOf (Z.of_int n)) + else + let min_must_null = MustNulls.min_elt must_nulls_set in + let min_may_null = MayNulls.min_elt may_nulls_set in + warn_no_null min_may_null; + (* if smallest index in sets coincides, remove indexes >= n and add all indexes from min_null to n - 1 to both sets; + * warn if resulting array may not contain null byte *) + if Z.equal min_must_null min_may_null then + (update_must_indexes min_must_null must_nulls_set, update_may_indexes min_may_null may_nulls_set, Idx.of_int !Cil.kindOfSizeOf (Z.of_int n)) + (* else return empty must_nulls_set, remove indexes >= n and add all indexes from min_may_null to n - 1 to may_nulls_set; + * warn if resulting array may not contain null byte *) + else + (MustNulls.empty (), update_may_indexes min_may_null may_nulls_set, Idx.of_int !Cil.kindOfSizeOf (Z.of_int n)) + + let to_string_length (must_nulls_set, may_nulls_set, size) = + (* if must_nulls_set and min_nulls_set empty, definitely no null byte in array => return interval [size, inf) *) + if MustNulls.is_empty must_nulls_set && MayNulls.is_empty may_nulls_set then + match Idx.minimal size with + | Some min -> Idx.starting !Cil.kindOfSizeOf min + | None -> Idx.starting !Cil.kindOfSizeOf Z.zero + (* if only must_nulls_set empty, no guarantee that null ever encountered in array => return interval [minimal may null, inf) *) + else if MustNulls.is_empty must_nulls_set then + Idx.starting !Cil.kindOfSizeOf (MayNulls.min_elt may_nulls_set) + (* else return interval [minimal may null, minimal must null] *) + else + Idx.of_interval !Cil.kindOfSizeOf (MustNulls.min_elt must_nulls_set, MayNulls.min_elt may_nulls_set) + + (* TODO: copy and resize + * filter out any index before size of string src, then union and keep size of dest *) + let string_copy (must_nulls_set1, may_nulls_set1, size1) ar2 = function + (* strcpy *) + | None -> + let must_nulls_set2, may_nulls_set2, size2 = to_string ar2 in + let strlen2 = to_string_length ar2 in + (* filter out indexes before strlen(src) from dest sets and after strlen(src) from src sets and build union, keep size of dest *) + begin match Idx.minimal size1, Idx.maximal size1, Idx.minimal strlen2, Idx.maximal strlen2 with + | Some min1, Some max1, Some min2, Some max2 -> + let warn = + if Z.leq max1 min2 then + M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src is greater than the allocated size for dest" + else if Z.leq min1 max2 then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src may be greater than the allocated size for dest" in + warn; + let must_nulls_set_result = MustNulls.union (MustNulls.filter (Z.geq max2) must_nulls_set1) (MustNulls.filter (Z.leq min1) must_nulls_set2) in + let may_nulls_set_result = MayNulls.union (MayNulls.filter (Z.gt min2) may_nulls_set1) (MayNulls.filter (Z.leq max1) may_nulls_set2) in + (must_nulls_set_result, may_nulls_set_result, size1) + | Some min1, None, Some min2, Some max2 -> + let warn = + if Z.leq min1 max2 then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src may be greater than the allocated size for dest" in + warn; + let must_nulls_set_result = MustNulls.union (MustNulls.filter (Z.geq max2) must_nulls_set1) (MustNulls.filter (Z.leq min1) must_nulls_set2) in + let may_nulls_set_result = MayNulls.union (MayNulls.filter (Z.gt min2) may_nulls_set1) may_nulls_set2 in + (must_nulls_set_result, may_nulls_set_result, size1) + | Some min1, Some max1, Some min2, None -> + let warn = + if Z.leq max1 min2 then + M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src is greater than the allocated size for dest" + else if Z.leq min1 min2 then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src may be greater than the allocated size for dest" in + warn; + let must_nulls_set_result = MustNulls.filter (Z.leq min1) must_nulls_set2 in + let may_nulls_set_result = MayNulls.union (MayNulls.filter (Z.gt min2) may_nulls_set1) (MayNulls.filter (Z.leq max1) may_nulls_set2) in + (must_nulls_set_result, may_nulls_set_result, size1) + | Some min1, None, Some min2, None -> + let warn = + if Z.leq min1 min2 then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src may be greater than the allocated size for dest" in + warn; + let must_nulls_set_result = MustNulls.filter (Z.leq min1) must_nulls_set2 in + let may_nulls_set_result = MayNulls.union (MayNulls.filter (Z.gt min2) may_nulls_set1) may_nulls_set2 in + (must_nulls_set_result, may_nulls_set_result, size1) + (* any other case shouldn't happen as minimal index is always >= 0 *) + | _ -> (MustNulls.top (), MayNulls.top (), size1) + end + (* strncpy => strlen(src) is precise number *) + | Some n -> + let must_nulls_set2, may_nulls_set2, _ = to_n_string ar2 n in + (* filter out indexes before strlen(src) from dest sets and after strlen(src) from src sets and build union, keep size of dest *) + begin match Idx.minimal size1, Idx.maximal size1 with + | Some min1, Some max1 -> + let warn = + if Z.lt max1 (Z.of_int n) then + M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src is greater than the allocated size for dest" + else if Z.lt min1 (Z.of_int n) then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src may be greater than the allocated size for dest" in + warn; + let must_nulls_set_result = MustNulls.union (MustNulls.filter (Z.geq (Z.of_int n)) must_nulls_set1) (MustNulls.filter (Z.leq min1) must_nulls_set2) in + let may_nulls_set_result = MayNulls.union (MayNulls.filter (Z.geq (Z.of_int n)) may_nulls_set1) (MayNulls.filter (Z.leq max1) may_nulls_set2) in + (must_nulls_set_result, may_nulls_set_result, size1) + | Some min1, None -> + let warn = + if Z.lt min1 (Z.of_int n) then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src may be greater than the allocated size for dest" in + warn; + let must_nulls_set_result = MustNulls.union (MustNulls.filter (Z.geq (Z.of_int n)) must_nulls_set1) (MustNulls.filter (Z.leq min1) must_nulls_set2) in + let may_nulls_set_result = MayNulls.union (MayNulls.filter (Z.geq (Z.of_int n)) may_nulls_set1) may_nulls_set2 in + (must_nulls_set_result, may_nulls_set_result, size1) + (* any other case shouldn't happen as minimal index is always >= 0 *) + | _ -> (MustNulls.top (), MayNulls.top (), size1) + end + + let string_concat (must_nulls_set1, may_nulls_set1, size1) (must_nulls_set2, may_nulls_set2, size2) n = + let update_sets min1 max1 max1_exists minlen1 maxlen1 maxlen1_exists minlen2 maxlen2 maxlen2_exists must_nulls_set2' may_nulls_set2' = + (* track any potential buffer overflow and issue warning if needed *) + let warn = + if max1_exists && ((maxlen1_exists && maxlen2_exists && Z.leq max1 (Z.add maxlen1 maxlen2)) + || (maxlen1_exists && Z.leq max1 (Z.add maxlen1 minlen2)) || (maxlen2_exists && Z.leq max1 (Z.add minlen1 maxlen2)) + || Z.leq max1 (Z.add minlen1 minlen2)) then + M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of the concatenation of the strings in src and dest is greater than the allocated size for dest" + else if (maxlen1_exists && maxlen2_exists && Z.leq min1 (Z.add maxlen1 maxlen2)) || (maxlen1_exists && Z.leq min1 (Z.add maxlen1 minlen2)) + || (maxlen2_exists && Z.leq min1 (Z.add minlen1 maxlen2)) || Z.leq min1 (Z.add minlen1 minlen2) then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of the conctenation of the strings in src and dest may be greater than the allocated size for dest" in + warn; + (* if any must_nulls_set empty, result must_nulls_set also empty; + * for all i1, i2 in may_nulls_set1, may_nulls_set2: add i1 + i2 if it is <= strlen(dest) + strlen(src) to new may_nulls_set + * and keep indexes > strlen(dest) + strlen(src) of may_nulls_set *) + if MustNulls.is_empty must_nulls_set1 || MustNulls.is_empty must_nulls_set2' then + let may_nulls_set_result = + MayNulls.filter (Z.geq (Z.add minlen1 minlen2)) may_nulls_set1 + |> MayNulls.elements + |> BatList.cartesian_product (MayNulls.elements may_nulls_set2') + |> List.map (fun (i1, i2) -> Z.add i1 i2) + |> MayNulls.of_list + |> MayNulls.union (MayNulls.filter (Z.lt (Z.add minlen1 minlen2)) may_nulls_set1) + |> MayNulls.filter (fun x -> if max1_exists then Z.gt max1 x else true) in + (MustNulls.top (), may_nulls_set_result, size1) + (* if minimal must null = minimal may null in ar1 and ar2, add them and keep indexes > strlen(dest) + strlen(src) of ar1 *) + else if Z.equal (MustNulls.min_elt must_nulls_set1) (MayNulls.min_elt may_nulls_set1) then + let min_i1 = MustNulls.min_elt must_nulls_set1 in + let min_i2 = MustNulls.min_elt must_nulls_set2' in + let min_i = Z.add min_i1 min_i2 in + let must_nulls_set_result = + MustNulls.filter (fun x -> if maxlen1_exists && maxlen2_exists then Z.lt (Z.add maxlen1 maxlen2) x else false) must_nulls_set1 + |> MustNulls.add min_i + |> MustNulls.filter (Z.gt min1) in + let may_nulls_set_result = + MayNulls.filter (Z.lt (Z.add minlen1 minlen2)) may_nulls_set1 + |> MayNulls.add min_i + |> MayNulls.filter (fun x -> if max1_exists then Z.gt max1 x else true) in + (must_nulls_set_result, may_nulls_set_result, size1) + (* else only add all may nulls <= strlen(dest) + strlen(src) *) + else + let min_i2 = MustNulls.min_elt must_nulls_set2' in + let must_nulls_set_result = MustNulls.filter (fun x -> if maxlen1_exists && maxlen2_exists then Z.lt (Z.add maxlen1 maxlen2) x else false) must_nulls_set1 in + let may_nulls_set_result = + MayNulls.filter (Z.geq (Z.add minlen1 minlen2)) may_nulls_set1 + |> MayNulls.map (Z.add min_i2) + |> MayNulls.union (MayNulls.filter (Z.lt (Z.add minlen1 minlen2)) may_nulls_set1) + |> MayNulls.filter (fun x -> if max1_exists then Z.gt max1 x else true) in + (must_nulls_set_result, may_nulls_set_result, size1) in + let compute_concat must_nulls_set2' may_nulls_set2' = + let strlen1 = to_string_length (must_nulls_set1, may_nulls_set1, size1) in + let strlen2 = to_string_length (must_nulls_set2', may_nulls_set2', size2) in + begin match Idx.minimal size1, Idx.maximal size1, Idx.minimal strlen1, Idx.maximal strlen1, Idx.minimal strlen2, Idx.maximal strlen2 with + | Some min1, Some max1, Some minlen1, Some maxlen1, Some minlen2, Some maxlen2 -> + update_sets min1 max1 true minlen1 maxlen1 true minlen2 maxlen2 true must_nulls_set2' may_nulls_set2' + (* no upper bound for length of concatenation *) + | Some min1, Some max1, Some minlen1, None, Some minlen2, Some _ + | Some min1, Some max1, Some minlen1, Some _, Some minlen2, None + + | Some min1, Some max1, Some minlen1, None, Some minlen2, None -> + update_sets min1 max1 true minlen1 Z.zero false minlen2 Z.zero false must_nulls_set2' may_nulls_set2' + (* no upper bound for size of dest *) + | Some min1, None, Some minlen1, Some maxlen1, Some minlen2, Some maxlen2 -> + update_sets min1 Z.zero false minlen1 maxlen1 true minlen2 maxlen2 true must_nulls_set2' may_nulls_set2' + (* no upper bound for size of dest and length of concatenation *) + | Some min1, None, Some minlen1, None, Some minlen2, Some _ + | Some min1, None, Some minlen1, Some _, Some minlen2, None + | Some min1, None, Some minlen1, None, Some minlen2, None -> + update_sets min1 Z.zero false minlen1 Z.zero false minlen2 Z.zero false must_nulls_set2' may_nulls_set2' + (* any other case shouldn't happen as minimal index is always >= 0 *) + | _ -> (MustNulls.top (), MayNulls.top (), size1) + end in + + match n with + (* strcat *) + | None -> + let must_nulls_set2', may_nulls_set2', _ = to_string (must_nulls_set2, may_nulls_set2, size2) in + compute_concat must_nulls_set2' may_nulls_set2' + (* strncat *) + | Some num -> + (* take at most n bytes from src; if no null byte among them, add null byte at index n *) + let must_nulls_set2', may_nulls_set2' = + let must_nulls_set2, may_nulls_set2, _ = to_string (must_nulls_set2, may_nulls_set2, size2) in + if not (MayNulls.exists (Z.gt (Z.of_int num)) may_nulls_set2) then + (MustNulls.singleton (Z.of_int num), MayNulls.singleton (Z.of_int num)) + else if not (MustNulls.exists (Z.gt (Z.of_int num)) must_nulls_set2) then + (MustNulls.empty (), MayNulls.add (Z.of_int num) (MayNulls.filter (Z.leq (Z.of_int num)) may_nulls_set2)) + else + (MustNulls.filter (Z.leq (Z.of_int num)) must_nulls_set2, MayNulls.filter (Z.leq (Z.of_int num)) may_nulls_set2) in + compute_concat must_nulls_set2' may_nulls_set2' + + (* TODO -- can I even do something useful at all? Might as well leave out substring_extraction and string_comparison *) + let substring_extraction _ _ = Some (top ()) + + (* TODO *) + let string_comparison _ _ _ = Idx.top_of IInt + + let update_length _ x = x + + let project ?(varAttr=[]) ?(typAttr=[]) _ t = t +end + module AttributeConfiguredArrayDomain(Val: LatticeWithSmartOps) (Idx:IntDomain.Z):S with type value = Val.t and type idx = Idx.t = struct module P = PartitionedWithLength(Val)(Idx) @@ -1216,14 +1466,6 @@ struct let set_i u (i,v) = U.set ask u (index_as_expression i) v in set_i (List.fold_left set_i u unrolledValues) (factor (), rest) - (* TODO! *) - let to_string _ = top () - let to_n_string _ _ _ = top () - let to_string_length _ = Idx.top_of !Cil.kindOfSizeOf - let string_concat _ _ _ = top () - let substring_extraction _ _ = Some (top ()) - let string_comparison _ _ _ = Idx.top_of IInt - let project ?(varAttr=[]) ?(typAttr=[]) ask (t:t) = match get_domain ~varAttr ~typAttr, t with | PartitionedDomain, (Some x, None) -> to_t @@ (Some x, None, None) diff --git a/src/cdomains/arrayDomain.mli b/src/cdomains/arrayDomain.mli index 0df132a8e2..5df3679cfa 100644 --- a/src/cdomains/arrayDomain.mli +++ b/src/cdomains/arrayDomain.mli @@ -2,7 +2,7 @@ open IntOps open GoblintCil module VDQ = ValueDomainQueries -type domain = TrivialDomain | PartitionedDomain | UnrolledDomain | MustNullByteDomain +type domain = TrivialDomain | PartitionedDomain | UnrolledDomain | NullByteDomain val get_domain: varAttr:Cil.attributes -> typAttr:Cil.attributes -> domain (** gets the underlying domain: chosen by the attributes in AttributeConfiguredArrayDomain *) @@ -55,34 +55,42 @@ sig val smart_widen: (Cil.exp -> BigIntOps.t option) -> (Cil.exp -> BigIntOps.t option) -> t -> t -> t val smart_leq: (Cil.exp -> BigIntOps.t option) -> (Cil.exp -> BigIntOps.t option) -> t -> t -> bool val update_length: idx -> t -> t + val project: ?varAttr:Cil.attributes -> ?typAttr:Cil.attributes -> VDQ.t -> t -> t +end + +(** Abstract domains representing strings a.k.a. null-terminated char arrays. *) +module type Str = +sig + include S val to_string: t -> t (** Returns an abstract value with at most one null byte marking the end of the string *) - val to_n_string: t -> int -> bool -> t + val to_n_string: t -> int -> t (** [to_n_string index_set n no_null_warn] returns an abstract value with a potential null * byte marking the end of the string and if needed followed by further null bytes to obtain - * an n bytes string. If the resulting value doesn't surely contain a terminating null_byte, - * issue a warning if [no_null_warn] is true. *) + * an n bytes string. *) val to_string_length: t -> idx (** Returns length of string represented by input abstract value *) + val string_copy: t -> t -> int option -> t + (** [string_copy dest src n] returns an abstract value representing the copy of string [src] + * into array [dest], taking at most [n] bytes of [src] if present *) + val string_concat: t -> t -> int option -> t (** [string_concat s1 s2 n] returns a new abstract value representing the string * concatenation of the input abstract values [s1] and [s2], taking at most [n] bytes of * [s2] if present *) - val substring_extraction: t -> t -> t option - (** [substring_extraction haystack needle] returns None if the string represented by the - * abstract value [needle] surely isn't a substring of [haystack], else Some (top) *) + val substring_extraction: t -> t -> t + (** [substring_extraction haystack needle] returns null if the string represented by the + * abstract value [needle] surely isn't a substring of [haystack], else top *) val string_comparison: t -> t -> int option -> idx (** [string_comparison s1 s2 n] returns a negative / positive idx element if the string * represented by [s1] is less / greater than the one by [s2] or zero if they are equal; * only compares the first [n] bytes if present *) - - val project: ?varAttr:Cil.attributes -> ?typAttr:Cil.attributes -> VDQ.t -> t -> t end module type LatticeWithSmartOps = @@ -93,6 +101,14 @@ sig val smart_leq: (Cil.exp -> BigIntOps.t option) -> (Cil.exp -> BigIntOps.t option) -> t -> t -> bool end +module type LatticeWithNull = +sig + include Lattice.S + val null: unit -> t + val not_null: unit -> t + val is_null: t -> bool +end + module Trivial (Val: Lattice.S) (Idx: Lattice.S): S with type value = Val.t and type idx = Idx.t (** This functor creates a trivial single cell representation of an array. The * indexing type is taken as a parameter to satisfy the type system, it is not @@ -110,17 +126,17 @@ module Partitioned (Val: LatticeWithSmartOps) (Idx: IntDomain.Z): S with type va * have a signature that allows for choosing an array representation at runtime. *) -module MustNullByte (Val: Lattice.S) (Idx: IntDomain.Z): S with type value = Val.t and type idx = Idx.t +module PartitionedWithLength (Val: LatticeWithSmartOps) (Idx:IntDomain.Z): S with type value = Val.t and type idx = Idx.t +(** Like partitioned but additionally manages the length of the array. *) + +module NullByte (Val: Lattice.S) (Idx: IntDomain.Z): S with type value = Val.t and type idx = Idx.t (** This functor creates an array representation by the indexes of all null bytes - * the array *surely* contains. This is useful to analyze strings, i.e. null- + * the array must and may contain. This is useful to analyze strings, i.e. null- * terminated char arrays, and particularly to determine if operations on strings * could lead to a buffer overflow. Concrete values from Val are not interesting - * for this domain. + * for this domain. It additionally tracks the array size. *) -module PartitionedWithLength (Val: LatticeWithSmartOps) (Idx:IntDomain.Z): S with type value = Val.t and type idx = Idx.t -(** Like partitioned but additionally manages the length of the array. *) - module AttributeConfiguredArrayDomain(Val: LatticeWithSmartOps) (Idx:IntDomain.Z):S with type value = Val.t and type idx = Idx.t (** Switches between PartitionedWithLength, TrivialWithLength and Unroll based on variable, type, and flag. - * Always runs MustNullByte in parallel. *) + * Always runs NullByte in parallel. *) diff --git a/src/util/options.schema.json b/src/util/options.schema.json index 7933b553ac..2ff2e8bf58 100644 --- a/src/util/options.schema.json +++ b/src/util/options.schema.json @@ -677,7 +677,7 @@ "description": "The domain that should be used for arrays. When employing the partition array domain, make sure to enable the expRelation analysis as well. When employing the unrolling array domain, make sure to set the ana.base.arrays.unrolling-factor >0.", "type": "string", - "enum": ["trivial", "partitioned", "unroll", "mustnullbyte"], + "enum": ["trivial", "partitioned", "unroll"], "default": "trivial" }, "unrolling-factor": { From d59b45e6f863204171d02308d549e539c3af9fc6 Mon Sep 17 00:00:00 2001 From: Nathan Schmidt Date: Tue, 30 May 2023 22:58:15 +0200 Subject: [PATCH 0053/1312] Added functions for strstr and str(n)cmp to Null Byte Domain --- src/cdomains/arrayDomain.ml | 61 ++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index c2468e885f..c4d81dfc69 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1353,11 +1353,64 @@ struct (MustNulls.filter (Z.leq (Z.of_int num)) must_nulls_set2, MayNulls.filter (Z.leq (Z.of_int num)) may_nulls_set2) in compute_concat must_nulls_set2' may_nulls_set2' - (* TODO -- can I even do something useful at all? Might as well leave out substring_extraction and string_comparison *) - let substring_extraction _ _ = Some (top ()) + let substring_extraction haystack (must_nulls_set_needle, may_nulls_set_needle, size_needle) = + (* if needle is empty string, i.e. certain null byte at index 0, return haystack as string *) + if MustNulls.mem Z.zero must_nulls_set_needle then + to_string haystack + else + let haystack_len = to_string_length haystack in + let needle_len = to_string_length (must_nulls_set_needle, may_nulls_set_needle, size_needle) in + match Idx.maximal haystack_len, Idx.minimal needle_len with + | Some haystack_max, Some needle_min -> + (* if strlen(haystack) < strlen(needle), needle can never be substring of haystack => return null pointer -- TODO: how to do that? *) + if Z.lt haystack_max needle_min then + (MustNulls.top (), MayNulls.top (), Idx.of_int !Cil.kindOfSizeOf Z.zero) + else + (MustNulls.top (), MayNulls.top (), Idx.top_of !Cil.kindOfSizeOf) + | _ -> (MustNulls.top (), MayNulls.top (), Idx.top_of !Cil.kindOfSizeOf) - (* TODO *) - let string_comparison _ _ _ = Idx.top_of IInt + let string_comparison (must_nulls_set1, may_nulls_set1, _) (must_nulls_set2, may_nulls_set2, _) = function + (* strcmp *) + | None -> + (* if s1 = s2 = empty string, i.e. certain null byte at index 0, return 0 *) + if MustNulls.mem Z.zero must_nulls_set1 && (MustNulls.mem Z.zero must_nulls_set2) then + Idx.of_int IInt Z.zero + (* if only s1 = empty string, return negative integer *) + else if MustNulls.mem Z.zero must_nulls_set1 && not (MustNulls.mem Z.zero must_nulls_set2) then + Idx.ending IInt Z.minus_one + (* if only s2 = empty string, return positive integer *) + else if MustNulls.mem Z.zero must_nulls_set2 then + Idx.starting IInt Z.one + else + (* if first null bytes are certain and have different indexes, return integer <> 0 *) + (try if Z.equal (MustNulls.min_elt must_nulls_set1) (MayNulls.min_elt may_nulls_set1) + && Z.equal (MustNulls.min_elt must_nulls_set2) (MayNulls.min_elt may_nulls_set2) + && not (Z.equal (MustNulls.min_elt must_nulls_set1) (MustNulls.min_elt must_nulls_set2)) then + Idx.join (Idx.ending IInt Z.minus_one) (Idx.starting IInt Z.one) + else + Idx.top_of IInt + with Not_found -> Idx.top_of IInt) + (* strncmp *) + | Some num -> + (* if s1 = empty and s2 = empty string or n = 0, return 0 *) + if MustNulls.mem Z.zero must_nulls_set1 && ((MustNulls.mem Z.zero must_nulls_set2) || Z.equal Z.zero (Z.of_int num)) then + Idx.of_int IInt Z.zero + (* if only s1 = empty string, return negative integer *) + else if MustNulls.mem Z.zero must_nulls_set1 && not (MustNulls.mem Z.zero must_nulls_set2) then + Idx.ending IInt Z.minus_one + (* if only s2 = empty string, return positive integer *) + else if MustNulls.mem Z.zero must_nulls_set2 then + Idx.starting IInt Z.one + else + (* if first null bytes are certain, have different indexes and are before index n for s2, return integer <> 0 *) + (try if Z.equal (MustNulls.min_elt must_nulls_set1) (MayNulls.min_elt may_nulls_set1) + && Z.equal (MustNulls.min_elt must_nulls_set2) (MayNulls.min_elt may_nulls_set2) + && Z.lt (MustNulls.min_elt must_nulls_set2) (Z.of_int num) + && not (Z.equal (MustNulls.min_elt must_nulls_set1) (MustNulls.min_elt must_nulls_set2)) then + Idx.join (Idx.ending IInt Z.minus_one) (Idx.starting IInt Z.one) + else + Idx.top_of IInt + with Not_found -> Idx.top_of IInt) let update_length _ x = x From 7a41dc40445df6d29bfc4445a2877b987828b491 Mon Sep 17 00:00:00 2001 From: Nathan Schmidt Date: Wed, 31 May 2023 11:51:49 +0200 Subject: [PATCH 0054/1312] First adaptations to AttributeConfiguredArrayDomain --- src/cdomains/arrayDomain.ml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index c4d81dfc69..3e13080ab0 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -874,7 +874,7 @@ end module type LatticeWithNull = sig - include Lattice.S + include LatticeWithSmartOps val null: unit -> t val not_null: unit -> t val is_null: t -> bool @@ -1186,9 +1186,7 @@ struct (* else return interval [minimal may null, minimal must null] *) else Idx.of_interval !Cil.kindOfSizeOf (MustNulls.min_elt must_nulls_set, MayNulls.min_elt may_nulls_set) - - (* TODO: copy and resize - * filter out any index before size of string src, then union and keep size of dest *) + let string_copy (must_nulls_set1, may_nulls_set1, size1) ar2 = function (* strcpy *) | None -> @@ -1417,11 +1415,12 @@ struct let project ?(varAttr=[]) ?(typAttr=[]) _ t = t end -module AttributeConfiguredArrayDomain(Val: LatticeWithSmartOps) (Idx:IntDomain.Z):S with type value = Val.t and type idx = Idx.t = +module AttributeConfiguredArrayDomain(Val: LatticeWithNull) (Idx:IntDomain.Z):S with type value = Val.t and type idx = Idx.t = struct module P = PartitionedWithLength(Val)(Idx) module T = TrivialWithLength(Val)(Idx) module U = UnrollWithLength(Val)(Idx) + module N = NullByte(Val)(Idx) type idx = Idx.t type value = Val.t @@ -1439,6 +1438,7 @@ struct module I = struct include LatticeFlagHelper (T) (U) (K) let name () = "" end include LatticeFlagHelper (P) (I) (K) + (* include Lattice.Prod (LatticeFlagHelper (P) (I) (K)) (N) *) let domain_of_t = function | (Some p, None) -> PartitionedDomain From 77ce08ed61dadf105783e0874b2d3f146b260da9 Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Thu, 1 Jun 2023 05:01:09 +0200 Subject: [PATCH 0055/1312] Tests for loop termination --- scripts/update_suite.rb | 6 + .../01-simple-loop-terminating.c | 15 ++ .../02-simple-loop-nonterminating.c | 12 ++ .../03-nested-loop-terminating.c | 27 ++++ .../04-nested-loop-nonterminating.c | 23 +++ .../80-termination/05-for-loop-terminating.c | 14 ++ .../06-for-loop-nonterminating.c | 10 ++ .../07-nested-for-loop-terminating.c | 20 +++ .../08-nested-for-loop-nonterminating.c | 19 +++ .../09-complex-for-loop-terminating.c | 107 +++++++++++++ .../10-complex-loop-terminating.c | 135 ++++++++++++++++ .../80-termination/11-loopless-termination.c | 7 + .../12-do-while-instant-terminating.c | 15 ++ .../80-termination/13-do-while-terminating.c | 16 ++ .../14-do-while-nonterminating.c | 16 ++ .../15-complex-loop-combination-terminating.c | 144 ++++++++++++++++++ ...16-nested-loop-nontrivial-nonterminating.c | 23 +++ 17 files changed, 609 insertions(+) create mode 100644 tests/regression/80-termination/01-simple-loop-terminating.c create mode 100644 tests/regression/80-termination/02-simple-loop-nonterminating.c create mode 100644 tests/regression/80-termination/03-nested-loop-terminating.c create mode 100644 tests/regression/80-termination/04-nested-loop-nonterminating.c create mode 100644 tests/regression/80-termination/05-for-loop-terminating.c create mode 100644 tests/regression/80-termination/06-for-loop-nonterminating.c create mode 100644 tests/regression/80-termination/07-nested-for-loop-terminating.c create mode 100644 tests/regression/80-termination/08-nested-for-loop-nonterminating.c create mode 100644 tests/regression/80-termination/09-complex-for-loop-terminating.c create mode 100644 tests/regression/80-termination/10-complex-loop-terminating.c create mode 100644 tests/regression/80-termination/11-loopless-termination.c create mode 100644 tests/regression/80-termination/12-do-while-instant-terminating.c create mode 100644 tests/regression/80-termination/13-do-while-terminating.c create mode 100644 tests/regression/80-termination/14-do-while-nonterminating.c create mode 100644 tests/regression/80-termination/15-complex-loop-combination-terminating.c create mode 100644 tests/regression/80-termination/16-nested-loop-nontrivial-nonterminating.c diff --git a/scripts/update_suite.rb b/scripts/update_suite.rb index e99068829e..dead6cd8f1 100755 --- a/scripts/update_suite.rb +++ b/scripts/update_suite.rb @@ -165,6 +165,8 @@ def collect_warnings when /^\[Error\]/ then "warn" when /^\[Info\]/ then "warn" when /^\[Success\]/ then "success" + when /^\[Terminating\]/ then "term" + when /^\[Nonterminating\]/ then "noterm" when /\[Debug\]/ then next # debug "warnings" shouldn't count as other warnings (against NOWARN) when /^ on line \d+ $/ then next # dead line warnings shouldn't count (used for unreachability with NOWARN) when /^ on lines \d+..\d+ $/ then next # dead line warnings shouldn't count (used for unreachability with NOWARN) @@ -298,6 +300,10 @@ def parse_tests (lines) tests[i] = "fail" elsif obj =~ /UNKNOWN/ then tests[i] = "unknown" + elsif obj =~ /NON?TERM/ then + tests[i] = "noterm" + elsif obj =~ /TERM/ then + tests[i] = "term" elsif obj =~ /(assert|__goblint_check).*\(/ then if obj =~ /FAIL/ then tests[i] = "fail" diff --git a/tests/regression/80-termination/01-simple-loop-terminating.c b/tests/regression/80-termination/01-simple-loop-terminating.c new file mode 100644 index 0000000000..931b125171 --- /dev/null +++ b/tests/regression/80-termination/01-simple-loop-terminating.c @@ -0,0 +1,15 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int i = 1; + + while (i <= 10) // TERM + { + printf("%d\n", i); + i++; + } + + return 0; +} diff --git a/tests/regression/80-termination/02-simple-loop-nonterminating.c b/tests/regression/80-termination/02-simple-loop-nonterminating.c new file mode 100644 index 0000000000..520a4a82e0 --- /dev/null +++ b/tests/regression/80-termination/02-simple-loop-nonterminating.c @@ -0,0 +1,12 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + while (1) // NOTERM + { + continue; + } + + return 0; +} diff --git a/tests/regression/80-termination/03-nested-loop-terminating.c b/tests/regression/80-termination/03-nested-loop-terminating.c new file mode 100644 index 0000000000..172827af42 --- /dev/null +++ b/tests/regression/80-termination/03-nested-loop-terminating.c @@ -0,0 +1,27 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int rows = 3; + int columns = 4; + int i = 1; + + // Outer while loop for rows + while (i <= rows) + { // TERM + int j = 1; + + // Inner while loop for columns + while (j <= columns) + { // TERM + printf("(%d, %d) ", i, j); + j++; + } + + printf("\n"); + i++; + } + + return 0; +} diff --git a/tests/regression/80-termination/04-nested-loop-nonterminating.c b/tests/regression/80-termination/04-nested-loop-nonterminating.c new file mode 100644 index 0000000000..37af9ed6fb --- /dev/null +++ b/tests/regression/80-termination/04-nested-loop-nonterminating.c @@ -0,0 +1,23 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int outerCount = 1; + + while (outerCount <= 3) // NOTERM + { + int innerCount = 1; + + while (1) // NOTERM + { + printf("(%d, %d) ", outerCount, innerCount); + innerCount++; + } + + printf("\n"); + outerCount++; + } + + return 0; +} diff --git a/tests/regression/80-termination/05-for-loop-terminating.c b/tests/regression/80-termination/05-for-loop-terminating.c new file mode 100644 index 0000000000..ab286a6dd4 --- /dev/null +++ b/tests/regression/80-termination/05-for-loop-terminating.c @@ -0,0 +1,14 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int i; + + for (i = 1; i <= 10; i++) // TERM + { + printf("%d\n", i); + } + + return 0; +} diff --git a/tests/regression/80-termination/06-for-loop-nonterminating.c b/tests/regression/80-termination/06-for-loop-nonterminating.c new file mode 100644 index 0000000000..466001e6e5 --- /dev/null +++ b/tests/regression/80-termination/06-for-loop-nonterminating.c @@ -0,0 +1,10 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() { + for (;;) { // NOTERM + printf("This loop does not terminate.\n"); + } + + return 0; +} diff --git a/tests/regression/80-termination/07-nested-for-loop-terminating.c b/tests/regression/80-termination/07-nested-for-loop-terminating.c new file mode 100644 index 0000000000..eec4dda908 --- /dev/null +++ b/tests/regression/80-termination/07-nested-for-loop-terminating.c @@ -0,0 +1,20 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int rows = 3; + int columns = 4; + + // Nested loop to iterate over rows and columns + for (int i = 1; i <= rows; i++) // TERM + { + for (int j = 1; j <= columns; j++) // TERM + { + printf("(%d, %d) ", i, j); + } + printf("\n"); + } + + return 0; +} diff --git a/tests/regression/80-termination/08-nested-for-loop-nonterminating.c b/tests/regression/80-termination/08-nested-for-loop-nonterminating.c new file mode 100644 index 0000000000..3f7bcb4f07 --- /dev/null +++ b/tests/regression/80-termination/08-nested-for-loop-nonterminating.c @@ -0,0 +1,19 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int outerCount, innerCount; + + for (outerCount = 1; outerCount <= 3; outerCount++) // NOTERM + { + for (innerCount = 1;; innerCount++) // NOTERM + { + printf("(%d, %d) ", outerCount, innerCount); + } + + printf("\n"); + } + + return 0; +} diff --git a/tests/regression/80-termination/09-complex-for-loop-terminating.c b/tests/regression/80-termination/09-complex-for-loop-terminating.c new file mode 100644 index 0000000000..ed28fa9b43 --- /dev/null +++ b/tests/regression/80-termination/09-complex-for-loop-terminating.c @@ -0,0 +1,107 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int i, j, k; + + // Outer loop + for (i = 1; i <= 5; i++) // TERM + { + // Inner loop 1 + for (j = 1; j <= i; j++) // TERM + { + printf("%d ", j); + } + printf("\n"); + + // Inner loop 2 + for (k = i; k >= 1; k--) // TERM + { + printf("%d ", k); + } + printf("\n"); + } + + // Additional loop + for (i = 5; i >= 1; i--) // TERM + { + for (j = i; j >= 1; j--) // TERM + { + printf("%d ", j); + } + printf("\n"); + } + + // Loop with conditions + for (i = 1; i <= 10; i++) // TERM + { + if (i % 2 == 0) + { + printf("%d is even\n", i); + } + else + { + printf("%d is odd\n", i); + } + } + + // Loop with nested conditions + for (i = 1; i <= 10; i++) // TERM + { + printf("Number: %d - ", i); + if (i < 5) + { + printf("Less than 5\n"); + } + else if (i > 5) + { + printf("Greater than 5\n"); + } + else + { + printf("Equal to 5\n"); + } + } + + // Loop with a break statement + for (i = 1; i <= 10; i++) // TERM + { + printf("%d ", i); + if (i == 5) + { + break; + } + } + printf("\n"); + + // Loop with a continue statement + for (i = 1; i <= 10; i++) // TERM + { + if (i % 2 == 0) + { + continue; + } + printf("%d ", i); + } + printf("\n"); + + // Loop with complex conditions + for (i = 1; i <= 10; i++) // TERM + { + if (i > 5 && i % 2 == 0) + { + printf("%d ", i); + } + } + printf("\n"); + + // Loop with multiple variables + int a, b, c; + for (a = 1, b = 2, c = 3; a <= 10; a++, b += 2, c += 3) // TERM + { + printf("%d %d %d\n", a, b, c); + } + + return 0; +} diff --git a/tests/regression/80-termination/10-complex-loop-terminating.c b/tests/regression/80-termination/10-complex-loop-terminating.c new file mode 100644 index 0000000000..3a19f17bee --- /dev/null +++ b/tests/regression/80-termination/10-complex-loop-terminating.c @@ -0,0 +1,135 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int i = 1; + int j = 1; + int k = 5; + + // Outer while loop + while (i <= 5) // TERM + { + // Inner while loop 1 + while (j <= i) // TERM + { + printf("%d ", j); + j++; + } + printf("\n"); + j = 1; + + // Inner while loop 2 + while (k >= 1) // TERM + { + printf("%d ", k); + k--; + } + printf("\n"); + k = 5; + + i++; + } + + // Additional while loop + i = 5; + while (i >= 1) // TERM + { + j = i; + while (j >= 1) // TERM + { + printf("%d ", j); + j--; + } + printf("\n"); + i--; + } + + // Loop with conditions + i = 1; + while (i <= 10) // TERM + { + if (i % 2 == 0) + { + printf("%d is even\n", i); + } + else + { + printf("%d is odd\n", i); + } + i++; + } + + // Loop with nested conditions + i = 1; + while (i <= 10) // TERM + { + printf("Number: %d - ", i); + if (i < 5) + { + printf("Less than 5\n"); + } + else if (i > 5) + { + printf("Greater than 5\n"); + } + else + { + printf("Equal to 5\n"); + } + i++; + } + + // Loop with a break statement + i = 1; + while (i <= 10) // TERM + { + printf("%d ", i); + if (i == 5) + { + break; + } + i++; + } + printf("\n"); + + // Loop with a continue statement + i = 1; + while (i <= 10) // TERM + { + if (i % 2 == 0) + { + i++; + continue; + } + printf("%d ", i); + i++; + } + printf("\n"); + + // Loop with complex conditions + i = 1; + while (i <= 10) // TERM + { + if (i > 5 && i % 2 == 0) + { + printf("%d ", i); + } + i++; + } + printf("\n"); + + // Loop with multiple variables + int a = 1; + int b = 2; + int c = 3; + while (a <= 10) // TERM + { + printf("%d %d %d\n", a, b, c); + a++; + b += 2; + c += 3; + } + + return 0; +} diff --git a/tests/regression/80-termination/11-loopless-termination.c b/tests/regression/80-termination/11-loopless-termination.c new file mode 100644 index 0000000000..b118e65e35 --- /dev/null +++ b/tests/regression/80-termination/11-loopless-termination.c @@ -0,0 +1,7 @@ +// TERM +#include + +int main() { + printf("Terminating code without a loop\n"); + return 0; +} diff --git a/tests/regression/80-termination/12-do-while-instant-terminating.c b/tests/regression/80-termination/12-do-while-instant-terminating.c new file mode 100644 index 0000000000..cc3cc41edc --- /dev/null +++ b/tests/regression/80-termination/12-do-while-instant-terminating.c @@ -0,0 +1,15 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int i = 0; + + do // TERM + { + printf("Inside the do-while loop\n"); + } while (i > 0); + + printf("Exited the loop\n"); + return 0; +} diff --git a/tests/regression/80-termination/13-do-while-terminating.c b/tests/regression/80-termination/13-do-while-terminating.c new file mode 100644 index 0000000000..05fe270f04 --- /dev/null +++ b/tests/regression/80-termination/13-do-while-terminating.c @@ -0,0 +1,16 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int i = 1; + + do // TERM + { + printf("Inside the do-while loop\n"); + i++; + } while (i <= 5); + + printf("Exited the loop\n"); + return 0; +} diff --git a/tests/regression/80-termination/14-do-while-nonterminating.c b/tests/regression/80-termination/14-do-while-nonterminating.c new file mode 100644 index 0000000000..1c70d4fc76 --- /dev/null +++ b/tests/regression/80-termination/14-do-while-nonterminating.c @@ -0,0 +1,16 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int i = 1; + + do // NOTERM + { + printf("Inside the do-while loop\n"); + i++; + } while (i >= 2); + + printf("Exited the loop\n"); + return 0; +} diff --git a/tests/regression/80-termination/15-complex-loop-combination-terminating.c b/tests/regression/80-termination/15-complex-loop-combination-terminating.c new file mode 100644 index 0000000000..54f8cd97c8 --- /dev/null +++ b/tests/regression/80-termination/15-complex-loop-combination-terminating.c @@ -0,0 +1,144 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + // Non-nested loops + int i; + + // for loop + for (i = 1; i <= 10; i++) // TERM + { + printf("For loop iteration: %d\n", i); + } + + // while loop + int j = 1; + while (j <= 10) // TERM + { + printf("While loop iteration: %d\n", j); + j++; + } + + // do-while loop + int k = 1; + do // TERM + { + printf("Do-While loop iteration: %d\n", k); + k++; + } while (k <= 10); + + // Nested loops + int a, b; + + // Nested for and while loop + for (a = 1; a <= 5; a++) // TERM + { + int c = 1; + while (c <= a) // TERM + { + printf("Nested For-While loop: %d\n", c); + c++; + } + } + + // Nested while and do-while loop + int x = 1; + while (x <= 5) // TERM + { + int y = 1; + do // TERM + { + printf("Nested While-Do-While loop: %d\n", y); + y++; + } while (y <= x); + x++; + } + + // Nested do-while and for loop + int p = 1; + do // TERM + { + for (int q = 1; q <= p; q++) // TERM + { + printf("Nested Do-While-For loop: %d\n", q); + } + p++; + } while (p <= 5); + + // Additional loops + int m; + + // Nested while loop with a break statement + int n = 1; + while (n <= 5) // TERM + { + printf("Outer While loop iteration: %d\n", n); + m = 1; + while (1) // TERM + { + printf("Inner While loop iteration: %d\n", m); + m++; + if (m == 4) + { + break; + } + } + n++; + } + + // Loop with a continue statement + for (int r = 1; r <= 10; r++) // TERM + { + if (r % 3 == 0) + { + continue; + } + printf("Loop with Continue: %d\n", r); + } + + // Loop with multiple conditions + int s = 1; + while (s <= 10 && s % 2 == 0) // TERM + { + printf("Loop with Multiple Conditions: %d\n", s); + s++; + } + + // Loop with multiple variables + int t, u; + for (t = 1, u = 10; t <= 5 && u >= 5; t++, u--) // TERM + { + printf("Loop with Multiple Variables: %d %d\n", t, u); + } + + // Loop with nested conditions + for (int v = 1; v <= 10; v++) // TERM + { + printf("Loop with Nested Conditions: %d - ", v); + if (v < 5) + { + printf("Less than 5\n"); + } + else if (v > 5) + { + printf("Greater than 5\n"); + } + else + { + printf("Equal to 5\n"); + } + } + + /* // Loop with a label and goto statement + int w = 1; +start: + if (w <= 5) + { + printf("Loop with Label and Goto: %d\n", w); + w++; + goto start; // TERM + } */ + + return 0; +} diff --git a/tests/regression/80-termination/16-nested-loop-nontrivial-nonterminating.c b/tests/regression/80-termination/16-nested-loop-nontrivial-nonterminating.c new file mode 100644 index 0000000000..855fbd0dca --- /dev/null +++ b/tests/regression/80-termination/16-nested-loop-nontrivial-nonterminating.c @@ -0,0 +1,23 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int outerCount = 1; + + while (outerCount <= 3) // NOTERM + { + int innerCount = 1; + + while (outerCount < 3 || innerCount > 0) // NOTERM + { + printf("(%d, %d) ", outerCount, innerCount); + innerCount++; + } + + printf("\n"); + outerCount++; + } + + return 0; +} From af5bf08d647ada99e283a2f8594e55751c06a2a9 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Thu, 1 Jun 2023 11:34:01 +0200 Subject: [PATCH 0056/1312] Remove debug output --- src/analyses/termination_new.ml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index 1128365963..87effec842 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -57,7 +57,6 @@ struct (* provides information to Goblint*) let query ctx (type a) (q: a Queries.t): a Queries.result = - print_endline @@ ""^(!loopExit.vname); let open Queries in match q with | Queries.MustTermLoop v when check_bounded ctx v -> From 4e2298278a6a8f4afaf31e600446dd8bade91d9f Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Fri, 2 Jun 2023 13:14:15 +0200 Subject: [PATCH 0057/1312] Recognize every assignment to the loop counter var This way we catch GOTOs into loops except for the case when the loop guard evaluates to false already the first time. --- src/analyses/termination_new.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index 87effec842..4b1dbb6a84 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -43,7 +43,7 @@ struct (* Detect loop counter variable assignment to 0 *) match lval, rval with (* Assume that the following loop does not terminate *) - (Var x, NoOffset), zero when is_loop_counter_var x -> + (Var x, NoOffset), _ when is_loop_counter_var x -> D.add x false ctx.local (* Loop exit: Check whether loop counter variable is bounded *) | (Var y, NoOffset), Lval (Var x, NoOffset) when is_loop_exit_indicator y -> From 1ad86f20d5db844b6e715e498f75bbf7438b6e67 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Fri, 2 Jun 2023 13:21:38 +0200 Subject: [PATCH 0058/1312] Remove unused open --- src/analyses/termination_new.ml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index 4b1dbb6a84..936471ceaf 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -3,7 +3,6 @@ open Analyses open GoblintCil open TerminationPreprocessing -open Printf exception PreProcessing of string From 837c7979255d41b5ed47f49eee05845a16139e68 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Sat, 3 Jun 2023 13:29:17 +0200 Subject: [PATCH 0059/1312] first tests for recursion termination analysis, added empty functor and GMapG --- runningGob.sh | 22 ++++++++--- src/analyses/termination_new.ml | 2 - src/framework/analyses.ml | 53 ++++++++++++++++++++++++++ src/framework/constraints.ml | 66 ++++++++++++++++++++++++++++++++- src/framework/control.ml | 1 + 5 files changed, 135 insertions(+), 9 deletions(-) diff --git a/runningGob.sh b/runningGob.sh index f765b5afab..e3b5a6da45 100755 --- a/runningGob.sh +++ b/runningGob.sh @@ -1,22 +1,32 @@ #!/bin/bash -make +#make #make install # set options and file for apron execution -options_apron="--set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --enable warn.debug" #note: preprocessing first needs to be added to apron +options_apron="--set ana.activated[+] apron --enable ana.int.interval --set --enable warn.debug" #note: preprocessing first needs to be added to apron options_signs="--set "ana.activated[+]" signs --enable warn.debug" options_term="--set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra" -cfile_loops="tests/regression/55-loop-unrolling/01-simple-cases.c" -cfile_signs="tests/regression/99-tutorials/01-first.c" +cfile_loops="tests/regression/55-loop-unrolling/01-simple-cases.c" +cfile_nonTerm="tests/regression/80-termination/02-simple-loop-nonterminating.c" +cfile_signs="tests/regression/99-tutorials/01-first.c" +cfile_deadCode="tests/regression/80-termination/16-nested-loop-nontrivial-nonterminating.c" # run analysis, write cil output to file and enable visualization via html #./goblint $cfile_loops $options_apron --enable justcil > output.txt #./goblint $cfile_loops $options_apron --html # run analysis, write cil output to file and enable visualization via html -./goblint $cfile_loops $options_term --enable justcil > output.txt -./goblint $cfile_loops $options_term --html +#./goblint $cfile_loops $options_term --enable justcil > output.txt +#./goblint $cfile_loops $options_term --html + +# run analysis, write cil output to file and enable visualization via html +./goblint $cfile_deadCode $options_term --enable justcil > output.txt +./goblint $cfile_deadCode $options_term --html + +# run analysis, write cil output to file and enable visualization via html +#./goblint $cfile_nonTerm $options_term --enable justcil > output.txt +#./goblint $cfile_nonTerm $options_term --html # set up server to see visualizatino python3 -m http.server --directory result 8081 diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index 1128365963..90c1004033 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -3,7 +3,6 @@ open Analyses open GoblintCil open TerminationPreprocessing -open Printf exception PreProcessing of string @@ -57,7 +56,6 @@ struct (* provides information to Goblint*) let query ctx (type a) (q: a Queries.t): a Queries.result = - print_endline @@ ""^(!loopExit.vname); let open Queries in match q with | Queries.MustTermLoop v when check_bounded ctx v -> diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 1a3a4ebeb1..c8d3873085 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -119,6 +119,59 @@ struct | x -> BatPrintf.fprintf f "%a" printXml x end +module GMapG (G: Lattice.S) (C: Printable.S) = +struct + module CVal = + struct + include Printable.Std (* To make it Groupable *) + include SetDomain.Make ( + struct + include C + let printXml f c = BatPrintf.fprintf f "%a" printXml c (* wrap in for HTML printing *) + end + ) + let name () = "contextsMap" + end + + module RangeVal = + struct + include SetDomain.Make ( + struct + include C (*TODO: sollte hier iwi ein tupel sein*) + let printXml f c = BatPrintf.fprintf f "%a" printXml c (* wrap in for HTML printing *) + end + ) + let name () = "contextsMap" + end + + module CMap = + struct + include MapDomain.MapBot (CVal) (RangeVal) + let name () = "contextsMap" + end + include Lattice.Lift2 (G) (CMap) (Printable.DefaultNames) + + let is_bot () = false + let is_top () = false + + (*let spec = function + | `Bot -> G.bot () + | `Lifted1 x -> x + | _ -> failwith "GVarG.spec" + let contexts = function + | `Bot -> CSet.bot () + | `Lifted2 x -> x + | _ -> failwith "GVarG.contexts" + let create_spec spec = `Lifted1 spec + let create_contexts contexts = `Lifted2 contexts + + let printXml f = function + | `Lifted1 x -> G.printXml f x + | `Lifted2 x -> BatPrintf.fprintf f "%a" CSet.printXml x + | x -> BatPrintf.fprintf f "%a" printXml x +*) +end + exception Deadcode diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 740d1f85a9..e9c1b9b0a2 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1343,7 +1343,7 @@ struct module EM = struct include MapDomain.MapBot (Basetype.CilExp) (Basetype.Bools) - let name () = "branches" + let name () = "bmodule Vranches" end module G = @@ -1692,6 +1692,70 @@ struct let event ctx e octx = S.event (conv ctx) e (conv octx) end +(** Add cycle detection in the function call graph to a analysis *) +module RecursionTermLifter (S: Spec) + : Spec with module D = S.D + and module G = S.G + and module C = S.C + and module G = GMapG (S.G) (S.C) += + +struct + module C = S.C + module P = S.P + module D = S.D + + (*global invariant + - fundec -> Map (S.C) (Set (fundec * S.C)) + So: g -> {c' -> f, c} + in case f, c --> g, c' *) + + (*module CVal = + struct + include C + include Printable.Std (* To make it Groupable *) + let printXml f c = BatPrintf.fprintf f "%a" printXml c (* wrap in for HTML printing *) + end + module M = MapDomain.MapBot (CVal) (CVal) +*) + module V = S.V + module G = S.G(*GMapG (S.G) (S.C)*) + (*struct + include Lattice.Prod (S.G) (M) + let printXml f (d,m) = BatPrintf.fprintf f "\n%a\n%a\n" S.G.printXml d M.printXml m + end*) + let name () = "RecursionTerm (" ^ S.name () ^ ")" + + type marshal = S.marshal + let init = S.init + let finalize = S.finalize (*TODO*) + + let startstate v = S.startstate v + let exitstate v = S.exitstate v + let morphstate = S.morphstate + + let context = S.context + + let query ctx = S.query (ctx) + let branch ctx = S.branch (ctx) + let assign ctx = S.assign (ctx) + let vdecl ctx = S.vdecl (ctx) + let enter ctx = S.enter (ctx) (*TODO*) + let paths_as_set ctx = S.paths_as_set (ctx) + let body ctx = S.body (ctx) + let return ctx = S.return (ctx) + let combine_env ctx = S.combine_env (ctx) + let combine_assign ctx = S.combine_assign (ctx) + let special ctx = S.special (ctx) + let threadenter ctx = S.threadenter (ctx) + let threadspawn ctx lv f args fctx = S.threadspawn (ctx) lv f args (fctx) + let sync ctx = S.sync (ctx) + let skip ctx = S.skip (ctx) + let asm ctx = S.asm (ctx) + let event ctx e octx = S.event (ctx) e (octx) +end + + module CompareGlobSys (SpecSys: SpecSys) = struct open SpecSys diff --git a/src/framework/control.ml b/src/framework/control.ml index 35cadfc12d..bd26fa7129 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -36,6 +36,7 @@ let spec_module: (module Spec) Lazy.t = lazy ( Also must be outside of deadcode, because deadcode splits (like mutex lock event) don't pass on tokens. *) |> lift (get_bool "ana.widen.tokens") (module WideningTokens.Lifter) |> lift true (module LongjmpLifter) + |> lift true (module RecursionTermLifter)(*TODO: should we really always evaluate it???*) ) in GobConfig.building_spec := false; ControlSpecC.control_spec_c := (module S1.C); From f4416fcfe35c1964c2064e45fefea06efd9f88eb Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Sat, 3 Jun 2023 13:31:15 +0200 Subject: [PATCH 0060/1312] now its working :) --- src/framework/constraints.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index e9c1b9b0a2..0586a87d3e 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1697,7 +1697,7 @@ module RecursionTermLifter (S: Spec) : Spec with module D = S.D and module G = S.G and module C = S.C - and module G = GMapG (S.G) (S.C) + and module G = S.G = struct From 4eacdf0c6f82ba33a9bf9de924952ec3ed70c6e1 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Sat, 3 Jun 2023 13:34:02 +0200 Subject: [PATCH 0061/1312] added GMapG --- src/framework/analyses.ml | 53 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 1a3a4ebeb1..1d1972ac45 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -120,6 +120,59 @@ struct end +module GMapG (G: Lattice.S) (C: Printable.S) = +struct + module CVal = + struct + include Printable.Std (* To make it Groupable *) + include SetDomain.Make ( + struct + include C + let printXml f c = BatPrintf.fprintf f "%a" printXml c (* wrap in for HTML printing *) + end + ) + let name () = "contextsMap" + end + + module RangeVal = + struct + include SetDomain.Make ( + struct + include C (*TODO: sollte hier iwi ein tupel sein*) + let printXml f c = BatPrintf.fprintf f "%a" printXml c (* wrap in for HTML printing *) + end + ) + let name () = "contextsMap" + end + + module CMap = + struct + include MapDomain.MapBot (CVal) (RangeVal) + let name () = "contextsMap" + end + include Lattice.Lift2 (G) (CMap) (Printable.DefaultNames) + + let is_bot () = false + let is_top () = false + + (*let spec = function + | `Bot -> G.bot () + | `Lifted1 x -> x + | _ -> failwith "GVarG.spec" + let contexts = function + | `Bot -> CSet.bot () + | `Lifted2 x -> x + | _ -> failwith "GVarG.contexts" + let create_spec spec = `Lifted1 spec + let create_contexts contexts = `Lifted2 contexts + + let printXml f = function + | `Lifted1 x -> G.printXml f x + | `Lifted2 x -> BatPrintf.fprintf f "%a" CSet.printXml x + | x -> BatPrintf.fprintf f "%a" printXml x +*) +end + exception Deadcode (** [Dom (D)] produces D lifted where bottom means dead-code *) From 3d411ea7148a25c0bb4db60600f6630f12cf71d9 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Sat, 3 Jun 2023 13:35:32 +0200 Subject: [PATCH 0062/1312] added functor definition (is empty) --- src/framework/constraints.ml | 65 ++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 740d1f85a9..7a4a6037b0 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1692,6 +1692,71 @@ struct let event ctx e octx = S.event (conv ctx) e (conv octx) end + +(** Add cycle detection in the function call graph to a analysis *) +module RecursionTermLifter (S: Spec) + : Spec with module D = S.D + and module G = S.G + and module C = S.C + and module G = S.G += + +struct + module C = S.C + module P = S.P + module D = S.D + + (*global invariant + - fundec -> Map (S.C) (Set (fundec * S.C)) + So: g -> {c' -> f, c} + in case f, c --> g, c' *) + + (*module CVal = + struct + include C + include Printable.Std (* To make it Groupable *) + let printXml f c = BatPrintf.fprintf f "%a" printXml c (* wrap in for HTML printing *) + end + module M = MapDomain.MapBot (CVal) (CVal) +*) + module V = S.V + module G = S.G(*GMapG (S.G) (S.C)*) + (*struct + include Lattice.Prod (S.G) (M) + let printXml f (d,m) = BatPrintf.fprintf f "\n%a\n%a\n" S.G.printXml d M.printXml m + end*) + let name () = "RecursionTerm (" ^ S.name () ^ ")" + + type marshal = S.marshal + let init = S.init + let finalize = S.finalize (*TODO*) + + let startstate v = S.startstate v + let exitstate v = S.exitstate v + let morphstate = S.morphstate + + let context = S.context + + let query ctx = S.query (ctx) + let branch ctx = S.branch (ctx) + let assign ctx = S.assign (ctx) + let vdecl ctx = S.vdecl (ctx) + let enter ctx = S.enter (ctx) (*TODO*) + let paths_as_set ctx = S.paths_as_set (ctx) + let body ctx = S.body (ctx) + let return ctx = S.return (ctx) + let combine_env ctx = S.combine_env (ctx) + let combine_assign ctx = S.combine_assign (ctx) + let special ctx = S.special (ctx) + let threadenter ctx = S.threadenter (ctx) + let threadspawn ctx lv f args fctx = S.threadspawn (ctx) lv f args (fctx) + let sync ctx = S.sync (ctx) + let skip ctx = S.skip (ctx) + let asm ctx = S.asm (ctx) + let event ctx e octx = S.event (ctx) e (octx) +end + + module CompareGlobSys (SpecSys: SpecSys) = struct open SpecSys From 99e85b06025eea7ca61b71c224d1d7d774ace458 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Sat, 3 Jun 2023 13:36:14 +0200 Subject: [PATCH 0063/1312] added funtctor --- src/framework/control.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/framework/control.ml b/src/framework/control.ml index 35cadfc12d..bd26fa7129 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -36,6 +36,7 @@ let spec_module: (module Spec) Lazy.t = lazy ( Also must be outside of deadcode, because deadcode splits (like mutex lock event) don't pass on tokens. *) |> lift (get_bool "ana.widen.tokens") (module WideningTokens.Lifter) |> lift true (module LongjmpLifter) + |> lift true (module RecursionTermLifter)(*TODO: should we really always evaluate it???*) ) in GobConfig.building_spec := false; ControlSpecC.control_spec_c := (module S1.C); From 9f4d19ee482cfffd225013d24d1e3e83186464e0 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Sat, 3 Jun 2023 14:03:53 +0200 Subject: [PATCH 0064/1312] reverted the changes, wrong branch :) --- src/framework/analyses.ml | 54 ------------------------------ src/framework/constraints.ml | 64 ------------------------------------ src/framework/control.ml | 3 +- 3 files changed, 1 insertion(+), 120 deletions(-) diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index c8d3873085..7ac18f56f7 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -119,60 +119,6 @@ struct | x -> BatPrintf.fprintf f "%a" printXml x end -module GMapG (G: Lattice.S) (C: Printable.S) = -struct - module CVal = - struct - include Printable.Std (* To make it Groupable *) - include SetDomain.Make ( - struct - include C - let printXml f c = BatPrintf.fprintf f "%a" printXml c (* wrap in for HTML printing *) - end - ) - let name () = "contextsMap" - end - - module RangeVal = - struct - include SetDomain.Make ( - struct - include C (*TODO: sollte hier iwi ein tupel sein*) - let printXml f c = BatPrintf.fprintf f "%a" printXml c (* wrap in for HTML printing *) - end - ) - let name () = "contextsMap" - end - - module CMap = - struct - include MapDomain.MapBot (CVal) (RangeVal) - let name () = "contextsMap" - end - include Lattice.Lift2 (G) (CMap) (Printable.DefaultNames) - - let is_bot () = false - let is_top () = false - - (*let spec = function - | `Bot -> G.bot () - | `Lifted1 x -> x - | _ -> failwith "GVarG.spec" - let contexts = function - | `Bot -> CSet.bot () - | `Lifted2 x -> x - | _ -> failwith "GVarG.contexts" - let create_spec spec = `Lifted1 spec - let create_contexts contexts = `Lifted2 contexts - - let printXml f = function - | `Lifted1 x -> G.printXml f x - | `Lifted2 x -> BatPrintf.fprintf f "%a" CSet.printXml x - | x -> BatPrintf.fprintf f "%a" printXml x -*) -end - - exception Deadcode (** [Dom (D)] produces D lifted where bottom means dead-code *) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 0586a87d3e..d8b186160b 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1692,70 +1692,6 @@ struct let event ctx e octx = S.event (conv ctx) e (conv octx) end -(** Add cycle detection in the function call graph to a analysis *) -module RecursionTermLifter (S: Spec) - : Spec with module D = S.D - and module G = S.G - and module C = S.C - and module G = S.G -= - -struct - module C = S.C - module P = S.P - module D = S.D - - (*global invariant - - fundec -> Map (S.C) (Set (fundec * S.C)) - So: g -> {c' -> f, c} - in case f, c --> g, c' *) - - (*module CVal = - struct - include C - include Printable.Std (* To make it Groupable *) - let printXml f c = BatPrintf.fprintf f "%a" printXml c (* wrap in for HTML printing *) - end - module M = MapDomain.MapBot (CVal) (CVal) -*) - module V = S.V - module G = S.G(*GMapG (S.G) (S.C)*) - (*struct - include Lattice.Prod (S.G) (M) - let printXml f (d,m) = BatPrintf.fprintf f "\n%a\n%a\n" S.G.printXml d M.printXml m - end*) - let name () = "RecursionTerm (" ^ S.name () ^ ")" - - type marshal = S.marshal - let init = S.init - let finalize = S.finalize (*TODO*) - - let startstate v = S.startstate v - let exitstate v = S.exitstate v - let morphstate = S.morphstate - - let context = S.context - - let query ctx = S.query (ctx) - let branch ctx = S.branch (ctx) - let assign ctx = S.assign (ctx) - let vdecl ctx = S.vdecl (ctx) - let enter ctx = S.enter (ctx) (*TODO*) - let paths_as_set ctx = S.paths_as_set (ctx) - let body ctx = S.body (ctx) - let return ctx = S.return (ctx) - let combine_env ctx = S.combine_env (ctx) - let combine_assign ctx = S.combine_assign (ctx) - let special ctx = S.special (ctx) - let threadenter ctx = S.threadenter (ctx) - let threadspawn ctx lv f args fctx = S.threadspawn (ctx) lv f args (fctx) - let sync ctx = S.sync (ctx) - let skip ctx = S.skip (ctx) - let asm ctx = S.asm (ctx) - let event ctx e octx = S.event (ctx) e (octx) -end - - module CompareGlobSys (SpecSys: SpecSys) = struct open SpecSys diff --git a/src/framework/control.ml b/src/framework/control.ml index bd26fa7129..7e993733cd 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -36,8 +36,7 @@ let spec_module: (module Spec) Lazy.t = lazy ( Also must be outside of deadcode, because deadcode splits (like mutex lock event) don't pass on tokens. *) |> lift (get_bool "ana.widen.tokens") (module WideningTokens.Lifter) |> lift true (module LongjmpLifter) - |> lift true (module RecursionTermLifter)(*TODO: should we really always evaluate it???*) - ) in + ) in GobConfig.building_spec := false; ControlSpecC.control_spec_c := (module S1.C); (module S1) From d77b877e52ba40fd73ba5d2fd17069c9f3ab81c2 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Sat, 3 Jun 2023 14:05:00 +0200 Subject: [PATCH 0065/1312] fixed a typo --- src/framework/constraints.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index d8b186160b..740d1f85a9 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1343,7 +1343,7 @@ struct module EM = struct include MapDomain.MapBot (Basetype.CilExp) (Basetype.Bools) - let name () = "bmodule Vranches" + let name () = "branches" end module G = From 84cfb5bc5b35f57d1685bf47a803f9fee96d4a9c Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Sun, 4 Jun 2023 16:00:38 +0200 Subject: [PATCH 0066/1312] first tests for constraints --- output.txt | 204 +++++++++++++++++++++----------- src/framework/constraints.ml | 218 ++++++++++++++++++++++++++++++++++- 2 files changed, 348 insertions(+), 74 deletions(-) diff --git a/output.txt b/output.txt index d3b021a1f3..07c71d61b9 100644 --- a/output.txt +++ b/output.txt @@ -1096,31 +1096,35 @@ void example1(void) { int a[5] ; int i ; - int term27_5 = 0; + int term27_5-file_01-simple-cases ; { #line 25 i = 0; { +#line 27 + term27_5-file_01-simple-cases = 0; { #line 27 while (1) { -#line 27 - term27_5 ++; while_continue: /* CIL Label */ ; #line 27 if (! (i < 5)) { #line 27 goto while_break; } +#line 27 + term27_5-file_01-simple-cases ++; #line 28 a[i] = i; #line 29 i ++; } - } while_break: /* CIL Label */ ; } +#line 27 + term_exit- = term27_5-file_01-simple-cases; + } #line 32 __goblint_check(a[0] == 0); #line 33 @@ -1134,31 +1138,35 @@ void example2(void) { int a[5] ; int i ; - int term42_5 = 0; + int term42_5-file_01-simple-cases ; { #line 40 i = 0; { +#line 42 + term42_5-file_01-simple-cases = 0; { #line 42 while (1) { -#line 42 - term42_5 ++; while_continue: /* CIL Label */ ; #line 43 a[i] = i; #line 44 i ++; +#line 42 + term42_5-file_01-simple-cases ++; #line 42 if (! (i <= 5)) { #line 42 goto while_break; } } - } while_break: /* CIL Label */ ; } +#line 42 + term_exit- = term42_5-file_01-simple-cases; + } #line 47 __goblint_check(a[0] == 0); #line 48 @@ -1172,31 +1180,35 @@ void example3(void) { int a[10] ; int i ; - int term57_5 = 0; + int term57_5-file_01-simple-cases ; { #line 55 i = 0; { +#line 57 + term57_5-file_01-simple-cases = 0; { #line 57 while (1) { -#line 57 - term57_5 ++; while_continue: /* CIL Label */ ; #line 57 if (! (i < 5)) { #line 57 goto while_break; } +#line 57 + term57_5-file_01-simple-cases ++; #line 58 a[i] = i; #line 59 i ++; } - } while_break: /* CIL Label */ ; } +#line 57 + term_exit- = term57_5-file_01-simple-cases; + } #line 62 __goblint_check(a[0] == 0); #line 63 @@ -1213,7 +1225,7 @@ void example4(void) int a[10] ; int i ; int first_iteration ; - int term74_5 = 0; + int term74_5-file_01-simple-cases ; { #line 71 @@ -1221,17 +1233,19 @@ void example4(void) #line 72 first_iteration = 1; { +#line 74 + term74_5-file_01-simple-cases = 0; { #line 74 while (1) { -#line 74 - term74_5 ++; while_continue: /* CIL Label */ ; #line 74 if (! (i < 10)) { #line 74 goto while_break; } +#line 74 + term74_5-file_01-simple-cases ++; #line 75 if (first_iteration == 1) { #line 75 @@ -1249,9 +1263,11 @@ void example4(void) #line 79 i ++; } - } while_break: /* CIL Label */ ; } +#line 74 + term_exit- = term74_5-file_01-simple-cases; + } #line 82 __goblint_check(a[0] == 0); #line 83 @@ -1266,7 +1282,7 @@ void example5(void) int a[4] ; int i ; int top ; - int term95_5 = 0; + int term95_5-file_01-simple-cases ; { #line 92 @@ -1274,17 +1290,19 @@ void example5(void) #line 93 top = 0; { +#line 95 + term95_5-file_01-simple-cases = 0; { #line 95 while (1) { -#line 95 - term95_5 ++; while_continue: /* CIL Label */ ; #line 95 if (! (i < 4)) { #line 95 goto while_break; } +#line 95 + term95_5-file_01-simple-cases ++; #line 96 a[i] = 0; #line 97 @@ -1300,9 +1318,11 @@ void example5(void) #line 104 i ++; } - } while_break: /* CIL Label */ ; } +#line 95 + term_exit- = term95_5-file_01-simple-cases; + } #line 107 __goblint_check(a[0] == 0); #line 108 @@ -1319,7 +1339,7 @@ void example6(void) int a[5] ; int i ; int top ; - int term119_5 = 0; + int term119_5-file_01-simple-cases ; { #line 116 @@ -1327,17 +1347,19 @@ void example6(void) #line 117 top = 0; { +#line 119 + term119_5-file_01-simple-cases = 0; { #line 119 while (1) { -#line 119 - term119_5 ++; while_continue: /* CIL Label */ ; #line 119 if (! (i < 3)) { #line 119 goto while_break; } +#line 119 + term119_5-file_01-simple-cases ++; #line 120 a[i] = 0; #line 121 @@ -1345,9 +1367,11 @@ void example6(void) #line 122 i ++; } - } while_break: /* CIL Label */ ; } +#line 119 + term_exit- = term119_5-file_01-simple-cases; + } #line 125 __goblint_check(a[0] == 0); #line 126 @@ -1380,20 +1404,22 @@ void example7(void) int a[10] ; int i ; int tmp ; - int term143_2 = 0; + int term143_2-file_01-simple-cases ; { #line 142 i = 0; { +#line 143 + term143_2-file_01-simple-cases = 0; { #line 143 while (1) { -#line 143 - term143_2 ++; while_continue: /* CIL Label */ ; #line 143 tmp = update(i); +#line 143 + term143_2-file_01-simple-cases ++; #line 143 if (! tmp) { #line 143 @@ -1404,9 +1430,11 @@ void example7(void) #line 145 i ++; } - } while_break: /* CIL Label */ ; } +#line 143 + term_exit- = term143_2-file_01-simple-cases; + } #line 147 __goblint_check(a[0] == 0); #line 148 @@ -1422,8 +1450,8 @@ void example8(void) int b[5] ; int i ; int j ; - int term160_9 = 0; - int term157_2 = 0; + int term160_9-file_01-simple-cases ; + int term157_2-file_01-simple-cases ; { #line 155 @@ -1439,47 +1467,55 @@ void example8(void) #line 156 i = 0; { +#line 157 + term157_2-file_01-simple-cases = 0; { #line 157 while (1) { -#line 157 - term157_2 ++; while_continue: /* CIL Label */ ; #line 157 if (! (i < 5)) { #line 157 goto while_break; } +#line 157 + term157_2-file_01-simple-cases ++; #line 158 a[i] = i; #line 159 j = 0; { +#line 160 + term160_9-file_01-simple-cases = 0; { #line 160 while (1) { -#line 160 - term160_9 ++; while_continue___0: /* CIL Label */ ; #line 160 if (! (j < 5)) { #line 160 goto while_break___0; } +#line 160 + term160_9-file_01-simple-cases ++; #line 161 b[j] += a[i]; #line 162 j ++; } - } while_break___0: /* CIL Label */ ; } +#line 160 + term_exit- = term160_9-file_01-simple-cases; + } #line 164 i ++; } - } while_break: /* CIL Label */ ; } +#line 157 + term_exit- = term157_2-file_01-simple-cases; + } #line 166 return; } @@ -1489,23 +1525,25 @@ void example9(void) { int a[5] ; int i ; - int term174_2 = 0; + int term174_2-file_01-simple-cases ; { #line 173 i = 0; { +#line 174 + term174_2-file_01-simple-cases = 0; { #line 174 while (1) { -#line 174 - term174_2 ++; while_continue: /* CIL Label */ ; #line 174 if (! 1) { #line 174 goto while_break; } +#line 174 + term174_2-file_01-simple-cases ++; #line 175 a[i] = i; #line 176 @@ -1516,9 +1554,11 @@ void example9(void) goto while_break; } } - } while_break: /* CIL Label */ ; } +#line 174 + term_exit- = term174_2-file_01-simple-cases; + } #line 179 return; } @@ -1528,23 +1568,25 @@ void example10(void) { int a[5] ; int i ; - int term187_2 = 0; + int term187_2-file_01-simple-cases ; { #line 186 i = 0; { +#line 187 + term187_2-file_01-simple-cases = 0; { #line 187 while (1) { -#line 187 - term187_2 ++; while_continue: /* CIL Label */ ; #line 187 if (! (i < 5)) { #line 187 goto while_break; } +#line 187 + term187_2-file_01-simple-cases ++; #line 188 if (i == 3) { #line 189 @@ -1557,9 +1599,11 @@ void example10(void) #line 193 i ++; } - } while_break: /* CIL Label */ ; } +#line 187 + term_exit- = term187_2-file_01-simple-cases; + } #line 195 return; } @@ -1990,99 +2034,113 @@ void qsort(void *ptr , size_t count , size_t size , int (*comp)(void const * , char *a ; char *b ; char c ; - int term10_5 = 0; - int term9_3 = 0; - int term21_9 = 0; - int term17_5 = 0; - int term16_3 = 0; + int term10_5-file_stdlib ; + int term9_3-file_stdlib ; + int term21_9-file_stdlib ; + int term17_5-file_stdlib ; + int term16_3-file_stdlib ; { #line 9 i = (size_t )0; { +#line 9 + term9_3-file_stdlib = 0; { #line 9 while (1) { -#line 9 - term9_3 ++; while_continue: /* CIL Label */ ; #line 9 if (! (i < count)) { #line 9 goto while_break; } +#line 9 + term9_3-file_stdlib ++; #line 10 j = (size_t )0; { +#line 10 + term10_5-file_stdlib = 0; { #line 10 while (1) { -#line 10 - term10_5 ++; while_continue___0: /* CIL Label */ ; #line 10 if (! (j < count)) { #line 10 goto while_break___0; } +#line 10 + term10_5-file_stdlib ++; #line 11 (*comp)((void const *)(ptr + i * size), (void const *)(ptr + j * size)); #line 10 j ++; } - } while_break___0: /* CIL Label */ ; } +#line 10 + term_exit- = term10_5-file_stdlib; + } #line 9 i ++; } - } while_break: /* CIL Label */ ; } +#line 9 + term_exit- = term9_3-file_stdlib; + } #line 16 i___0 = (size_t )0; { +#line 16 + term16_3-file_stdlib = 0; { #line 16 while (1) { -#line 16 - term16_3 ++; while_continue___1: /* CIL Label */ ; #line 16 if (! (i___0 < count)) { #line 16 goto while_break___1; } +#line 16 + term16_3-file_stdlib ++; #line 17 j___0 = (size_t )0; { +#line 17 + term17_5-file_stdlib = 0; { #line 17 while (1) { -#line 17 - term17_5 ++; while_continue___2: /* CIL Label */ ; #line 17 if (! (j___0 < count)) { #line 17 goto while_break___2; } +#line 17 + term17_5-file_stdlib ++; #line 19 if (r) { #line 21 k = (size_t )0; { +#line 21 + term21_9-file_stdlib = 0; { #line 21 while (1) { -#line 21 - term21_9 ++; while_continue___3: /* CIL Label */ ; #line 21 if (! (k < size)) { #line 21 goto while_break___3; } +#line 21 + term21_9-file_stdlib ++; #line 22 a = (char *)((ptr + i___0 * size) + k); #line 23 @@ -2096,22 +2154,28 @@ void qsort(void *ptr , size_t count , size_t size , int (*comp)(void const * , #line 21 k ++; } - } while_break___3: /* CIL Label */ ; } +#line 21 + term_exit- = term21_9-file_stdlib; + } } #line 17 j___0 ++; } - } while_break___2: /* CIL Label */ ; } +#line 17 + term_exit- = term17_5-file_stdlib; + } #line 16 i___0 ++; } - } while_break___1: /* CIL Label */ ; } +#line 16 + term_exit- = term16_3-file_stdlib; + } #line 33 return; } @@ -2129,23 +2193,25 @@ void *bsearch(void const *key , void const *ptr , size_t count , size_t size size_t i ; void const *a ; int tmp ; - int term40_3 = 0; + int term40_3-file_stdlib ; { #line 40 i = (size_t )0; { +#line 40 + term40_3-file_stdlib = 0; { #line 40 while (1) { -#line 40 - term40_3 ++; while_continue: /* CIL Label */ ; #line 40 if (! (i < count)) { #line 40 goto while_break; } +#line 40 + term40_3-file_stdlib ++; #line 41 a = ptr + i * size; #line 42 @@ -2158,9 +2224,11 @@ void *bsearch(void const *key , void const *ptr , size_t count , size_t size #line 40 i ++; } - } while_break: /* CIL Label */ ; } +#line 40 + term_exit- = term40_3-file_stdlib; + } #line 47 return ((void *)0); } diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 7a4a6037b0..0177f3ea85 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -6,6 +6,7 @@ open GoblintCil open MyCFG open Analyses open GobConfig +include Printf module M = Messages @@ -562,7 +563,7 @@ struct let side_context sideg f c = if !AnalysisState.postsolving then sideg (GVar.contexts f) (G.create_contexts (G.CSet.singleton c)) - + let common_ctx var edge prev_node pval (getl:lv -> ld) sidel getg sideg : (D.t, S.G.t, S.C.t, S.V.t) ctx * D.t list ref * (lval option * varinfo * exp list * D.t) list ref = let r = ref [] in let spawns = ref [] in @@ -1692,6 +1693,194 @@ struct let event ctx e octx = S.event (conv ctx) e (conv octx) end +(** +module RecursionTermLifter (S: Spec): Spec = +struct + include S + + let name () = "RecursionTerm (" ^ S.name () ^ ")" + + (* global invariant: + - fundec -> (S.C -> Set (fundec * S.C)) -- used to detect loops in the call graph *) + + module V = + struct + include Printable.Option (S.V) (struct let name = "RecursionTerm" end) + let name () = "RecursionTerm" + let is_write_only t = true + let s x = `Left x + end + + module C = + struct + include S.C + include Printable.Std (* To make it Groupable *) + let printXml f c = BatPrintf.fprintf f "%a" printXml c (* wrap in for HTML printing *) + + end + + (*module Tuple = struct + type t = (fundec, S.C) [@@deriving eq, ord, hash] + let equal t1 t2 = false + let compare t1 t3 = 0 + let show t = "t" + let pretty () (x: t) = match x with _ -> . + + let printXml f (d,m) = BatPrintf.fprintf f "%a" printXml c (* wrap in for HTML printing *) + + let name u = "recursion" + let to_yojson (x: t) = match x with _ -> . + + let tag t = 1 + let arbitrary () = failwith "Printable.Empty.arbitrary" + + let relift t = t + end +*) + module Tupel (S:Spec) = + struct + include Printable.Std + type t = fundec * S.C.t [@@deriving eq, ord, hash] + + let equal_fundec = false + let hash_fundec = false + + let name () = "recursion" + + let pretty () (x: t) = match x with _ -> . + + let relift (f, c) = + (f, c) + + let equal t1 t2 = false + let compare t1 t3 = 0 + let show t = "t" + + let printXml f c = BatPrintf.fprintf f "%a" c (* wrap in for HTML printing *) + + let name u = "recursion" + let to_yojson (x: t) = match x with _ -> . + + let tag t = 1 + let arbitrary () = failwith "Printable.Empty.arbitrary" + end + + module T = + struct + include SetDomain.Make (Tupel (S)) + end + + module EM = + struct + include MapDomain.MapBot (C) (T) + let name () = "recursions" + end + + module G = + struct + include Lattice.Lift2 (S.G) (EM) (Printable.DefaultNames) + let name () = "recursionTerm" + let node = function + | `Bot -> EM.bot () + | `Lifted2 x -> x + | _ -> failwith "DeadBranchLifter.node" + let create_s s = `Lifted1 s + let create_node node = `Lifted2 node + + let printXml f = function + | `Lifted1 x -> S.G.printXml f x + | `Lifted2 x -> BatPrintf.fprintf f "%a" EM.printXml x + | x -> BatPrintf.fprintf f "%a" printXml x + end + + let conv (ctx: (_, G.t, _, V.t) ctx): (_, S.G.t, _, S.V.t) ctx = + { ctx with + global = (fun v -> G.s (ctx.global (V.s v))); + sideg = (fun v g -> ctx.sideg (V.s v) (G.create_s g)); + } + + let query ctx (type a) (q: a Queries.t): a Queries.result = + match q with + | WarnGlobal g -> + let g: V.t = Obj.obj g in + begin match g with + | `Left g -> + S.query (conv ctx) (WarnGlobal (Obj.repr g)) + | `Right g -> + let em = G.node (ctx.global (V.node g)) in + EM.iter (fun exp tv -> + match tv with + | `Lifted tv -> + let loc = Node.location g in (* TODO: looking up location now doesn't work nicely with incremental *) + let cilinserted = if loc.synthetic then "(possibly inserted by CIL) " else "" in + M.warn ~loc:(Node g) ~tags:[CWE (if tv then 571 else 570)] ~category:Deadcode "condition '%a' %sis always %B" d_exp exp cilinserted tv + | `Bot when not (CilType.Exp.equal exp one) -> (* all branches dead *) + M.error ~loc:(Node g) ~category:Analyzer ~tags:[Category Unsound] "both branches over condition '%a' are dead" d_exp exp + | `Bot (* all branches dead, fine at our inserted Neg(1)-s because no Pos(1) *) + | `Top -> (* may be both true and false *) + () + ) em; + end + | InvariantGlobal g -> + let g: V.t = Obj.obj g in + begin match g with + | `Left g -> + S.query (conv ctx) (InvariantGlobal (Obj.repr g)) + | `Right g -> + Queries.Result.top q + end + | IterSysVars (vq, vf) -> + (* vars for S *) + let vf' x = vf (Obj.repr (V.s (Obj.obj x))) in + S.query (conv ctx) (IterSysVars (vq, vf')); + + (* node vars for dead branches *) + begin match vq with + | Node {node; _} -> + vf (Obj.repr (V.node node)) + | _ -> + () + end + | _ -> + S.query (conv ctx) q + + + let branch ctx = S.branch (conv ctx) + + let branch ctx exp tv = + if !AnalysisState.postsolving then ( + try + let r = branch ctx exp tv in + (* branch is live *) + ctx.sideg (V.node ctx.prev_node) (G.create_node (EM.singleton exp (`Lifted tv))); (* record expression with reached tv *) + r + with Deadcode -> + (* branch is dead *) + ctx.sideg (V.node ctx.prev_node) (G.create_node (EM.singleton exp `Bot)); (* record expression without reached tv *) + raise Deadcode + ) + else ( + ctx.sideg (V.node ctx.prev_node) (G.create_node (EM.bot ())); (* create global variable during solving, to allow postsolving leq hack to pass verify *) + branch ctx exp tv + ) + + let assign ctx = S.assign (conv ctx) + let vdecl ctx = S.vdecl (conv ctx) + let enter ctx = S.enter (conv ctx) + let paths_as_set ctx = S.paths_as_set (conv ctx) + let body ctx = S.body (conv ctx) + let return ctx = S.return (conv ctx) + let combine_env ctx = S.combine_env (conv ctx) + let combine_assign ctx = S.combine_assign (conv ctx) + let special ctx = S.special (conv ctx) + let threadenter ctx = S.threadenter (conv ctx) + let threadspawn ctx lv f args fctx = S.threadspawn (conv ctx) lv f args (conv fctx) + let sync ctx = S.sync (conv ctx) + let skip ctx = S.skip (conv ctx) + let asm ctx = S.asm (conv ctx) + let event ctx e octx = S.event (conv ctx) e (conv octx) +end + (** Add cycle detection in the function call graph to a analysis *) module RecursionTermLifter (S: Spec) @@ -1711,7 +1900,10 @@ struct So: g -> {c' -> f, c} in case f, c --> g, c' *) - (*module CVal = + (* + + + module CVal = struct include C include Printable.Std (* To make it Groupable *) @@ -1719,8 +1911,15 @@ struct end module M = MapDomain.MapBot (CVal) (CVal) *) - module V = S.V - module G = S.G(*GMapG (S.G) (S.C)*) + module V = (*TODO: do I need to change V???*) + struct + include Printable.Option (S.V) (struct let name = "RecursionTerm" end) + let name () = "RecursionTerm" + let is_write_only t = true + let s x = `Left x + end + module G = S.G + (*GMapG (S.G) (S.C)*) (*struct include Lattice.Prod (S.G) (M) let printXml f (d,m) = BatPrintf.fprintf f "\n%a\n%a\n" S.G.printXml d M.printXml m @@ -1737,11 +1936,18 @@ struct let context = S.context + (**let side_context sideg f c = + if !AnalysisState.postsolving then + sideg (f) (G.create_contexts (G.CSet.singleton c))*) + let query ctx = S.query (ctx) let branch ctx = S.branch (ctx) let assign ctx = S.assign (ctx) let vdecl ctx = S.vdecl (ctx) - let enter ctx = S.enter (ctx) (*TODO*) + let enter ctx = + if !AnalysisState.postsolving then + printf "hallo hallo"; + S.enter (ctx) (*TODO*) let paths_as_set ctx = S.paths_as_set (ctx) let body ctx = S.body (ctx) let return ctx = S.return (ctx) @@ -1755,7 +1961,7 @@ struct let asm ctx = S.asm (ctx) let event ctx e octx = S.event (ctx) e (octx) end - +*) module CompareGlobSys (SpecSys: SpecSys) = struct From 936e7a76bdfb7b6607d5a721fef727e6c24bf4a1 Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Mon, 5 Jun 2023 16:04:58 +0200 Subject: [PATCH 0067/1312] Loop termination tests extended --- .../09-complex-for-loop-terminating.c | 2 +- .../10-complex-loop-terminating.c | 2 +- .../15-complex-loop-combination-terminating.c | 6 ++-- .../80-termination/17-goto-terminating.c | 18 +++++++++++ .../80-termination/18-goto-nonterminating.c | 15 +++++++++ .../80-termination/19-rand-terminating.c | 31 +++++++++++++++++++ .../80-termination/20-rand-nonterminating.c | 31 +++++++++++++++++++ 7 files changed, 100 insertions(+), 5 deletions(-) create mode 100644 tests/regression/80-termination/17-goto-terminating.c create mode 100644 tests/regression/80-termination/18-goto-nonterminating.c create mode 100644 tests/regression/80-termination/19-rand-terminating.c create mode 100644 tests/regression/80-termination/20-rand-nonterminating.c diff --git a/tests/regression/80-termination/09-complex-for-loop-terminating.c b/tests/regression/80-termination/09-complex-for-loop-terminating.c index ed28fa9b43..508b31500c 100644 --- a/tests/regression/80-termination/09-complex-for-loop-terminating.c +++ b/tests/regression/80-termination/09-complex-for-loop-terminating.c @@ -1,4 +1,4 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval #include int main() diff --git a/tests/regression/80-termination/10-complex-loop-terminating.c b/tests/regression/80-termination/10-complex-loop-terminating.c index 3a19f17bee..9d5cd4b928 100644 --- a/tests/regression/80-termination/10-complex-loop-terminating.c +++ b/tests/regression/80-termination/10-complex-loop-terminating.c @@ -1,4 +1,4 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval #include int main() diff --git a/tests/regression/80-termination/15-complex-loop-combination-terminating.c b/tests/regression/80-termination/15-complex-loop-combination-terminating.c index 54f8cd97c8..1ea228ae55 100644 --- a/tests/regression/80-termination/15-complex-loop-combination-terminating.c +++ b/tests/regression/80-termination/15-complex-loop-combination-terminating.c @@ -1,4 +1,4 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval #include int main() @@ -130,7 +130,7 @@ int main() } } - /* // Loop with a label and goto statement + // Loop with a label and goto statement int w = 1; start: if (w <= 5) @@ -138,7 +138,7 @@ int main() printf("Loop with Label and Goto: %d\n", w); w++; goto start; // TERM - } */ + } return 0; } diff --git a/tests/regression/80-termination/17-goto-terminating.c b/tests/regression/80-termination/17-goto-terminating.c new file mode 100644 index 0000000000..10aa729837 --- /dev/null +++ b/tests/regression/80-termination/17-goto-terminating.c @@ -0,0 +1,18 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval +#include + +int main() +{ + int num = 1; + +loop: // TERM + printf("Current number: %d\n", num); + num++; + + if (num <= 10) + { + goto loop; + } + + return 0; +} diff --git a/tests/regression/80-termination/18-goto-nonterminating.c b/tests/regression/80-termination/18-goto-nonterminating.c new file mode 100644 index 0000000000..dbb7a3df59 --- /dev/null +++ b/tests/regression/80-termination/18-goto-nonterminating.c @@ -0,0 +1,15 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval +#include + +int main() +{ + int num = 1; + +loop: // NOTERM + printf("Current number: %d\n", num); + num++; + + goto loop; + + return 0; +} diff --git a/tests/regression/80-termination/19-rand-terminating.c b/tests/regression/80-termination/19-rand-terminating.c new file mode 100644 index 0000000000..1d226f0df2 --- /dev/null +++ b/tests/regression/80-termination/19-rand-terminating.c @@ -0,0 +1,31 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval +#include +#include +#include + +int main() +{ + // Seed the random number generator + srand(time(NULL)); + + if (rand()) + { + // Loop inside the if part + for (int i = 1; i <= 5; i++) // TERM + { + printf("Loop inside if part: %d\n", i); + } + } + else + { + // Loop inside the else part + int j = 1; + while (j <= 5) // TERM + { + printf("Loop inside else part: %d\n", j); + j++; + } + } + + return 0; +} diff --git a/tests/regression/80-termination/20-rand-nonterminating.c b/tests/regression/80-termination/20-rand-nonterminating.c new file mode 100644 index 0000000000..6639e5bc76 --- /dev/null +++ b/tests/regression/80-termination/20-rand-nonterminating.c @@ -0,0 +1,31 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval +#include +#include +#include + +int main() +{ + // Seed the random number generator + srand(time(NULL)); + + if (rand()) + { + // Loop inside the if part + for (int i = 1; i <= 0; i++) // NOTERM + { + printf("Loop inside if part: %d\n", i); + } + } + else + { + // Loop inside the else part + int j = 1; + while (j < 5) // NOTERM + { + printf("Loop inside else part: %d\n", j); + j++; + } + } + + return 0; +} From 3818086469416a606ee38cde87324bd1d2ee2535 Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Thu, 1 Jun 2023 05:01:09 +0200 Subject: [PATCH 0068/1312] Tests for loop termination --- scripts/update_suite.rb | 6 + .../01-simple-loop-terminating.c | 15 ++ .../02-simple-loop-nonterminating.c | 12 ++ .../03-nested-loop-terminating.c | 27 ++++ .../04-nested-loop-nonterminating.c | 23 +++ .../80-termination/05-for-loop-terminating.c | 14 ++ .../06-for-loop-nonterminating.c | 10 ++ .../07-nested-for-loop-terminating.c | 20 +++ .../08-nested-for-loop-nonterminating.c | 19 +++ .../09-complex-for-loop-terminating.c | 107 +++++++++++++ .../10-complex-loop-terminating.c | 135 ++++++++++++++++ .../80-termination/11-loopless-termination.c | 7 + .../12-do-while-instant-terminating.c | 15 ++ .../80-termination/13-do-while-terminating.c | 16 ++ .../14-do-while-nonterminating.c | 16 ++ .../15-complex-loop-combination-terminating.c | 144 ++++++++++++++++++ ...16-nested-loop-nontrivial-nonterminating.c | 23 +++ 17 files changed, 609 insertions(+) create mode 100644 tests/regression/80-termination/01-simple-loop-terminating.c create mode 100644 tests/regression/80-termination/02-simple-loop-nonterminating.c create mode 100644 tests/regression/80-termination/03-nested-loop-terminating.c create mode 100644 tests/regression/80-termination/04-nested-loop-nonterminating.c create mode 100644 tests/regression/80-termination/05-for-loop-terminating.c create mode 100644 tests/regression/80-termination/06-for-loop-nonterminating.c create mode 100644 tests/regression/80-termination/07-nested-for-loop-terminating.c create mode 100644 tests/regression/80-termination/08-nested-for-loop-nonterminating.c create mode 100644 tests/regression/80-termination/09-complex-for-loop-terminating.c create mode 100644 tests/regression/80-termination/10-complex-loop-terminating.c create mode 100644 tests/regression/80-termination/11-loopless-termination.c create mode 100644 tests/regression/80-termination/12-do-while-instant-terminating.c create mode 100644 tests/regression/80-termination/13-do-while-terminating.c create mode 100644 tests/regression/80-termination/14-do-while-nonterminating.c create mode 100644 tests/regression/80-termination/15-complex-loop-combination-terminating.c create mode 100644 tests/regression/80-termination/16-nested-loop-nontrivial-nonterminating.c diff --git a/scripts/update_suite.rb b/scripts/update_suite.rb index e99068829e..dead6cd8f1 100755 --- a/scripts/update_suite.rb +++ b/scripts/update_suite.rb @@ -165,6 +165,8 @@ def collect_warnings when /^\[Error\]/ then "warn" when /^\[Info\]/ then "warn" when /^\[Success\]/ then "success" + when /^\[Terminating\]/ then "term" + when /^\[Nonterminating\]/ then "noterm" when /\[Debug\]/ then next # debug "warnings" shouldn't count as other warnings (against NOWARN) when /^ on line \d+ $/ then next # dead line warnings shouldn't count (used for unreachability with NOWARN) when /^ on lines \d+..\d+ $/ then next # dead line warnings shouldn't count (used for unreachability with NOWARN) @@ -298,6 +300,10 @@ def parse_tests (lines) tests[i] = "fail" elsif obj =~ /UNKNOWN/ then tests[i] = "unknown" + elsif obj =~ /NON?TERM/ then + tests[i] = "noterm" + elsif obj =~ /TERM/ then + tests[i] = "term" elsif obj =~ /(assert|__goblint_check).*\(/ then if obj =~ /FAIL/ then tests[i] = "fail" diff --git a/tests/regression/80-termination/01-simple-loop-terminating.c b/tests/regression/80-termination/01-simple-loop-terminating.c new file mode 100644 index 0000000000..931b125171 --- /dev/null +++ b/tests/regression/80-termination/01-simple-loop-terminating.c @@ -0,0 +1,15 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int i = 1; + + while (i <= 10) // TERM + { + printf("%d\n", i); + i++; + } + + return 0; +} diff --git a/tests/regression/80-termination/02-simple-loop-nonterminating.c b/tests/regression/80-termination/02-simple-loop-nonterminating.c new file mode 100644 index 0000000000..520a4a82e0 --- /dev/null +++ b/tests/regression/80-termination/02-simple-loop-nonterminating.c @@ -0,0 +1,12 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + while (1) // NOTERM + { + continue; + } + + return 0; +} diff --git a/tests/regression/80-termination/03-nested-loop-terminating.c b/tests/regression/80-termination/03-nested-loop-terminating.c new file mode 100644 index 0000000000..172827af42 --- /dev/null +++ b/tests/regression/80-termination/03-nested-loop-terminating.c @@ -0,0 +1,27 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int rows = 3; + int columns = 4; + int i = 1; + + // Outer while loop for rows + while (i <= rows) + { // TERM + int j = 1; + + // Inner while loop for columns + while (j <= columns) + { // TERM + printf("(%d, %d) ", i, j); + j++; + } + + printf("\n"); + i++; + } + + return 0; +} diff --git a/tests/regression/80-termination/04-nested-loop-nonterminating.c b/tests/regression/80-termination/04-nested-loop-nonterminating.c new file mode 100644 index 0000000000..37af9ed6fb --- /dev/null +++ b/tests/regression/80-termination/04-nested-loop-nonterminating.c @@ -0,0 +1,23 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int outerCount = 1; + + while (outerCount <= 3) // NOTERM + { + int innerCount = 1; + + while (1) // NOTERM + { + printf("(%d, %d) ", outerCount, innerCount); + innerCount++; + } + + printf("\n"); + outerCount++; + } + + return 0; +} diff --git a/tests/regression/80-termination/05-for-loop-terminating.c b/tests/regression/80-termination/05-for-loop-terminating.c new file mode 100644 index 0000000000..ab286a6dd4 --- /dev/null +++ b/tests/regression/80-termination/05-for-loop-terminating.c @@ -0,0 +1,14 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int i; + + for (i = 1; i <= 10; i++) // TERM + { + printf("%d\n", i); + } + + return 0; +} diff --git a/tests/regression/80-termination/06-for-loop-nonterminating.c b/tests/regression/80-termination/06-for-loop-nonterminating.c new file mode 100644 index 0000000000..466001e6e5 --- /dev/null +++ b/tests/regression/80-termination/06-for-loop-nonterminating.c @@ -0,0 +1,10 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() { + for (;;) { // NOTERM + printf("This loop does not terminate.\n"); + } + + return 0; +} diff --git a/tests/regression/80-termination/07-nested-for-loop-terminating.c b/tests/regression/80-termination/07-nested-for-loop-terminating.c new file mode 100644 index 0000000000..eec4dda908 --- /dev/null +++ b/tests/regression/80-termination/07-nested-for-loop-terminating.c @@ -0,0 +1,20 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int rows = 3; + int columns = 4; + + // Nested loop to iterate over rows and columns + for (int i = 1; i <= rows; i++) // TERM + { + for (int j = 1; j <= columns; j++) // TERM + { + printf("(%d, %d) ", i, j); + } + printf("\n"); + } + + return 0; +} diff --git a/tests/regression/80-termination/08-nested-for-loop-nonterminating.c b/tests/regression/80-termination/08-nested-for-loop-nonterminating.c new file mode 100644 index 0000000000..3f7bcb4f07 --- /dev/null +++ b/tests/regression/80-termination/08-nested-for-loop-nonterminating.c @@ -0,0 +1,19 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int outerCount, innerCount; + + for (outerCount = 1; outerCount <= 3; outerCount++) // NOTERM + { + for (innerCount = 1;; innerCount++) // NOTERM + { + printf("(%d, %d) ", outerCount, innerCount); + } + + printf("\n"); + } + + return 0; +} diff --git a/tests/regression/80-termination/09-complex-for-loop-terminating.c b/tests/regression/80-termination/09-complex-for-loop-terminating.c new file mode 100644 index 0000000000..ed28fa9b43 --- /dev/null +++ b/tests/regression/80-termination/09-complex-for-loop-terminating.c @@ -0,0 +1,107 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int i, j, k; + + // Outer loop + for (i = 1; i <= 5; i++) // TERM + { + // Inner loop 1 + for (j = 1; j <= i; j++) // TERM + { + printf("%d ", j); + } + printf("\n"); + + // Inner loop 2 + for (k = i; k >= 1; k--) // TERM + { + printf("%d ", k); + } + printf("\n"); + } + + // Additional loop + for (i = 5; i >= 1; i--) // TERM + { + for (j = i; j >= 1; j--) // TERM + { + printf("%d ", j); + } + printf("\n"); + } + + // Loop with conditions + for (i = 1; i <= 10; i++) // TERM + { + if (i % 2 == 0) + { + printf("%d is even\n", i); + } + else + { + printf("%d is odd\n", i); + } + } + + // Loop with nested conditions + for (i = 1; i <= 10; i++) // TERM + { + printf("Number: %d - ", i); + if (i < 5) + { + printf("Less than 5\n"); + } + else if (i > 5) + { + printf("Greater than 5\n"); + } + else + { + printf("Equal to 5\n"); + } + } + + // Loop with a break statement + for (i = 1; i <= 10; i++) // TERM + { + printf("%d ", i); + if (i == 5) + { + break; + } + } + printf("\n"); + + // Loop with a continue statement + for (i = 1; i <= 10; i++) // TERM + { + if (i % 2 == 0) + { + continue; + } + printf("%d ", i); + } + printf("\n"); + + // Loop with complex conditions + for (i = 1; i <= 10; i++) // TERM + { + if (i > 5 && i % 2 == 0) + { + printf("%d ", i); + } + } + printf("\n"); + + // Loop with multiple variables + int a, b, c; + for (a = 1, b = 2, c = 3; a <= 10; a++, b += 2, c += 3) // TERM + { + printf("%d %d %d\n", a, b, c); + } + + return 0; +} diff --git a/tests/regression/80-termination/10-complex-loop-terminating.c b/tests/regression/80-termination/10-complex-loop-terminating.c new file mode 100644 index 0000000000..3a19f17bee --- /dev/null +++ b/tests/regression/80-termination/10-complex-loop-terminating.c @@ -0,0 +1,135 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int i = 1; + int j = 1; + int k = 5; + + // Outer while loop + while (i <= 5) // TERM + { + // Inner while loop 1 + while (j <= i) // TERM + { + printf("%d ", j); + j++; + } + printf("\n"); + j = 1; + + // Inner while loop 2 + while (k >= 1) // TERM + { + printf("%d ", k); + k--; + } + printf("\n"); + k = 5; + + i++; + } + + // Additional while loop + i = 5; + while (i >= 1) // TERM + { + j = i; + while (j >= 1) // TERM + { + printf("%d ", j); + j--; + } + printf("\n"); + i--; + } + + // Loop with conditions + i = 1; + while (i <= 10) // TERM + { + if (i % 2 == 0) + { + printf("%d is even\n", i); + } + else + { + printf("%d is odd\n", i); + } + i++; + } + + // Loop with nested conditions + i = 1; + while (i <= 10) // TERM + { + printf("Number: %d - ", i); + if (i < 5) + { + printf("Less than 5\n"); + } + else if (i > 5) + { + printf("Greater than 5\n"); + } + else + { + printf("Equal to 5\n"); + } + i++; + } + + // Loop with a break statement + i = 1; + while (i <= 10) // TERM + { + printf("%d ", i); + if (i == 5) + { + break; + } + i++; + } + printf("\n"); + + // Loop with a continue statement + i = 1; + while (i <= 10) // TERM + { + if (i % 2 == 0) + { + i++; + continue; + } + printf("%d ", i); + i++; + } + printf("\n"); + + // Loop with complex conditions + i = 1; + while (i <= 10) // TERM + { + if (i > 5 && i % 2 == 0) + { + printf("%d ", i); + } + i++; + } + printf("\n"); + + // Loop with multiple variables + int a = 1; + int b = 2; + int c = 3; + while (a <= 10) // TERM + { + printf("%d %d %d\n", a, b, c); + a++; + b += 2; + c += 3; + } + + return 0; +} diff --git a/tests/regression/80-termination/11-loopless-termination.c b/tests/regression/80-termination/11-loopless-termination.c new file mode 100644 index 0000000000..b118e65e35 --- /dev/null +++ b/tests/regression/80-termination/11-loopless-termination.c @@ -0,0 +1,7 @@ +// TERM +#include + +int main() { + printf("Terminating code without a loop\n"); + return 0; +} diff --git a/tests/regression/80-termination/12-do-while-instant-terminating.c b/tests/regression/80-termination/12-do-while-instant-terminating.c new file mode 100644 index 0000000000..cc3cc41edc --- /dev/null +++ b/tests/regression/80-termination/12-do-while-instant-terminating.c @@ -0,0 +1,15 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int i = 0; + + do // TERM + { + printf("Inside the do-while loop\n"); + } while (i > 0); + + printf("Exited the loop\n"); + return 0; +} diff --git a/tests/regression/80-termination/13-do-while-terminating.c b/tests/regression/80-termination/13-do-while-terminating.c new file mode 100644 index 0000000000..05fe270f04 --- /dev/null +++ b/tests/regression/80-termination/13-do-while-terminating.c @@ -0,0 +1,16 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int i = 1; + + do // TERM + { + printf("Inside the do-while loop\n"); + i++; + } while (i <= 5); + + printf("Exited the loop\n"); + return 0; +} diff --git a/tests/regression/80-termination/14-do-while-nonterminating.c b/tests/regression/80-termination/14-do-while-nonterminating.c new file mode 100644 index 0000000000..1c70d4fc76 --- /dev/null +++ b/tests/regression/80-termination/14-do-while-nonterminating.c @@ -0,0 +1,16 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int i = 1; + + do // NOTERM + { + printf("Inside the do-while loop\n"); + i++; + } while (i >= 2); + + printf("Exited the loop\n"); + return 0; +} diff --git a/tests/regression/80-termination/15-complex-loop-combination-terminating.c b/tests/regression/80-termination/15-complex-loop-combination-terminating.c new file mode 100644 index 0000000000..54f8cd97c8 --- /dev/null +++ b/tests/regression/80-termination/15-complex-loop-combination-terminating.c @@ -0,0 +1,144 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + // Non-nested loops + int i; + + // for loop + for (i = 1; i <= 10; i++) // TERM + { + printf("For loop iteration: %d\n", i); + } + + // while loop + int j = 1; + while (j <= 10) // TERM + { + printf("While loop iteration: %d\n", j); + j++; + } + + // do-while loop + int k = 1; + do // TERM + { + printf("Do-While loop iteration: %d\n", k); + k++; + } while (k <= 10); + + // Nested loops + int a, b; + + // Nested for and while loop + for (a = 1; a <= 5; a++) // TERM + { + int c = 1; + while (c <= a) // TERM + { + printf("Nested For-While loop: %d\n", c); + c++; + } + } + + // Nested while and do-while loop + int x = 1; + while (x <= 5) // TERM + { + int y = 1; + do // TERM + { + printf("Nested While-Do-While loop: %d\n", y); + y++; + } while (y <= x); + x++; + } + + // Nested do-while and for loop + int p = 1; + do // TERM + { + for (int q = 1; q <= p; q++) // TERM + { + printf("Nested Do-While-For loop: %d\n", q); + } + p++; + } while (p <= 5); + + // Additional loops + int m; + + // Nested while loop with a break statement + int n = 1; + while (n <= 5) // TERM + { + printf("Outer While loop iteration: %d\n", n); + m = 1; + while (1) // TERM + { + printf("Inner While loop iteration: %d\n", m); + m++; + if (m == 4) + { + break; + } + } + n++; + } + + // Loop with a continue statement + for (int r = 1; r <= 10; r++) // TERM + { + if (r % 3 == 0) + { + continue; + } + printf("Loop with Continue: %d\n", r); + } + + // Loop with multiple conditions + int s = 1; + while (s <= 10 && s % 2 == 0) // TERM + { + printf("Loop with Multiple Conditions: %d\n", s); + s++; + } + + // Loop with multiple variables + int t, u; + for (t = 1, u = 10; t <= 5 && u >= 5; t++, u--) // TERM + { + printf("Loop with Multiple Variables: %d %d\n", t, u); + } + + // Loop with nested conditions + for (int v = 1; v <= 10; v++) // TERM + { + printf("Loop with Nested Conditions: %d - ", v); + if (v < 5) + { + printf("Less than 5\n"); + } + else if (v > 5) + { + printf("Greater than 5\n"); + } + else + { + printf("Equal to 5\n"); + } + } + + /* // Loop with a label and goto statement + int w = 1; +start: + if (w <= 5) + { + printf("Loop with Label and Goto: %d\n", w); + w++; + goto start; // TERM + } */ + + return 0; +} diff --git a/tests/regression/80-termination/16-nested-loop-nontrivial-nonterminating.c b/tests/regression/80-termination/16-nested-loop-nontrivial-nonterminating.c new file mode 100644 index 0000000000..855fbd0dca --- /dev/null +++ b/tests/regression/80-termination/16-nested-loop-nontrivial-nonterminating.c @@ -0,0 +1,23 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int outerCount = 1; + + while (outerCount <= 3) // NOTERM + { + int innerCount = 1; + + while (outerCount < 3 || innerCount > 0) // NOTERM + { + printf("(%d, %d) ", outerCount, innerCount); + innerCount++; + } + + printf("\n"); + outerCount++; + } + + return 0; +} From 9f60dca36bfc5cf7628d6ba5d3f03d2109db4a57 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Sat, 3 Jun 2023 13:29:17 +0200 Subject: [PATCH 0069/1312] first tests for recursion termination analysis, added empty functor and GMapG --- runningGob.sh | 22 ++++++++---- src/framework/analyses.ml | 53 +++++++++++++++++++++++++++++ src/framework/constraints.ml | 66 +++++++++++++++++++++++++++++++++++- src/framework/control.ml | 1 + 4 files changed, 135 insertions(+), 7 deletions(-) diff --git a/runningGob.sh b/runningGob.sh index f765b5afab..e3b5a6da45 100755 --- a/runningGob.sh +++ b/runningGob.sh @@ -1,22 +1,32 @@ #!/bin/bash -make +#make #make install # set options and file for apron execution -options_apron="--set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --enable warn.debug" #note: preprocessing first needs to be added to apron +options_apron="--set ana.activated[+] apron --enable ana.int.interval --set --enable warn.debug" #note: preprocessing first needs to be added to apron options_signs="--set "ana.activated[+]" signs --enable warn.debug" options_term="--set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra" -cfile_loops="tests/regression/55-loop-unrolling/01-simple-cases.c" -cfile_signs="tests/regression/99-tutorials/01-first.c" +cfile_loops="tests/regression/55-loop-unrolling/01-simple-cases.c" +cfile_nonTerm="tests/regression/80-termination/02-simple-loop-nonterminating.c" +cfile_signs="tests/regression/99-tutorials/01-first.c" +cfile_deadCode="tests/regression/80-termination/16-nested-loop-nontrivial-nonterminating.c" # run analysis, write cil output to file and enable visualization via html #./goblint $cfile_loops $options_apron --enable justcil > output.txt #./goblint $cfile_loops $options_apron --html # run analysis, write cil output to file and enable visualization via html -./goblint $cfile_loops $options_term --enable justcil > output.txt -./goblint $cfile_loops $options_term --html +#./goblint $cfile_loops $options_term --enable justcil > output.txt +#./goblint $cfile_loops $options_term --html + +# run analysis, write cil output to file and enable visualization via html +./goblint $cfile_deadCode $options_term --enable justcil > output.txt +./goblint $cfile_deadCode $options_term --html + +# run analysis, write cil output to file and enable visualization via html +#./goblint $cfile_nonTerm $options_term --enable justcil > output.txt +#./goblint $cfile_nonTerm $options_term --html # set up server to see visualizatino python3 -m http.server --directory result 8081 diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 1a3a4ebeb1..c8d3873085 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -119,6 +119,59 @@ struct | x -> BatPrintf.fprintf f "%a" printXml x end +module GMapG (G: Lattice.S) (C: Printable.S) = +struct + module CVal = + struct + include Printable.Std (* To make it Groupable *) + include SetDomain.Make ( + struct + include C + let printXml f c = BatPrintf.fprintf f "%a" printXml c (* wrap in for HTML printing *) + end + ) + let name () = "contextsMap" + end + + module RangeVal = + struct + include SetDomain.Make ( + struct + include C (*TODO: sollte hier iwi ein tupel sein*) + let printXml f c = BatPrintf.fprintf f "%a" printXml c (* wrap in for HTML printing *) + end + ) + let name () = "contextsMap" + end + + module CMap = + struct + include MapDomain.MapBot (CVal) (RangeVal) + let name () = "contextsMap" + end + include Lattice.Lift2 (G) (CMap) (Printable.DefaultNames) + + let is_bot () = false + let is_top () = false + + (*let spec = function + | `Bot -> G.bot () + | `Lifted1 x -> x + | _ -> failwith "GVarG.spec" + let contexts = function + | `Bot -> CSet.bot () + | `Lifted2 x -> x + | _ -> failwith "GVarG.contexts" + let create_spec spec = `Lifted1 spec + let create_contexts contexts = `Lifted2 contexts + + let printXml f = function + | `Lifted1 x -> G.printXml f x + | `Lifted2 x -> BatPrintf.fprintf f "%a" CSet.printXml x + | x -> BatPrintf.fprintf f "%a" printXml x +*) +end + exception Deadcode diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 740d1f85a9..e9c1b9b0a2 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1343,7 +1343,7 @@ struct module EM = struct include MapDomain.MapBot (Basetype.CilExp) (Basetype.Bools) - let name () = "branches" + let name () = "bmodule Vranches" end module G = @@ -1692,6 +1692,70 @@ struct let event ctx e octx = S.event (conv ctx) e (conv octx) end +(** Add cycle detection in the function call graph to a analysis *) +module RecursionTermLifter (S: Spec) + : Spec with module D = S.D + and module G = S.G + and module C = S.C + and module G = GMapG (S.G) (S.C) += + +struct + module C = S.C + module P = S.P + module D = S.D + + (*global invariant + - fundec -> Map (S.C) (Set (fundec * S.C)) + So: g -> {c' -> f, c} + in case f, c --> g, c' *) + + (*module CVal = + struct + include C + include Printable.Std (* To make it Groupable *) + let printXml f c = BatPrintf.fprintf f "%a" printXml c (* wrap in for HTML printing *) + end + module M = MapDomain.MapBot (CVal) (CVal) +*) + module V = S.V + module G = S.G(*GMapG (S.G) (S.C)*) + (*struct + include Lattice.Prod (S.G) (M) + let printXml f (d,m) = BatPrintf.fprintf f "\n%a\n%a\n" S.G.printXml d M.printXml m + end*) + let name () = "RecursionTerm (" ^ S.name () ^ ")" + + type marshal = S.marshal + let init = S.init + let finalize = S.finalize (*TODO*) + + let startstate v = S.startstate v + let exitstate v = S.exitstate v + let morphstate = S.morphstate + + let context = S.context + + let query ctx = S.query (ctx) + let branch ctx = S.branch (ctx) + let assign ctx = S.assign (ctx) + let vdecl ctx = S.vdecl (ctx) + let enter ctx = S.enter (ctx) (*TODO*) + let paths_as_set ctx = S.paths_as_set (ctx) + let body ctx = S.body (ctx) + let return ctx = S.return (ctx) + let combine_env ctx = S.combine_env (ctx) + let combine_assign ctx = S.combine_assign (ctx) + let special ctx = S.special (ctx) + let threadenter ctx = S.threadenter (ctx) + let threadspawn ctx lv f args fctx = S.threadspawn (ctx) lv f args (fctx) + let sync ctx = S.sync (ctx) + let skip ctx = S.skip (ctx) + let asm ctx = S.asm (ctx) + let event ctx e octx = S.event (ctx) e (octx) +end + + module CompareGlobSys (SpecSys: SpecSys) = struct open SpecSys diff --git a/src/framework/control.ml b/src/framework/control.ml index 35cadfc12d..bd26fa7129 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -36,6 +36,7 @@ let spec_module: (module Spec) Lazy.t = lazy ( Also must be outside of deadcode, because deadcode splits (like mutex lock event) don't pass on tokens. *) |> lift (get_bool "ana.widen.tokens") (module WideningTokens.Lifter) |> lift true (module LongjmpLifter) + |> lift true (module RecursionTermLifter)(*TODO: should we really always evaluate it???*) ) in GobConfig.building_spec := false; ControlSpecC.control_spec_c := (module S1.C); From 40128b7eb28855a9f79f649caf5f8e3288f6f7ab Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Sat, 3 Jun 2023 13:31:15 +0200 Subject: [PATCH 0070/1312] now its working :) --- src/framework/constraints.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index e9c1b9b0a2..0586a87d3e 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1697,7 +1697,7 @@ module RecursionTermLifter (S: Spec) : Spec with module D = S.D and module G = S.G and module C = S.C - and module G = GMapG (S.G) (S.C) + and module G = S.G = struct From b35766b44e2b5002a0bf5f78b5a533fc6087b4d5 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Sat, 3 Jun 2023 14:03:53 +0200 Subject: [PATCH 0071/1312] reverted the changes, wrong branch :) --- src/framework/analyses.ml | 54 ------------------------------ src/framework/constraints.ml | 64 ------------------------------------ src/framework/control.ml | 3 +- 3 files changed, 1 insertion(+), 120 deletions(-) diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index c8d3873085..7ac18f56f7 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -119,60 +119,6 @@ struct | x -> BatPrintf.fprintf f "%a" printXml x end -module GMapG (G: Lattice.S) (C: Printable.S) = -struct - module CVal = - struct - include Printable.Std (* To make it Groupable *) - include SetDomain.Make ( - struct - include C - let printXml f c = BatPrintf.fprintf f "%a" printXml c (* wrap in for HTML printing *) - end - ) - let name () = "contextsMap" - end - - module RangeVal = - struct - include SetDomain.Make ( - struct - include C (*TODO: sollte hier iwi ein tupel sein*) - let printXml f c = BatPrintf.fprintf f "%a" printXml c (* wrap in for HTML printing *) - end - ) - let name () = "contextsMap" - end - - module CMap = - struct - include MapDomain.MapBot (CVal) (RangeVal) - let name () = "contextsMap" - end - include Lattice.Lift2 (G) (CMap) (Printable.DefaultNames) - - let is_bot () = false - let is_top () = false - - (*let spec = function - | `Bot -> G.bot () - | `Lifted1 x -> x - | _ -> failwith "GVarG.spec" - let contexts = function - | `Bot -> CSet.bot () - | `Lifted2 x -> x - | _ -> failwith "GVarG.contexts" - let create_spec spec = `Lifted1 spec - let create_contexts contexts = `Lifted2 contexts - - let printXml f = function - | `Lifted1 x -> G.printXml f x - | `Lifted2 x -> BatPrintf.fprintf f "%a" CSet.printXml x - | x -> BatPrintf.fprintf f "%a" printXml x -*) -end - - exception Deadcode (** [Dom (D)] produces D lifted where bottom means dead-code *) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 0586a87d3e..d8b186160b 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1692,70 +1692,6 @@ struct let event ctx e octx = S.event (conv ctx) e (conv octx) end -(** Add cycle detection in the function call graph to a analysis *) -module RecursionTermLifter (S: Spec) - : Spec with module D = S.D - and module G = S.G - and module C = S.C - and module G = S.G -= - -struct - module C = S.C - module P = S.P - module D = S.D - - (*global invariant - - fundec -> Map (S.C) (Set (fundec * S.C)) - So: g -> {c' -> f, c} - in case f, c --> g, c' *) - - (*module CVal = - struct - include C - include Printable.Std (* To make it Groupable *) - let printXml f c = BatPrintf.fprintf f "%a" printXml c (* wrap in for HTML printing *) - end - module M = MapDomain.MapBot (CVal) (CVal) -*) - module V = S.V - module G = S.G(*GMapG (S.G) (S.C)*) - (*struct - include Lattice.Prod (S.G) (M) - let printXml f (d,m) = BatPrintf.fprintf f "\n%a\n%a\n" S.G.printXml d M.printXml m - end*) - let name () = "RecursionTerm (" ^ S.name () ^ ")" - - type marshal = S.marshal - let init = S.init - let finalize = S.finalize (*TODO*) - - let startstate v = S.startstate v - let exitstate v = S.exitstate v - let morphstate = S.morphstate - - let context = S.context - - let query ctx = S.query (ctx) - let branch ctx = S.branch (ctx) - let assign ctx = S.assign (ctx) - let vdecl ctx = S.vdecl (ctx) - let enter ctx = S.enter (ctx) (*TODO*) - let paths_as_set ctx = S.paths_as_set (ctx) - let body ctx = S.body (ctx) - let return ctx = S.return (ctx) - let combine_env ctx = S.combine_env (ctx) - let combine_assign ctx = S.combine_assign (ctx) - let special ctx = S.special (ctx) - let threadenter ctx = S.threadenter (ctx) - let threadspawn ctx lv f args fctx = S.threadspawn (ctx) lv f args (fctx) - let sync ctx = S.sync (ctx) - let skip ctx = S.skip (ctx) - let asm ctx = S.asm (ctx) - let event ctx e octx = S.event (ctx) e (octx) -end - - module CompareGlobSys (SpecSys: SpecSys) = struct open SpecSys diff --git a/src/framework/control.ml b/src/framework/control.ml index bd26fa7129..7e993733cd 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -36,8 +36,7 @@ let spec_module: (module Spec) Lazy.t = lazy ( Also must be outside of deadcode, because deadcode splits (like mutex lock event) don't pass on tokens. *) |> lift (get_bool "ana.widen.tokens") (module WideningTokens.Lifter) |> lift true (module LongjmpLifter) - |> lift true (module RecursionTermLifter)(*TODO: should we really always evaluate it???*) - ) in + ) in GobConfig.building_spec := false; ControlSpecC.control_spec_c := (module S1.C); (module S1) From e67ea327f230250553ec621691142f6edb6acaf3 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Sat, 3 Jun 2023 14:05:00 +0200 Subject: [PATCH 0072/1312] fixed a typo --- src/framework/constraints.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index d8b186160b..740d1f85a9 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1343,7 +1343,7 @@ struct module EM = struct include MapDomain.MapBot (Basetype.CilExp) (Basetype.Bools) - let name () = "bmodule Vranches" + let name () = "branches" end module G = From ff5377b4413168b21808da54305ffa5311f29e7e Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Mon, 5 Jun 2023 16:04:58 +0200 Subject: [PATCH 0073/1312] Loop termination tests extended --- .../09-complex-for-loop-terminating.c | 2 +- .../10-complex-loop-terminating.c | 2 +- .../15-complex-loop-combination-terminating.c | 6 ++-- .../80-termination/17-goto-terminating.c | 18 +++++++++++ .../80-termination/18-goto-nonterminating.c | 15 +++++++++ .../80-termination/19-rand-terminating.c | 31 +++++++++++++++++++ .../80-termination/20-rand-nonterminating.c | 31 +++++++++++++++++++ 7 files changed, 100 insertions(+), 5 deletions(-) create mode 100644 tests/regression/80-termination/17-goto-terminating.c create mode 100644 tests/regression/80-termination/18-goto-nonterminating.c create mode 100644 tests/regression/80-termination/19-rand-terminating.c create mode 100644 tests/regression/80-termination/20-rand-nonterminating.c diff --git a/tests/regression/80-termination/09-complex-for-loop-terminating.c b/tests/regression/80-termination/09-complex-for-loop-terminating.c index ed28fa9b43..508b31500c 100644 --- a/tests/regression/80-termination/09-complex-for-loop-terminating.c +++ b/tests/regression/80-termination/09-complex-for-loop-terminating.c @@ -1,4 +1,4 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval #include int main() diff --git a/tests/regression/80-termination/10-complex-loop-terminating.c b/tests/regression/80-termination/10-complex-loop-terminating.c index 3a19f17bee..9d5cd4b928 100644 --- a/tests/regression/80-termination/10-complex-loop-terminating.c +++ b/tests/regression/80-termination/10-complex-loop-terminating.c @@ -1,4 +1,4 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval #include int main() diff --git a/tests/regression/80-termination/15-complex-loop-combination-terminating.c b/tests/regression/80-termination/15-complex-loop-combination-terminating.c index 54f8cd97c8..1ea228ae55 100644 --- a/tests/regression/80-termination/15-complex-loop-combination-terminating.c +++ b/tests/regression/80-termination/15-complex-loop-combination-terminating.c @@ -1,4 +1,4 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval #include int main() @@ -130,7 +130,7 @@ int main() } } - /* // Loop with a label and goto statement + // Loop with a label and goto statement int w = 1; start: if (w <= 5) @@ -138,7 +138,7 @@ int main() printf("Loop with Label and Goto: %d\n", w); w++; goto start; // TERM - } */ + } return 0; } diff --git a/tests/regression/80-termination/17-goto-terminating.c b/tests/regression/80-termination/17-goto-terminating.c new file mode 100644 index 0000000000..10aa729837 --- /dev/null +++ b/tests/regression/80-termination/17-goto-terminating.c @@ -0,0 +1,18 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval +#include + +int main() +{ + int num = 1; + +loop: // TERM + printf("Current number: %d\n", num); + num++; + + if (num <= 10) + { + goto loop; + } + + return 0; +} diff --git a/tests/regression/80-termination/18-goto-nonterminating.c b/tests/regression/80-termination/18-goto-nonterminating.c new file mode 100644 index 0000000000..dbb7a3df59 --- /dev/null +++ b/tests/regression/80-termination/18-goto-nonterminating.c @@ -0,0 +1,15 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval +#include + +int main() +{ + int num = 1; + +loop: // NOTERM + printf("Current number: %d\n", num); + num++; + + goto loop; + + return 0; +} diff --git a/tests/regression/80-termination/19-rand-terminating.c b/tests/regression/80-termination/19-rand-terminating.c new file mode 100644 index 0000000000..1d226f0df2 --- /dev/null +++ b/tests/regression/80-termination/19-rand-terminating.c @@ -0,0 +1,31 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval +#include +#include +#include + +int main() +{ + // Seed the random number generator + srand(time(NULL)); + + if (rand()) + { + // Loop inside the if part + for (int i = 1; i <= 5; i++) // TERM + { + printf("Loop inside if part: %d\n", i); + } + } + else + { + // Loop inside the else part + int j = 1; + while (j <= 5) // TERM + { + printf("Loop inside else part: %d\n", j); + j++; + } + } + + return 0; +} diff --git a/tests/regression/80-termination/20-rand-nonterminating.c b/tests/regression/80-termination/20-rand-nonterminating.c new file mode 100644 index 0000000000..6639e5bc76 --- /dev/null +++ b/tests/regression/80-termination/20-rand-nonterminating.c @@ -0,0 +1,31 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval +#include +#include +#include + +int main() +{ + // Seed the random number generator + srand(time(NULL)); + + if (rand()) + { + // Loop inside the if part + for (int i = 1; i <= 0; i++) // NOTERM + { + printf("Loop inside if part: %d\n", i); + } + } + else + { + // Loop inside the else part + int j = 1; + while (j < 5) // NOTERM + { + printf("Loop inside else part: %d\n", j); + j++; + } + } + + return 0; +} From 69cee5fb397bd3523ffe25534218ac28790e50ee Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Mon, 5 Jun 2023 16:52:59 +0200 Subject: [PATCH 0074/1312] Added testcases with randomness --- .../21-no-exit-on-rand-unproofable.c | 19 +++++++++++++++++++ .../22-exit-on-rand-unproofable.c | 15 +++++++++++++++ .../23-exit-on-rand-terminating.c | 16 ++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 tests/regression/80-termination/21-no-exit-on-rand-unproofable.c create mode 100644 tests/regression/80-termination/22-exit-on-rand-unproofable.c create mode 100644 tests/regression/80-termination/23-exit-on-rand-terminating.c diff --git a/tests/regression/80-termination/21-no-exit-on-rand-unproofable.c b/tests/regression/80-termination/21-no-exit-on-rand-unproofable.c new file mode 100644 index 0000000000..4510ac1bb7 --- /dev/null +++ b/tests/regression/80-termination/21-no-exit-on-rand-unproofable.c @@ -0,0 +1,19 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval +#include + +int main() +{ + int forever, i = 0; + + while (i < 4 || forever == 1) + { + i++; + if (i == 4) + { + if (rand()) + { + forever = 1; + } + } + } +} \ No newline at end of file diff --git a/tests/regression/80-termination/22-exit-on-rand-unproofable.c b/tests/regression/80-termination/22-exit-on-rand-unproofable.c new file mode 100644 index 0000000000..97b18ed5fc --- /dev/null +++ b/tests/regression/80-termination/22-exit-on-rand-unproofable.c @@ -0,0 +1,15 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval +#include + +int main() +{ + int forever = 1; + + while (forever == 1) + { + if (rand()) //May exit, may not + { + forever = 0; + } + } +} \ No newline at end of file diff --git a/tests/regression/80-termination/23-exit-on-rand-terminating.c b/tests/regression/80-termination/23-exit-on-rand-terminating.c new file mode 100644 index 0000000000..5e2be62637 --- /dev/null +++ b/tests/regression/80-termination/23-exit-on-rand-terminating.c @@ -0,0 +1,16 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval +#include + +int main() +{ + int shortrun, i = 0; + + while (i < 90 || shortrun == 1) + { + i++; + if (rand()) + { + shortrun = 1; + } + } +} \ No newline at end of file From 4f45a89d0bbade25f5c102bfc376b933984147c2 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Mon, 5 Jun 2023 17:10:44 +0200 Subject: [PATCH 0075/1312] added upjumping goto statement --- runningGob.sh | 1 + src/analyses/termination_new.ml | 14 +++++++++++++- src/framework/control.ml | 2 +- src/util/terminationPreprocessing.ml | 10 +++++++++- 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/runningGob.sh b/runningGob.sh index f765b5afab..52d0830b81 100755 --- a/runningGob.sh +++ b/runningGob.sh @@ -9,6 +9,7 @@ options_term="--set "ana.activated[+]" termination --enable warn.debug --set ana cfile_loops="tests/regression/55-loop-unrolling/01-simple-cases.c" cfile_signs="tests/regression/99-tutorials/01-first.c" +cfile_goto="tests/incremental/02-cfg-comparison/01-added-return-stmt.c" # run analysis, write cil output to file and enable visualization via html #./goblint $cfile_loops $options_apron --enable justcil > output.txt diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index 936471ceaf..a380532afe 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -3,10 +3,12 @@ open Analyses open GoblintCil open TerminationPreprocessing +include Printf exception PreProcessing of string let loopCounters : varinfo list ref = ref [] +let upjumpingGotos : location list ref = ref [] (*contains the locations of the upjumping gotos*) let loopExit : varinfo ref = ref (makeVarinfo false "-error" Cil.intType) @@ -16,6 +18,14 @@ let is_loop_counter_var (x : varinfo) = let is_loop_exit_indicator (x : varinfo) = x = !loopExit +(* checks if at the current location (=loc) of the analysis an upjumping goto was already reached + true: no upjumping goto was reached till now*) +let currrently_no_upjumping_gotos (loc : location) = + List.for_all (function (l) -> (l >= loc)) upjumpingGotos.contents + +let no_upjumping_gotos () = + (List.length upjumpingGotos.contents) <= 0 + (** Checks whether a variable can be bounded *) let check_bounded ctx varinfo = let exp = Lval (Var varinfo, NoOffset) in @@ -43,10 +53,12 @@ struct match lval, rval with (* Assume that the following loop does not terminate *) (Var x, NoOffset), _ when is_loop_counter_var x -> + if not (no_upjumping_gotos ()) then printf "\n4 problem\n"; D.add x false ctx.local (* Loop exit: Check whether loop counter variable is bounded *) | (Var y, NoOffset), Lval (Var x, NoOffset) when is_loop_exit_indicator y -> let is_bounded = check_bounded ctx x in + if not (no_upjumping_gotos ()) then printf "\n5 problem\n"; D.add x is_bounded ctx.local | _ -> ctx.local @@ -68,6 +80,6 @@ end let () = (** Register the preprocessing *) - Cilfacade.register_preprocess_cil (Spec.name ()) (new loopCounterVisitor loopCounters loopExit); + Cilfacade.register_preprocess_cil (Spec.name ()) (new loopCounterVisitor loopCounters upjumpingGotos loopExit); (** Register this analysis within the master control program *) MCP.register_analysis (module Spec : MCPSpec) diff --git a/src/framework/control.ml b/src/framework/control.ml index bd26fa7129..7722621d5a 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -36,7 +36,7 @@ let spec_module: (module Spec) Lazy.t = lazy ( Also must be outside of deadcode, because deadcode splits (like mutex lock event) don't pass on tokens. *) |> lift (get_bool "ana.widen.tokens") (module WideningTokens.Lifter) |> lift true (module LongjmpLifter) - |> lift true (module RecursionTermLifter)(*TODO: should we really always evaluate it???*) + (*|> lift true (module RecursionTermLifter)*)(*TODO: should we really always evaluate it???*) ) in GobConfig.building_spec := false; ControlSpecC.control_spec_c := (module S1.C); diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index 1a0e725624..684733c05f 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -6,6 +6,7 @@ *) open GoblintCil +include Printf let extract_file_name s = (*There still may be a need to filter more chars*) let ls = String.split_on_char '/' s in (*Assuming '/' as path seperator*) @@ -20,7 +21,7 @@ let extract_file_name s = (*There still may be a need to filt let show_location_id l = string_of_int l.line ^ "_" ^ string_of_int l.column ^ "-file" ^ "_" ^ extract_file_name l.file -class loopCounterVisitor lc le (fd : fundec) = object(self) +class loopCounterVisitor lc lg le (fd : fundec) = object(self) inherit nopCilVisitor method! vfunc (f:fundec) = if !le.vname <> "term_exit-" then begin @@ -46,6 +47,13 @@ class loopCounterVisitor lc le (fd : fundec) = object(self) let nb = mkBlock [init_stmt; mkStmt s.skind; exit_stmt] in s.skind <- Block nb; s + | Goto (sref, l) -> + let goto_jmp_stmt = sref.contents.skind in + let loc_stmt = get_stmtLoc goto_jmp_stmt in + if CilType.Location.compare l loc_stmt >= 0 (*is pos if first loc is greater -> below the second loc*) + then + lg := List.append !lg ([l] : location list); (*problem: the program might not terminate!*) + s | _ -> s in ChangeDoChildrenPost (s, action); end From a820c31ff07dcf5849b231663c0bca553bba42cf Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Tue, 6 Jun 2023 10:15:07 +0200 Subject: [PATCH 0076/1312] plain functor --- src/framework/constraints.ml | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 0177f3ea85..8132e6a1d7 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1693,7 +1693,7 @@ struct let event ctx e octx = S.event (conv ctx) e (conv octx) end -(** +(* module RecursionTermLifter (S: Spec): Spec = struct include S @@ -1711,7 +1711,7 @@ struct let s x = `Left x end - module C = + module C_ = struct include S.C include Printable.Std (* To make it Groupable *) @@ -1772,13 +1772,13 @@ struct module EM = struct - include MapDomain.MapBot (C) (T) + include MapDomain.MapBot (C_) (T) let name () = "recursions" end module G = struct - include Lattice.Lift2 (S.G) (EM) (Printable.DefaultNames) + include Lattice.Lift2 (S.G) (EM) (Printable.DefaultNames) (*Todo: do we need lift2?*) let name () = "recursionTerm" let node = function | `Bot -> EM.bot () @@ -1793,6 +1793,8 @@ struct | x -> BatPrintf.fprintf f "%a" printXml x end + + let conv (ctx: (_, G.t, _, V.t) ctx): (_, S.G.t, _, S.V.t) ctx = { ctx with global = (fun v -> G.s (ctx.global (V.s v))); @@ -1880,7 +1882,7 @@ struct let asm ctx = S.asm (conv ctx) let event ctx e octx = S.event (conv ctx) e (conv octx) end - +*) (** Add cycle detection in the function call graph to a analysis *) module RecursionTermLifter (S: Spec) @@ -1911,13 +1913,14 @@ struct end module M = MapDomain.MapBot (CVal) (CVal) *) - module V = (*TODO: do I need to change V???*) + module V = S.V + (*(*TODO: do I need to change V???*) struct include Printable.Option (S.V) (struct let name = "RecursionTerm" end) let name () = "RecursionTerm" let is_write_only t = true let s x = `Left x - end + end*) module G = S.G (*GMapG (S.G) (S.C)*) (*struct @@ -1961,7 +1964,7 @@ struct let asm ctx = S.asm (ctx) let event ctx e octx = S.event (ctx) e (octx) end -*) + module CompareGlobSys (SpecSys: SpecSys) = struct From f298efa3e087d32ee2608a55b74d1d291c5e56a0 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Tue, 6 Jun 2023 11:44:02 +0200 Subject: [PATCH 0077/1312] added tuple; problem: fundec is just a type and not a module --- src/framework/constraints.ml | 41 +++++++++++++++++++++++++++++++++--- src/framework/control.ml | 2 +- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 8132e6a1d7..37cf9a5f51 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1904,7 +1904,6 @@ struct (* - module CVal = struct include C @@ -1921,8 +1920,44 @@ struct let is_write_only t = true let s x = `Left x end*) - module G = S.G - (*GMapG (S.G) (S.C)*) + + module C_ = + struct + include S.C + include Printable.Std (* To make it Groupable *) + let printXml f c = BatPrintf.fprintf f "%a" printXml c (* wrap in for HTML printing *) + + end + + module type FundecType = + sig + type t = fundec + + val getFundec: t -> fundec + (* Define any other values or types exposed by the module *) + end + + module Fundec (F:fundec) : FundecType = + struct + let getFundec = F + let fname = F.fname + end + + (* Tuple of fundec and S.C*) + module T = (*Todo: is this Printable.S or S.C*) + struct + include Printable.Std + type t = (fundec * S.C.t) + + let equal (a1, b1) (a2, b2) = if (a1 = a2) && (b1 = b2) then true else false + let show () = " " + end + + (* Set of Tuples*) + module TSet = SetDomain.Make (T) + + module G = S.G(*Lattice.Lift2 (S.G) (MapDomain.MapBot (C_) (TSet)) (Printable.DefaultNames) (*TODO: does MapBot fit?*)*) + (*GMapG (S.G) (S.C)*) (*struct include Lattice.Prod (S.G) (M) let printXml f (d,m) = BatPrintf.fprintf f "\n%a\n%a\n" S.G.printXml d M.printXml m diff --git a/src/framework/control.ml b/src/framework/control.ml index 7722621d5a..bd26fa7129 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -36,7 +36,7 @@ let spec_module: (module Spec) Lazy.t = lazy ( Also must be outside of deadcode, because deadcode splits (like mutex lock event) don't pass on tokens. *) |> lift (get_bool "ana.widen.tokens") (module WideningTokens.Lifter) |> lift true (module LongjmpLifter) - (*|> lift true (module RecursionTermLifter)*)(*TODO: should we really always evaluate it???*) + |> lift true (module RecursionTermLifter)(*TODO: should we really always evaluate it???*) ) in GobConfig.building_spec := false; ControlSpecC.control_spec_c := (module S1.C); From f940d01dae2b821937e839016c9cd68bc1e4c61e Mon Sep 17 00:00:00 2001 From: Nathan Schmidt Date: Tue, 6 Jun 2023 11:48:54 +0200 Subject: [PATCH 0078/1312] Finished draft of Null Byte Array Domain --- src/analyses/base.ml | 114 +++--- src/cdomains/arrayDomain.ml | 762 +++++++++++++++++++---------------- src/cdomains/arrayDomain.mli | 43 +- src/cdomains/valueDomain.ml | 30 +- 4 files changed, 544 insertions(+), 405 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 84ff44480d..8d89283e14 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -540,6 +540,8 @@ struct | `Thread _ -> empty (* thread IDs are abstract and nothing known can be reached from them *) | `JmpBuf _ -> empty (* Jump buffers are abstract and nothing known can be reached from them *) | `Mutex -> empty (* mutexes are abstract and nothing known can be reached from them *) + | `NullByte -> empty (* TODO: is this correct? *) + | `NotNullByte -> empty (* TODO: is this correct? *) (* Get the list of addresses accessable immediately from a given address, thus * all pointers within a structure should be considered, but we don't follow @@ -682,6 +684,8 @@ struct | `Thread _ -> (empty, TS.bot (), false) (* TODO: is this right? *) | `JmpBuf _ -> (empty, TS.bot (), false) (* TODO: is this right? *) | `Mutex -> (empty, TS.bot (), false) (* TODO: is this right? *) + | `NullByte -> (empty, TS.bot (), false) (* TODO: is this right? *) + | `NotNullByte -> (empty, TS.bot (), false) (* TODO: is this right? *) in reachable_from_value (get (Analyses.ask_of_ctx ctx) ctx.global ctx.local adr None) in @@ -2059,19 +2063,6 @@ struct let st: store = ctx.local in let gs = ctx.global in let desc = LF.find f in - let memory_copying dst src = - let dest_a, dest_typ = addr_type_of_exp dst in - let src_lval = mkMem ~addr:(Cil.stripCasts src) ~off:NoOffset in - let src_typ = eval_lv (Analyses.ask_of_ctx ctx) gs st src_lval - |> AD.get_type in - (* when src and destination type coincide, take value from the source, otherwise use top *) - let value = if typeSig dest_typ = typeSig src_typ then - let src_cast_lval = mkMem ~addr:(Cilfacade.mkCast ~e:src ~newt:(TPtr (dest_typ, []))) ~off:NoOffset in - eval_rv (Analyses.ask_of_ctx ctx) gs st (Lval src_cast_lval) - else - VD.top_value (unrollType dest_typ) - in - set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value in (* for string functions *) let eval_n = function (* if only n characters of a given string are needed, evaluate expression n to an integer option *) @@ -2087,24 +2078,41 @@ struct (* do nothing if all characters are needed *) | _ -> None in - let string_manipulation s1 s2 lv all op = + let string_manipulation s1 s2 lv all op_addr op_array = let s1_a, s1_typ = addr_type_of_exp s1 in let s2_a, s2_typ = addr_type_of_exp s2 in - match lv, op with - | Some lv_val, Some f -> - (* when whished types coincide, compute result of operation op, otherwise use top *) - let lv_a = eval_lv (Analyses.ask_of_ctx ctx) gs st lv_val in - let lv_typ = Cilfacade.typeOfLval lv_val in - if all && typeSig s1_typ = typeSig s2_typ && typeSig s2_typ = typeSig lv_typ then (* all types need to coincide *) - lv_a, lv_typ, (f s1_a s2_a) - else if not all && typeSig s1_typ = typeSig s2_typ then (* only the types of s1 and s2 need to coincide *) - lv_a, lv_typ, (f s1_a s2_a) - else - lv_a, lv_typ, (VD.top_value (unrollType lv_typ)) - | _ -> - (* check if s1 is potentially a string literal as writing to it would be undefined behavior; then return top *) - let _ = AD.string_writing_defined s1_a in - s1_a, s1_typ, VD.top_value (unrollType s1_typ) + (* compute value in string literals domain if s1 and s2 are both string literals *) + if AD.get_type s1_a = charPtrType && AD.get_type s2_a = charPtrType then + begin match lv, op_addr with + | Some lv_val, Some f -> + (* when whished types coincide, compute result of operation op_addr, otherwise use top *) + let lv_a = eval_lv (Analyses.ask_of_ctx ctx) gs st lv_val in + let lv_typ = Cilfacade.typeOfLval lv_val in + if all && typeSig s1_typ = typeSig s2_typ && typeSig s2_typ = typeSig lv_typ then (* all types need to coincide *) + lv_a, lv_typ, (f s1_a s2_a) + else if not all && typeSig s1_typ = typeSig s2_typ then (* only the types of s1 and s2 need to coincide *) + lv_a, lv_typ, (f s1_a s2_a) + else + lv_a, lv_typ, (VD.top_value (unrollType lv_typ)) + | _ -> + (* check if s1 is potentially a string literal as writing to it would be undefined behavior; then return top *) + let _ = AD.string_writing_defined s1_a in + s1_a, s1_typ, VD.top_value (unrollType s1_typ) + end + (* else compute value in array domain *) + else + let eval_dst = eval_rv (Analyses.ask_of_ctx ctx) gs st s1 in + let eval_src = eval_rv (Analyses.ask_of_ctx ctx) gs st s2 in + match eval_dst, eval_src with + | `Array array_dst, `Array array_src -> + begin match lv with + | Some lv_val -> + let lv_a = eval_lv (Analyses.ask_of_ctx ctx) gs st lv_val in + let lv_typ = Cilfacade.typeOfLval lv_val in + lv_a, lv_typ, op_array array_dst array_src + | None -> s1_a, s1_typ, op_array array_dst array_src + end + | _ -> s1_a, s1_typ, VD.top_value (unrollType s1_typ) in let st = match desc.special args, f.vname with | Memset { dest; ch; count; }, _ -> @@ -2126,26 +2134,23 @@ struct let value = VD.zero_init_value dest_typ in set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value | Memcpy { dest = dst; src }, _ -> - memory_copying dst src - (* strcpy(dest, src); *) - | Strcpy { dest = dst; src; n = None }, _ -> let dest_a, dest_typ = addr_type_of_exp dst in - (* when dest surely isn't a string literal, try copying src to dest *) - if AD.string_writing_defined dest_a then - memory_copying dst src - else - (* else return top (after a warning was issued) *) - set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ (VD.top_value (unrollType dest_typ)) - (* strncpy(dest, src, n); *) + let src_lval = mkMem ~addr:(Cil.stripCasts src) ~off:NoOffset in + let src_typ = eval_lv (Analyses.ask_of_ctx ctx) gs st src_lval + |> AD.get_type in + (* when src and destination type coincide, take value from the source, otherwise use top *) + let value = if typeSig dest_typ = typeSig src_typ then + let src_cast_lval = mkMem ~addr:(Cilfacade.mkCast ~e:src ~newt:(TPtr (dest_typ, []))) ~off:NoOffset in + eval_rv (Analyses.ask_of_ctx ctx) gs st (Lval src_cast_lval) + else + VD.top_value (unrollType dest_typ) + in + set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value | Strcpy { dest = dst; src; n }, _ -> - begin match eval_n n with - | Some num -> - let dest_a, dest_typ, value = string_manipulation dst src None false None in - set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value - | None -> failwith "already handled in case above" - end + let dest_a, dest_typ, value = string_manipulation dst src None false None (fun ar1 ar2 -> `Array(CArrays.string_copy ar1 ar2 (eval_n n))) in + set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value | Strcat { dest = dst; src; n }, _ -> - let dest_a, dest_typ, value = string_manipulation dst src None false None in + let dest_a, dest_typ, value = string_manipulation dst src None false None (fun ar1 ar2 -> `Array(CArrays.string_concat ar1 ar2 (eval_n n))) in set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value | Strlen s, _ -> begin match lv with @@ -2154,7 +2159,16 @@ struct let dest_typ = Cilfacade.typeOfLval lv_val in let lval = mkMem ~addr:(Cil.stripCasts s) ~off:NoOffset in let address = eval_lv (Analyses.ask_of_ctx ctx) gs st lval in - let value = `Int(AD.to_string_length address) in + let value = + (* if s string literal, compute strlen in string literals domain *) + if AD.get_type address = charPtrType then + `Int(AD.to_string_length address) + (* else compute strlen in array domain *) + else + begin match eval_rv (Analyses.ask_of_ctx ctx) gs st s with + | `Array array_s -> `Int(CArrays.to_string_length array_s) + | _ -> VD.top_value (unrollType dest_typ) + end in set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value | None -> st end @@ -2164,7 +2178,8 @@ struct (* when haystack, needle and dest type coincide, check if needle is a substring of haystack: if that is the case, assign the substring of haystack starting at the first occurrence of needle to dest, else use top *) - let dest_a, dest_typ, value = string_manipulation haystack needle lv true (Some (fun h_a n_a -> `Address(AD.substring_extraction h_a n_a))) in + let dest_a, dest_typ, value = string_manipulation haystack needle lv true (Some (fun h_a n_a -> `Address(AD.substring_extraction h_a n_a))) + (fun h_ar n_ar -> `Array(CArrays.substring_extraction h_ar n_ar)) in set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value | None -> st end @@ -2172,7 +2187,8 @@ struct begin match lv with | Some _ -> (* when s1 and s2 type coincide, compare both both strings completely or their first n characters, otherwise use top *) - let dest_a, dest_typ, value = string_manipulation s1 s2 lv false (Some (fun s1_a s2_a -> `Int(AD.string_comparison s1_a s2_a (eval_n n)))) in + let dest_a, dest_typ, value = string_manipulation s1 s2 lv false (Some (fun s1_a s2_a -> `Int(AD.string_comparison s1_a s2_a (eval_n n)))) + (fun s1_ar s2_ar -> `Int(CArrays.string_comparison s1_ar s2_ar (eval_n n))) in set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value | None -> st end diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 3e13080ab0..287fb90e45 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -8,7 +8,7 @@ module A = Array module BI = IntOps.BigIntOps module VDQ = ValueDomainQueries -type domain = TrivialDomain | PartitionedDomain | UnrolledDomain | NullByteDomain +type domain = TrivialDomain | PartitionedDomain | UnrolledDomain (* determines the domain based on variable, type and flag *) let get_domain ~varAttr ~typAttr = @@ -39,14 +39,12 @@ let get_domain ~varAttr ~typAttr = let can_recover_from_top x = x <> TrivialDomain -module type S = +module type SMinusDomain = sig include Lattice.S type idx type value - val domain_of_t: t -> domain - val get: ?checkBounds:bool -> VDQ.t -> t -> Basetype.CilExp.t option * idx -> value val set: VDQ.t -> t -> Basetype.CilExp.t option * idx -> value -> t val make: ?varAttr:attributes -> ?typAttr:attributes -> idx -> value -> t @@ -64,9 +62,17 @@ sig val project: ?varAttr:attributes -> ?typAttr:attributes -> VDQ.t -> t -> t end +module type S = +sig + include SMinusDomain + + val domain_of_t: t -> domain +end + module type Str = sig - include S + include SMinusDomain + val to_string: t -> t val to_n_string: t -> int -> t val to_string_length: t -> idx @@ -76,6 +82,13 @@ sig val string_comparison: t -> t -> int option -> idx end +module type StrWithDomain = +sig + include Str + + val domain_of_t: t -> domain +end + module type LatticeWithSmartOps = sig include Lattice.S @@ -84,6 +97,13 @@ sig val smart_leq: (Cil.exp -> BI.t option) -> (Cil.exp -> BI.t option) -> t -> t -> bool end +module type LatticeWithNull = +sig + include LatticeWithSmartOps + val null: unit -> t + val not_null: unit -> t + val is_null: t -> bool +end module Trivial (Val: Lattice.S) (Idx: Lattice.S): S with type value = Val.t and type idx = Idx.t = struct @@ -872,17 +892,9 @@ struct let to_yojson (x, y) = `Assoc [ (Base.name (), Base.to_yojson x); ("length", Idx.to_yojson y) ] end -module type LatticeWithNull = -sig - include LatticeWithSmartOps - val null: unit -> t - val not_null: unit -> t - val is_null: t -> bool -end - -module NullByte (Val: LatticeWithNull) (Idx: IntDomain.Z): S with type value = Val.t and type idx = Idx.t = +module NullByte (Val: LatticeWithNull) (Idx: IntDomain.Z): Str with type value = Val.t and type idx = Idx.t = struct - module MustNulls = SetDomain.Reverse (SetDomain.ToppedSet (IntDomain.BigInt) (struct let topname = "No Nulls" end)) + module MustNulls = SetDomain.Reverse (SetDomain.ToppedSet (IntDomain.BigInt) (struct let topname = "All Null" end)) module MayNulls = SetDomain.ToppedSet (IntDomain.BigInt) (struct let topname = "All Null" end) (* (Must Null Set, May Null Set, Array Size) *) include Lattice.Prod3 (MustNulls) (MayNulls) (Idx) @@ -891,34 +903,54 @@ struct type idx = Idx.t type value = Val.t - let domain_of_t _ = NullByteDomain - - let get ?(checkBounds=true) (ask: VDQ.t) (must_nulls_set, _, size) (e, i) = + let get ?(checkBounds=true) (ask: VDQ.t) (must_nulls_set, may_nulls_set, size) (e, i) = let rec all_indexes_must_null i max = if Z.gt i max then true - else if MustNulls.exists (Z.equal i) must_nulls_set then + else if MustNulls.mem i must_nulls_set then all_indexes_must_null (Z.add i Z.one) max else false in - let min_i = match Idx.minimal i with - | Some min -> - if Z.lt min Z.zero then - Z.zero (* assume worst case minimal index *) + let min interval = match Idx.minimal interval with + | Some min_num -> + if Z.lt min_num Z.zero then + Z.zero (* assume worst case minimal natural number *) else - min - | None -> Z.zero in (* assume worst case minimal index *) + min_num + | None -> Z.zero in (* assume worst case minimal natural number *) + + let min_i = min i in let max_i = Idx.maximal i in + let min_size = min size in (* warn if index is (potentially) out of bounds *) if checkBounds then (array_oob_check (module Idx) (must_nulls_set, size) (e, i)); - match max_i, Idx.minimal size with - (* if there is no maximum number in interval, return top of value *) - | None, _ -> Val.top () - | Some max, Some min_size when Z.geq max Z.zero && Z.lt max min_size -> - (* else only return null if all numbers in interval are in must null index set *) - if all_indexes_must_null min_i max then + match max_i, Idx.maximal size with + (* if there is no maximum value in index interval *) + | None, _ -> + (* ... return not_null if no i >= min_i in may_nulls_set *) + if not (MayNulls.exists (Z.leq min_i) may_nulls_set) then + Val.not_null () + (* ... else return top of value *) + else + Val.top () + (* if there is no maximum size *) + | Some max_i, None when Z.geq max_i Z.zero -> + (* ... and maximum value in index interval < minimal size, return null if all numbers in index interval are in must_nulls_set *) + if Z.lt max_i min_size && all_indexes_must_null min_i max_i then Val.null () + (* ... return not_null if no number in index interval is in may_nulls_set *) + else if not (MayNulls.exists (fun x -> Z.geq x min_i && Z.leq x max_i) may_nulls_set) then + Val.not_null () + else + Val.top () + | Some max_i, Some max_size when Z.geq max_i Z.zero -> + (* if maximum value in index interval < minimal size, return null if all numbers in index interval are in must_nulls_set *) + if Z.lt max_i min_size && all_indexes_must_null min_i max_i then + Val.null () + (* if maximum value in index interval < maximal size, return not_null if no number in index interval is in may_nulls_set *) + else if Z.lt max_i max_size && not (MayNulls.exists (fun x -> Z.geq x min_i && Z.leq x max_i) may_nulls_set) then + Val.not_null () else Val.top () (* if maximum number in interval is invalid, i.e. negative, return top of value *) @@ -930,112 +962,101 @@ struct may_nulls_set else add_indexes (Z.add i Z.one) max (MayNulls.add i may_nulls_set) in - let rec remove_indexes i max must_nulls_set = - if Z.gt i max then - may_nulls_set - else - remove_indexes (Z.add i Z.one) max (MustNulls.remove i must_nulls_set) in - let min_of_natural_number num = - match Idx.minimal num with - | Some min -> - if Z.lt min Z.zero then - Z.zero (* assume worst case minimal index *) + let min interval = match Idx.minimal interval with + | Some min_num -> + if Z.lt min_num Z.zero then + Z.zero (* assume worst case minimal natural number *) else - min - | None -> Z.zero in (* assume worst case moptionimal index *) - let min_size = min_of_natural_number size in - let min_i = min_of_natural_number i in + min_num + | None -> Z.zero in (* assume worst case minimal natural number *) + + let min_size = min size in + let min_i = min i in let max_i = Idx.maximal i in - (* warn if index is (potentially) out of bounds *) - array_oob_check (module Idx) (must_nulls_set, size) (e, i); - match max_i, Val.is_null v with - (* if no maximum number in interval and value = null, modify may_nulls_set to top = all possible indexes < size *) - | None, true -> (must_nulls_set, MayNulls.top (), size) - (* if no maximum number in interval and value != null, modify must_nulls_set to top = empty set *) - | None, false -> (MustNulls.top (), may_nulls_set, size) - (* if value = null *) - | Some max, true when Z.geq max Z.zero -> - begin match Idx.maximal size with - | Some max_size -> - (* ... and i is exact number < size, add i to must_nulls_set and may_nulls_set *) - if Z.equal min_i max && Z.lt min_i min_size then - (MustNulls.add min_i must_nulls_set, MayNulls.add min_i may_nulls_set, size) - (* ... and i is exact number in size interval, add i only to may_nulls_set *) - else if Z.equal min_i max && Z.lt min_i max_size then - (must_nulls_set, MayNulls.add min_i may_nulls_set, size) - (* ... and i is exact number >= size, warn and return tuple unmodified *) - else if Z.equal min_i max then - (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Write operation outside of array bounds"; - (must_nulls_set, may_nulls_set, size)) - (* ... and i is interval with lower bound = 0 and upper bound in size interval, modify may_nulls_set to top *) - else if Z.equal min_i Z.zero && Z.equal max (Z.sub max_size Z.one) then - (must_nulls_set, MayNulls.top (), size) - (* ... and i is interval with lower bound = 0 and upper bound >= size, warn and modify may_nulls_set to top *) - else if Z.equal min_i Z.zero && Z.geq max max_size then - (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Write operation outside of array bounds"; - (must_nulls_set, MayNulls.top (), size)) - (* ... and i is interval with lower bound > 0 and upper bound >= size, warn and add all indexes from interval lower bound to size to may_nulls_set *) - else if Z.geq max max_size then - (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Write operation outside of array bounds"; - (must_nulls_set, add_indexes min_i max_size may_nulls_set, size)) - (* ... and i is interval with upper bound < size, add all indexes of interval to may_nulls_set*) - else - (must_nulls_set, add_indexes min_i max may_nulls_set, size) - (* ..., size has no upper limit *) - | None -> - (* ... and i is exact number < minimal size, add i to must_nulls_set and may_nulls_set *) - if Z.equal min_i max && Z.lt min_i min_size then - (MustNulls.add min_i must_nulls_set, MayNulls.add min_i may_nulls_set, size) - (* ... and i is exact number >= minimal size, add i to may_nulls_set only *) - else if Z.equal min_i max then - (must_nulls_set, MayNulls.add min_i may_nulls_set, size) - (* ... and i is interval, add all indexes of interval to may_nulls_set *) - else - (must_nulls_set, add_indexes min_i max may_nulls_set, size) - end - (* if value != null *) - | Some max, false when Z.geq max Z.zero -> - begin match Idx.maximal size with + let set_exact i = + match Idx.maximal size with + (* if size has no upper limit *) + | None -> + (* ..., i < minimal size and value = null, add i to must_nulls_set and may_nulls_set *) + if Z.lt i min_size && Val.is_null v then + (MustNulls.add i must_nulls_set, MayNulls.add i may_nulls_set, size) + (* ..., i < minimal size and value <> null, remove i from must_nulls_set and may_nulls_set *) + else if Z.lt i min_size then + (MustNulls.remove i must_nulls_set, MayNulls.remove i may_nulls_set, size) + (* ..., i >= minimal size and value = null, add i only to may_nulls_set *) + else if Val.is_null v then + (must_nulls_set, MayNulls.add i may_nulls_set, size) + (* ..., i >= minimal size and value <> null, remove i only from must_nulls_set *) + else + (MustNulls.remove i must_nulls_set, may_nulls_set, size) + | Some max_size -> + (* if i < minimal size and value = null, add i to must_nulls_set and may_nulls_set *) + if Z.lt i min_size && Val.is_null v then + (MustNulls.add i must_nulls_set, MayNulls.add i may_nulls_set, size) + (* if i < minimal size and value <> null, remove i from must_nulls_set and may_nulls_set *) + else if Z.lt i min_size then + (MustNulls.remove i must_nulls_set, MayNulls.remove i may_nulls_set, size) + (* if minimal size <= i < maximal size and value = null, add i only to may_nulls_set *) + else if Z.lt i max_size && Val.is_null v then + (must_nulls_set, MayNulls.add i may_nulls_set, size) + (* if minimal size <= i < maximal size and value <> null, remove i only from must_nulls_set *) + else if Z.lt i max_size then + (MustNulls.remove i must_nulls_set, may_nulls_set, size) + (* if i >= maximal size, return tuple unmodified *) + else + (must_nulls_set, may_nulls_set, size) in + + let set_interval_must min_i max_i = + (* if value = null, return must_nulls_set unmodified as not clear which index is set to null *) + if Val.is_null v then + must_nulls_set + (* if value <> null, only keep indexes must_i < minimal index and must_i > maximal index *) + else if Z.equal min_i Z.zero && Z.geq max_i min_size then + MustNulls.top () + else + MustNulls.filter (fun x -> (Z.lt x min_i || Z.gt x max_i) && Z.lt x min_size) must_nulls_set in + + let set_interval_may min_i max_i = + (* if value <> null, return may_nulls_set unmodified as not clear which index is set to value *) + if not (Val.is_null v) then + may_nulls_set + (* if value = null *) + else + match Idx.maximal size with + (* ... and size has no upper limit, add all indexes of interval to may_nulls_set *) + | None -> add_indexes min_i max_i may_nulls_set | Some max_size -> - (* ... and i is exact number < size, remove i from must_nulls_set and may_nulls_set *) - if Z.equal min_i max && Z.lt min_i min_size then - (MustNulls.remove min_i must_nulls_set, MayNulls.remove min_i may_nulls_set, size) - (* ... and i is exact number in size interval, remove i only from must_nulls_set *) - else if Z.equal min_i max && Z.lt min_i max_size then - (MustNulls.remove min_i must_nulls_set, may_nulls_set, size) - (* ... and i is exact number >= size, warn and return tuple unmodified *) - else if Z.equal min_i max then - (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Write operation outside of array bounds"; - (must_nulls_set, may_nulls_set, size)) - (* ... and i is interval with lower bound = 0 and upper bound = size, modify must_nulls_set to top *) - else if Z.equal min_i Z.zero && Z.equal max max_size then - (MustNulls.top (), may_nulls_set, size) - (* ... and i is interval with lower bound = 0 and upper bound >= size, warn and modify must_nulls_set to top *) - else if Z.equal min_i Z.zero && Z.geq max max_size then - (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Write operation outside of array bounds"; - (MustNulls.top (), may_nulls_set, size)) - (* ... and i is interval with lower bound > 0 and upper bound >= size, warn and remove all indexes from interval lower bound to size from must_nulls_set *) - else if Z.geq max max_size then - (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Write operation outside of array bounds"; - (remove_indexes min_i max_size must_nulls_set, may_nulls_set, size)) - (* ... and i is interval with upper bound < size, remove all indexes of interval from must_nulls_set *) - else - (remove_indexes min_i max must_nulls_set, may_nulls_set, size) - (* ..., size is unlimited *) - | None -> - (* ... and i is exact number < minimal size, remove i from must_nulls_set and may_nulls_set *) - if Z.equal min_i max && Z.lt min_i min_size then - (MustNulls.remove min_i must_nulls_set, MayNulls.remove min_i may_nulls_set, size) - (* ... and i is exact number >= minimal size, remove i from must_nulls_set only *) - else if Z.equal min_i max then - (MustNulls.remove min_i must_nulls_set, may_nulls_set, size) - (* ... and i is interval, remove all indexes from interval of must_nulls_set *) + (* ... add all indexes < maximal size to may_nulls_set *) + if Z.equal min_i Z.zero && Z.geq max_i max_size then + MayNulls.top () + else if Z.geq max_i max_size then + add_indexes min_i (Z.sub max_size Z.one) may_nulls_set else - (remove_indexes min_i max must_nulls_set, may_nulls_set, size) - end + add_indexes min_i max_i may_nulls_set in + + (* warn if index is (potentially) out of bounds *) + array_oob_check (module Idx) (must_nulls_set, size) (e, i); + match max_i with + (* if no maximum number in index interval *) + | None -> + (* ..., value = null*) + if Val.is_null v && Idx.maximal size = None then + match Idx.maximal size with + (* ... and there is no maximal size, modify may_nulls_set to top *) + | None -> (must_nulls_set, MayNulls.top (), size) + (* ..., add all i from minimal index to maximal size to may_nulls_set *) + | Some max_size -> (must_nulls_set, add_indexes min_i (Z.sub max_size Z.one) may_nulls_set, size) + (* ... and value <> null, only keep indexes < minimal index in must_nulls_set *) + else + (MustNulls.filter (Z.gt min_i) must_nulls_set, may_nulls_set, size) + | Some max_i when Z.geq max_i Z.zero -> + if Z.equal min_i max_i then + set_exact min_i + else + (set_interval_must min_i max_i, set_interval_may min_i max_i, size) (* if maximum number in interval is invalid, i.e. negative, return tuple unmodified *) - | _ -> (must_nulls_set, may_nulls_set, size) + | _ -> (must_nulls_set, may_nulls_set, size) let make ?(varAttr=[]) ?(typAttr=[]) i v = let min_i, max_i = match Idx.minimal i, Idx.maximal i with @@ -1063,10 +1084,10 @@ struct | None, None -> Z.zero, None in match max_i, Val.is_null v with (* if value = null, return (bot = all indexes up to minimal size - 1, top = all indexes up to maximal size - 1, size) *) - | Some max, true -> (MustNulls.bot (), MayNulls.top (), Idx.of_interval !Cil.kindOfSizeOf (min_i, max)) + | Some max_i, true -> (MustNulls.bot (), MayNulls.top (), Idx.of_interval !Cil.kindOfSizeOf (min_i, max_i)) | None, true -> (MustNulls.bot (), MayNulls.top (), Idx.starting !Cil.kindOfSizeOf min_i) - (* if value != null, return (top = no indexes, bot = no indexes, size) *) - | Some max, false -> (MustNulls.top (), MayNulls.bot (), Idx.of_interval !Cil.kindOfSizeOf (min_i, max)) + (* if value <> null, return (top = no indexes, bot = no indexes, size) *) + | Some max_i, false -> (MustNulls.top (), MayNulls.bot (), Idx.of_interval !Cil.kindOfSizeOf (min_i, max_i)) | None, false -> (MustNulls.top (), MayNulls.bot (), Idx.starting !Cil.kindOfSizeOf min_i) let length (_, _, size) = Some size @@ -1077,14 +1098,13 @@ struct let map f (must_nulls_set, may_nulls_set, size) = (* if f(null) = null, all values in must_nulls_set still are surely null; - * assume top for may_nulls_set as checking effect of for every possible value is unfeasbile*) + * assume top for may_nulls_set as checking effect of f for every possible value is unfeasbile *) if Val.is_null (f (Val.null ())) then (must_nulls_set, MayNulls.top (), size) (* else also return top for must_nulls_set *) else (MustNulls.top (), MayNulls.top (), size) - (* TODO: check there is no smarter implementation -- problem is domain doesn't work on values but Z.t / idx for size *) let fold_left f acc _ = f acc (Val.top ()) let smart_join _ _ = join @@ -1095,12 +1115,12 @@ struct let to_string (must_nulls_set, may_nulls_set, size) = (* if must_nulls_set and min_nulls_set empty, definitely no null byte in array => warn about certain buffer overflow and return tuple unchanged *) if MustNulls.is_empty must_nulls_set && MayNulls.is_empty may_nulls_set then - (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array access past end: buffer overflow"; + (M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array access past end: buffer overflow"; (must_nulls_set, may_nulls_set, size)) (* if only must_nulls_set empty, no certainty about array containing null byte => warn about potential buffer overflow and return tuple unchanged *) else if MustNulls.is_empty must_nulls_set then (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "May access array past end: potential buffer overflow"; - must_nulls_set, may_nulls_set, size) + (must_nulls_set, may_nulls_set, size)) else let min_must_null = MustNulls.min_elt must_nulls_set in (* if smallest index in sets coincides, only this null byte is kept in both sets *) @@ -1111,227 +1131,226 @@ struct (MustNulls.empty (), MayNulls.filter (Z.geq min_must_null) may_nulls_set, Idx.of_int !Cil.kindOfSizeOf (Z.add min_must_null Z.one)) let to_n_string (must_nulls_set, may_nulls_set, size) n = - let rec add_indexes i max may_nulls_set = + let rec add_indexes i max set = if Z.geq i max then - may_nulls_set + set else - add_indexes (Z.add i Z.one) max (MayNulls.add i may_nulls_set) in + add_indexes (Z.add i Z.one) max (MayNulls.add i set) in let update_must_indexes min_must_null must_nulls_set = if Z.equal min_must_null Z.zero then MustNulls.bot () else (* if strlen < n, every byte starting from min_must_null is surely also transformed to null *) - add_indexes min_must_null (Z.of_int n) (MustNulls.filter (Z.gt (Z.of_int n)) must_nulls_set) in + add_indexes min_must_null (Z.of_int n) must_nulls_set + |> MustNulls.filter (Z.gt (Z.of_int n)) in let update_may_indexes min_may_null may_nulls_set = if Z.equal min_may_null Z.zero then MayNulls.top () else - (* if strlen < n, every byte starting from may_must_null may be transformed to null *) - add_indexes min_may_null (Z.of_int n) (MayNulls.filter (Z.gt (Z.of_int n)) may_nulls_set) in - let warn_no_null min_null = - if Z.geq min_null (Z.of_int n) then - M.warn "Resulting string might not be null-terminated because src doesn't contain a null byte in the first n bytes" in + (* if minimal strlen < n, every byte starting from minimal may null index may be transformed to null *) + add_indexes min_may_null (Z.of_int n) may_nulls_set + |> MayNulls.filter (Z.gt (Z.of_int n)) in + let warn_no_null min_must_null exists_min_must_null min_may_null = + if Z.geq min_may_null (Z.of_int n) then + M.error "Resulting string might not be null-terminated because src doesn't contain a null byte in the first n bytes" + else if (exists_min_must_null && Z.geq min_must_null (Z.of_int n)) || not exists_min_must_null then + M.warn "Resulting string might not be null-terminated because src might not contain a null byte in the first n bytes" in if n < 0 then (MustNulls.top (), MayNulls.top (), Idx.top_of !Cil.kindOfSizeOf) else - let check_n = match Idx.minimal size, Idx.maximal size with - | Some min, Some max -> - if Z.gt (Z.of_int n) max then + ((match Idx.minimal size, Idx.maximal size with + | Some min_size, Some max_size -> + if Z.gt (Z.of_int n) max_size then M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array size is smaller than n bytes; can cause a buffer overflow" - else if Z.gt (Z.of_int n) min then + else if Z.gt (Z.of_int n) min_size then M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array size might be smaller than n bytes; can cause a buffer overflow" - | Some min, None -> - if Z.gt (Z.of_int n) min then + | Some min_size, None -> + if Z.gt (Z.of_int n) min_size then M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array size might be smaller than n bytes; can cause a buffer overflow" - | None, Some max -> - if Z.gt (Z.of_int n) max then + | None, Some max_size -> + if Z.gt (Z.of_int n) max_size then M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array size is smaller than n bytes; can cause a buffer overflow" - | None, None -> () in - check_n; + | None, None -> ()); + (* if definitely no null byte in array, i.e. must_nulls_set = may_nulls_set = empty set *) if MustNulls.is_empty must_nulls_set && MayNulls.is_empty may_nulls_set then - match Idx.minimal size with - (* ... there *may* be null bytes from minimal size to n - 1 if minimal size < n *) - | Some min when Z.geq min Z.zero -> (must_nulls_set, add_indexes min (Z.of_int n) may_nulls_set, Idx.of_int !Cil.kindOfSizeOf (Z.of_int n)) - | _ -> (must_nulls_set, may_nulls_set, Idx.of_int !Cil.kindOfSizeOf (Z.of_int n)) - (* if only must_nulls_set empty, remove indexes >= n and add all indexes from min_may_null to n - 1 to may_nulls_set; - * warn if resulting array may not contain null byte *) + (M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end + "Resulting string might not be null-terminated because src doesn't contain a null byte"; + match Idx.maximal size with + (* ... there *may* be null bytes from maximal size to n - 1 if maximal size < n (i.e. past end) *) + | Some max_size when Z.geq max_size Z.zero -> (must_nulls_set, add_indexes max_size (Z.of_int n) may_nulls_set, Idx.of_int !Cil.kindOfSizeOf (Z.of_int n)) + | _ -> (must_nulls_set, may_nulls_set, Idx.of_int !Cil.kindOfSizeOf (Z.of_int n))) + (* if only must_nulls_set empty, remove indexes >= n from may_nulls_set and add all indexes from minimal may null index to n - 1; + * warn as in any case, resulting array not guaranteed to contain null byte *) else if MustNulls.is_empty must_nulls_set then let min_may_null = MayNulls.min_elt may_nulls_set in - warn_no_null min_may_null; + warn_no_null Z.zero false min_may_null; (must_nulls_set, update_may_indexes min_may_null may_nulls_set, Idx.of_int !Cil.kindOfSizeOf (Z.of_int n)) else let min_must_null = MustNulls.min_elt must_nulls_set in let min_may_null = MayNulls.min_elt may_nulls_set in - warn_no_null min_may_null; - (* if smallest index in sets coincides, remove indexes >= n and add all indexes from min_null to n - 1 to both sets; - * warn if resulting array may not contain null byte *) - if Z.equal min_must_null min_may_null then - (update_must_indexes min_must_null must_nulls_set, update_may_indexes min_may_null may_nulls_set, Idx.of_int !Cil.kindOfSizeOf (Z.of_int n)) - (* else return empty must_nulls_set, remove indexes >= n and add all indexes from min_may_null to n - 1 to may_nulls_set; - * warn if resulting array may not contain null byte *) - else - (MustNulls.empty (), update_may_indexes min_may_null may_nulls_set, Idx.of_int !Cil.kindOfSizeOf (Z.of_int n)) + (* warn if resulting array may not contain null byte *) + warn_no_null min_must_null true min_may_null; + (* remove indexes >= n and add all indexes from minimal must/may null to n - 1 in the sets *) + (update_must_indexes min_must_null must_nulls_set, update_may_indexes min_may_null may_nulls_set, Idx.of_int !Cil.kindOfSizeOf (Z.of_int n))) let to_string_length (must_nulls_set, may_nulls_set, size) = - (* if must_nulls_set and min_nulls_set empty, definitely no null byte in array => return interval [size, inf) *) + (* if must_nulls_set and min_nulls_set empty, definitely no null byte in array => return interval [size, inf) and warn *) if MustNulls.is_empty must_nulls_set && MayNulls.is_empty may_nulls_set then + (M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array doesn't contain a null byte: buffer overflow"; match Idx.minimal size with - | Some min -> Idx.starting !Cil.kindOfSizeOf min - | None -> Idx.starting !Cil.kindOfSizeOf Z.zero - (* if only must_nulls_set empty, no guarantee that null ever encountered in array => return interval [minimal may null, inf) *) + | Some min_size -> Idx.starting !Cil.kindOfSizeOf min_size + | None -> Idx.starting !Cil.kindOfSizeOf Z.zero) + (* if only must_nulls_set empty, no guarantee that null ever encountered in array => return interval [minimal may null, inf) and *) else if MustNulls.is_empty must_nulls_set then - Idx.starting !Cil.kindOfSizeOf (MayNulls.min_elt may_nulls_set) + (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array might not contain a null byte: potential buffer overflow"; + Idx.starting !Cil.kindOfSizeOf (MayNulls.min_elt may_nulls_set)) (* else return interval [minimal may null, minimal must null] *) else - Idx.of_interval !Cil.kindOfSizeOf (MustNulls.min_elt must_nulls_set, MayNulls.min_elt may_nulls_set) + Idx.of_interval !Cil.kindOfSizeOf (MayNulls.min_elt may_nulls_set, MustNulls.min_elt must_nulls_set) - let string_copy (must_nulls_set1, may_nulls_set1, size1) ar2 = function + let string_copy (must_nulls_set1, may_nulls_set1, size1) ar2 n = + (* filter out indexes before strlen(src) from dest sets and after strlen(src) from src sets and build union, keep size of dest *) + let update_sets must_nulls_set2 may_nulls_set2 min_len1 min_len2 = + match Idx.minimal size1, Idx.maximal size1, min_len1, min_len2 with + | Some min_size1, Some max_size1, Some min_len2, Some max_len2 -> + (if Z.lt max_size1 min_len2 then + M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src is greater than the allocated size for dest" + else if Z.lt min_size1 max_len2 then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src may be greater than the allocated size for dest"); + let must_nulls_set_result = + (* get must nulls from src string < minimal size of dest *) + MustNulls.filter (Z.lt min_size1) must_nulls_set2 + (* and keep indexes of dest >= maximal strlen of src *) + |> MustNulls.union (MustNulls.filter (Z.geq max_len2) must_nulls_set1) in + let may_nulls_set_result = + (* get may nulls from src string < maximal size of dest *) + MayNulls.filter (Z.lt max_size1) may_nulls_set2 + (* and keep indexes of dest >= minimal strlen of src *) + |> MayNulls.union (MayNulls.filter (Z.geq min_len2) may_nulls_set1) in + (must_nulls_set_result, may_nulls_set_result, size1) + | Some min_size1, None, Some min_len2, Some max_len2 -> + (if Z.lt min_size1 max_len2 then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src may be greater than the allocated size for dest"); + let must_nulls_set_result = + MustNulls.filter (Z.lt min_size1) must_nulls_set2 + |> MustNulls.union (MustNulls.filter (Z.geq max_len2) must_nulls_set1) in + let may_nulls_set_result = + (* get all may nulls from src string as no maximal size of dest *) + may_nulls_set2 + |> MayNulls.union (MayNulls.filter (Z.geq min_len2) may_nulls_set1) in + (must_nulls_set_result, may_nulls_set_result, size1) + | Some min_size1, Some max_size1, Some min_len2, None -> + (if Z.lt max_size1 min_len2 then + M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src is greater than the allocated size for dest" + else if Z.lt min_size1 min_len2 then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src may be greater than the allocated size for dest"); + (* do not keep any index of dest as no maximal strlen of src *) + let must_nulls_set_result = MustNulls.filter (Z.lt min_size1) must_nulls_set2 in + let may_nulls_set_result = + MayNulls.filter (Z.lt max_size1) may_nulls_set2 + |> MayNulls.union (MayNulls.filter (Z.geq min_len2) may_nulls_set1) in + (must_nulls_set_result, may_nulls_set_result, size1) + | Some min_size1, None, Some min_len2, None -> + (if Z.lt min_size1 min_len2 then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src may be greater than the allocated size for dest"); + (* do not keep any index of dest as no maximal strlen of src *) + let must_nulls_set_result = MustNulls.filter (Z.lt min_size1) must_nulls_set2 in + let may_nulls_set_result = + (* get all may nulls from src string as no maximal size of dest *) + may_nulls_set2 + |> MayNulls.union (MayNulls.filter (Z.geq min_len2) may_nulls_set1) in + (must_nulls_set_result, may_nulls_set_result, size1) + (* any other case shouldn't happen as minimal index is always >= 0 *) + | _ -> (MustNulls.top (), MayNulls.top (), size1) in + + match n with (* strcpy *) | None -> - let must_nulls_set2, may_nulls_set2, size2 = to_string ar2 in + let must_nulls_set2, may_nulls_set2, _ = to_string ar2 in let strlen2 = to_string_length ar2 in - (* filter out indexes before strlen(src) from dest sets and after strlen(src) from src sets and build union, keep size of dest *) - begin match Idx.minimal size1, Idx.maximal size1, Idx.minimal strlen2, Idx.maximal strlen2 with - | Some min1, Some max1, Some min2, Some max2 -> - let warn = - if Z.leq max1 min2 then - M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src is greater than the allocated size for dest" - else if Z.leq min1 max2 then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src may be greater than the allocated size for dest" in - warn; - let must_nulls_set_result = MustNulls.union (MustNulls.filter (Z.geq max2) must_nulls_set1) (MustNulls.filter (Z.leq min1) must_nulls_set2) in - let may_nulls_set_result = MayNulls.union (MayNulls.filter (Z.gt min2) may_nulls_set1) (MayNulls.filter (Z.leq max1) may_nulls_set2) in - (must_nulls_set_result, may_nulls_set_result, size1) - | Some min1, None, Some min2, Some max2 -> - let warn = - if Z.leq min1 max2 then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src may be greater than the allocated size for dest" in - warn; - let must_nulls_set_result = MustNulls.union (MustNulls.filter (Z.geq max2) must_nulls_set1) (MustNulls.filter (Z.leq min1) must_nulls_set2) in - let may_nulls_set_result = MayNulls.union (MayNulls.filter (Z.gt min2) may_nulls_set1) may_nulls_set2 in - (must_nulls_set_result, may_nulls_set_result, size1) - | Some min1, Some max1, Some min2, None -> - let warn = - if Z.leq max1 min2 then - M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src is greater than the allocated size for dest" - else if Z.leq min1 min2 then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src may be greater than the allocated size for dest" in - warn; - let must_nulls_set_result = MustNulls.filter (Z.leq min1) must_nulls_set2 in - let may_nulls_set_result = MayNulls.union (MayNulls.filter (Z.gt min2) may_nulls_set1) (MayNulls.filter (Z.leq max1) may_nulls_set2) in - (must_nulls_set_result, may_nulls_set_result, size1) - | Some min1, None, Some min2, None -> - let warn = - if Z.leq min1 min2 then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src may be greater than the allocated size for dest" in - warn; - let must_nulls_set_result = MustNulls.filter (Z.leq min1) must_nulls_set2 in - let may_nulls_set_result = MayNulls.union (MayNulls.filter (Z.gt min2) may_nulls_set1) may_nulls_set2 in - (must_nulls_set_result, may_nulls_set_result, size1) - (* any other case shouldn't happen as minimal index is always >= 0 *) - | _ -> (MustNulls.top (), MayNulls.top (), size1) - end - (* strncpy => strlen(src) is precise number *) + update_sets must_nulls_set2 may_nulls_set2 (Idx.minimal strlen2) (Idx.maximal strlen2) + (* strncpy = exactly n bytes from src are copied to dest *) | Some n -> let must_nulls_set2, may_nulls_set2, _ = to_n_string ar2 n in - (* filter out indexes before strlen(src) from dest sets and after strlen(src) from src sets and build union, keep size of dest *) - begin match Idx.minimal size1, Idx.maximal size1 with - | Some min1, Some max1 -> - let warn = - if Z.lt max1 (Z.of_int n) then - M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src is greater than the allocated size for dest" - else if Z.lt min1 (Z.of_int n) then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src may be greater than the allocated size for dest" in - warn; - let must_nulls_set_result = MustNulls.union (MustNulls.filter (Z.geq (Z.of_int n)) must_nulls_set1) (MustNulls.filter (Z.leq min1) must_nulls_set2) in - let may_nulls_set_result = MayNulls.union (MayNulls.filter (Z.geq (Z.of_int n)) may_nulls_set1) (MayNulls.filter (Z.leq max1) may_nulls_set2) in - (must_nulls_set_result, may_nulls_set_result, size1) - | Some min1, None -> - let warn = - if Z.lt min1 (Z.of_int n) then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src may be greater than the allocated size for dest" in - warn; - let must_nulls_set_result = MustNulls.union (MustNulls.filter (Z.geq (Z.of_int n)) must_nulls_set1) (MustNulls.filter (Z.leq min1) must_nulls_set2) in - let may_nulls_set_result = MayNulls.union (MayNulls.filter (Z.geq (Z.of_int n)) may_nulls_set1) may_nulls_set2 in - (must_nulls_set_result, may_nulls_set_result, size1) - (* any other case shouldn't happen as minimal index is always >= 0 *) - | _ -> (MustNulls.top (), MayNulls.top (), size1) - end + update_sets must_nulls_set2 may_nulls_set2 (Some (Z.of_int n)) (Some (Z.of_int n)) let string_concat (must_nulls_set1, may_nulls_set1, size1) (must_nulls_set2, may_nulls_set2, size2) n = - let update_sets min1 max1 max1_exists minlen1 maxlen1 maxlen1_exists minlen2 maxlen2 maxlen2_exists must_nulls_set2' may_nulls_set2' = + let update_sets min_size1 max_size1 max_size1_exists minlen1 maxlen1 maxlen1_exists minlen2 maxlen2 maxlen2_exists must_nulls_set2' may_nulls_set2' = (* track any potential buffer overflow and issue warning if needed *) - let warn = - if max1_exists && ((maxlen1_exists && maxlen2_exists && Z.leq max1 (Z.add maxlen1 maxlen2)) - || (maxlen1_exists && Z.leq max1 (Z.add maxlen1 minlen2)) || (maxlen2_exists && Z.leq max1 (Z.add minlen1 maxlen2)) - || Z.leq max1 (Z.add minlen1 minlen2)) then - M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of the concatenation of the strings in src and dest is greater than the allocated size for dest" - else if (maxlen1_exists && maxlen2_exists && Z.leq min1 (Z.add maxlen1 maxlen2)) || (maxlen1_exists && Z.leq min1 (Z.add maxlen1 minlen2)) - || (maxlen2_exists && Z.leq min1 (Z.add minlen1 maxlen2)) || Z.leq min1 (Z.add minlen1 minlen2) then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of the conctenation of the strings in src and dest may be greater than the allocated size for dest" in - warn; + (if max_size1_exists && Z.lt max_size1 (Z.add minlen1 minlen2) then + M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end + "The length of the concatenation of the strings in src and dest is greater than the allocated size for dest" + else if (maxlen1_exists && maxlen2_exists && Z.lt min_size1 (Z.add maxlen1 maxlen2)) + || (maxlen1_exists && Z.lt min_size1 (Z.add maxlen1 minlen2)) + || (maxlen2_exists && Z.lt min_size1 (Z.add minlen1 maxlen2)) + || Z.lt min_size1 (Z.add minlen1 minlen2) then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end + "The length of the conctenation of the strings in src and dest may be greater than the allocated size for dest"); (* if any must_nulls_set empty, result must_nulls_set also empty; * for all i1, i2 in may_nulls_set1, may_nulls_set2: add i1 + i2 if it is <= strlen(dest) + strlen(src) to new may_nulls_set - * and keep indexes > strlen(dest) + strlen(src) of may_nulls_set *) + * and keep indexes > minimal strlen(dest) + strlen(src) of may_nulls_set *) if MustNulls.is_empty must_nulls_set1 || MustNulls.is_empty must_nulls_set2' then let may_nulls_set_result = - MayNulls.filter (Z.geq (Z.add minlen1 minlen2)) may_nulls_set1 + MayNulls.filter (fun x -> if maxlen1_exists && maxlen2_exists then Z.leq x (Z.add maxlen1 maxlen2) else true) may_nulls_set1 |> MayNulls.elements |> BatList.cartesian_product (MayNulls.elements may_nulls_set2') |> List.map (fun (i1, i2) -> Z.add i1 i2) |> MayNulls.of_list |> MayNulls.union (MayNulls.filter (Z.lt (Z.add minlen1 minlen2)) may_nulls_set1) - |> MayNulls.filter (fun x -> if max1_exists then Z.gt max1 x else true) in + |> MayNulls.filter (fun x -> if max_size1_exists then Z.gt max_size1 x else true) in (MustNulls.top (), may_nulls_set_result, size1) - (* if minimal must null = minimal may null in ar1 and ar2, add them and keep indexes > strlen(dest) + strlen(src) of ar1 *) - else if Z.equal (MustNulls.min_elt must_nulls_set1) (MayNulls.min_elt may_nulls_set1) then + (* if minimal must null = minimal may null in ar1 and ar2, add them together and keep indexes > strlen(dest) + strlen(src) of ar1 *) + else if Z.equal (MustNulls.min_elt must_nulls_set1) (MayNulls.min_elt may_nulls_set1) && Z.equal (MustNulls.min_elt must_nulls_set2') (MayNulls.min_elt may_nulls_set2') then let min_i1 = MustNulls.min_elt must_nulls_set1 in let min_i2 = MustNulls.min_elt must_nulls_set2' in let min_i = Z.add min_i1 min_i2 in let must_nulls_set_result = - MustNulls.filter (fun x -> if maxlen1_exists && maxlen2_exists then Z.lt (Z.add maxlen1 maxlen2) x else false) must_nulls_set1 + MustNulls.filter (Z.lt min_i) must_nulls_set1 |> MustNulls.add min_i - |> MustNulls.filter (Z.gt min1) in + |> MustNulls.filter (Z.gt min_size1) in let may_nulls_set_result = - MayNulls.filter (Z.lt (Z.add minlen1 minlen2)) may_nulls_set1 + MayNulls.filter (Z.lt min_i) may_nulls_set1 |> MayNulls.add min_i - |> MayNulls.filter (fun x -> if max1_exists then Z.gt max1 x else true) in + |> MayNulls.filter (fun x -> if max_size1_exists then Z.gt max_size1 x else true) in (must_nulls_set_result, may_nulls_set_result, size1) - (* else only add all may nulls <= strlen(dest) + strlen(src) *) + (* else only add all may nulls together <= strlen(dest) + strlen(src) *) else let min_i2 = MustNulls.min_elt must_nulls_set2' in + let may_nulls_set2'_until_min_i2 = MayNulls.filter (Z.geq min_i2) may_nulls_set2' in let must_nulls_set_result = MustNulls.filter (fun x -> if maxlen1_exists && maxlen2_exists then Z.lt (Z.add maxlen1 maxlen2) x else false) must_nulls_set1 in let may_nulls_set_result = - MayNulls.filter (Z.geq (Z.add minlen1 minlen2)) may_nulls_set1 - |> MayNulls.map (Z.add min_i2) + MayNulls.filter (fun x -> if maxlen1_exists && maxlen2_exists then Z.leq x (Z.add maxlen1 maxlen2) else true) may_nulls_set1 + |> MayNulls.elements + |> BatList.cartesian_product (MayNulls.elements may_nulls_set2'_until_min_i2) + |> List.map (fun (i1, i2) -> Z.add i1 i2) + |> MayNulls.of_list |> MayNulls.union (MayNulls.filter (Z.lt (Z.add minlen1 minlen2)) may_nulls_set1) - |> MayNulls.filter (fun x -> if max1_exists then Z.gt max1 x else true) in + |> MayNulls.filter (fun x -> if max_size1_exists then Z.gt max_size1 x else true) in (must_nulls_set_result, may_nulls_set_result, size1) in + let compute_concat must_nulls_set2' may_nulls_set2' = let strlen1 = to_string_length (must_nulls_set1, may_nulls_set1, size1) in let strlen2 = to_string_length (must_nulls_set2', may_nulls_set2', size2) in - begin match Idx.minimal size1, Idx.maximal size1, Idx.minimal strlen1, Idx.maximal strlen1, Idx.minimal strlen2, Idx.maximal strlen2 with - | Some min1, Some max1, Some minlen1, Some maxlen1, Some minlen2, Some maxlen2 -> - update_sets min1 max1 true minlen1 maxlen1 true minlen2 maxlen2 true must_nulls_set2' may_nulls_set2' - (* no upper bound for length of concatenation *) - | Some min1, Some max1, Some minlen1, None, Some minlen2, Some _ - | Some min1, Some max1, Some minlen1, Some _, Some minlen2, None - - | Some min1, Some max1, Some minlen1, None, Some minlen2, None -> - update_sets min1 max1 true minlen1 Z.zero false minlen2 Z.zero false must_nulls_set2' may_nulls_set2' - (* no upper bound for size of dest *) - | Some min1, None, Some minlen1, Some maxlen1, Some minlen2, Some maxlen2 -> - update_sets min1 Z.zero false minlen1 maxlen1 true minlen2 maxlen2 true must_nulls_set2' may_nulls_set2' - (* no upper bound for size of dest and length of concatenation *) - | Some min1, None, Some minlen1, None, Some minlen2, Some _ - | Some min1, None, Some minlen1, Some _, Some minlen2, None - | Some min1, None, Some minlen1, None, Some minlen2, None -> - update_sets min1 Z.zero false minlen1 Z.zero false minlen2 Z.zero false must_nulls_set2' may_nulls_set2' - (* any other case shouldn't happen as minimal index is always >= 0 *) - | _ -> (MustNulls.top (), MayNulls.top (), size1) - end in + match Idx.minimal size1, Idx.maximal size1, Idx.minimal strlen1, Idx.maximal strlen1, Idx.minimal strlen2, Idx.maximal strlen2 with + | Some min_size1, Some max_size1, Some minlen1, Some maxlen1, Some minlen2, Some maxlen2 -> + update_sets min_size1 max_size1 true minlen1 maxlen1 true minlen2 maxlen2 true must_nulls_set2' may_nulls_set2' + (* no upper bound for length of concatenation *) + | Some min_size1, Some max_size1, Some minlen1, None, Some minlen2, Some _ + | Some min_size1, Some max_size1, Some minlen1, Some _, Some minlen2, None + | Some min_size1, Some max_size1, Some minlen1, None, Some minlen2, None -> + update_sets min_size1 max_size1 true minlen1 Z.zero false minlen2 Z.zero false must_nulls_set2' may_nulls_set2' + (* no upper bound for size of dest *) + | Some min_size1, None, Some minlen1, Some maxlen1, Some minlen2, Some maxlen2 -> + update_sets min_size1 Z.zero false minlen1 maxlen1 true minlen2 maxlen2 true must_nulls_set2' may_nulls_set2' + (* no upper bound for size of dest and length of concatenation *) + | Some min_size1, None, Some minlen1, None, Some minlen2, Some _ + | Some min_size1, None, Some minlen1, Some _, Some minlen2, None + | Some min_size1, None, Some minlen1, None, Some minlen2, None -> + update_sets min_size1 Z.zero false minlen1 Z.zero false minlen2 Z.zero false must_nulls_set2' may_nulls_set2' + (* any other case shouldn't happen as minimal index is always >= 0 *) + | _ -> (MustNulls.top (), MayNulls.top (), size1) in match n with (* strcat *) @@ -1339,16 +1358,16 @@ struct let must_nulls_set2', may_nulls_set2', _ = to_string (must_nulls_set2, may_nulls_set2, size2) in compute_concat must_nulls_set2' may_nulls_set2' (* strncat *) - | Some num -> + | Some n -> (* take at most n bytes from src; if no null byte among them, add null byte at index n *) let must_nulls_set2', may_nulls_set2' = let must_nulls_set2, may_nulls_set2, _ = to_string (must_nulls_set2, may_nulls_set2, size2) in - if not (MayNulls.exists (Z.gt (Z.of_int num)) may_nulls_set2) then - (MustNulls.singleton (Z.of_int num), MayNulls.singleton (Z.of_int num)) - else if not (MustNulls.exists (Z.gt (Z.of_int num)) must_nulls_set2) then - (MustNulls.empty (), MayNulls.add (Z.of_int num) (MayNulls.filter (Z.leq (Z.of_int num)) may_nulls_set2)) + if not (MayNulls.exists (Z.gt (Z.of_int n)) may_nulls_set2) then + (MustNulls.singleton (Z.of_int n), MayNulls.singleton (Z.of_int n)) + else if not (MustNulls.exists (Z.gt (Z.of_int n)) must_nulls_set2) then + (MustNulls.empty (), MayNulls.add (Z.of_int n) (MayNulls.filter (Z.geq (Z.of_int n)) may_nulls_set2)) else - (MustNulls.filter (Z.leq (Z.of_int num)) must_nulls_set2, MayNulls.filter (Z.leq (Z.of_int num)) may_nulls_set2) in + (MustNulls.filter (Z.gt (Z.of_int n)) must_nulls_set2, MayNulls.filter (Z.gt (Z.of_int n)) may_nulls_set2) in compute_concat must_nulls_set2' may_nulls_set2' let substring_extraction haystack (must_nulls_set_needle, may_nulls_set_needle, size_needle) = @@ -1360,67 +1379,93 @@ struct let needle_len = to_string_length (must_nulls_set_needle, may_nulls_set_needle, size_needle) in match Idx.maximal haystack_len, Idx.minimal needle_len with | Some haystack_max, Some needle_min -> - (* if strlen(haystack) < strlen(needle), needle can never be substring of haystack => return null pointer -- TODO: how to do that? *) + (* if strlen(haystack) < strlen(needle), needle can never be substring of haystack => return null pointer *) + (* TODO: how to do that? Maybe pass on something I can identify as standing for null_ptr in base, where I plugin null_ptr *) if Z.lt haystack_max needle_min then (MustNulls.top (), MayNulls.top (), Idx.of_int !Cil.kindOfSizeOf Z.zero) else (MustNulls.top (), MayNulls.top (), Idx.top_of !Cil.kindOfSizeOf) | _ -> (MustNulls.top (), MayNulls.top (), Idx.top_of !Cil.kindOfSizeOf) - let string_comparison (must_nulls_set1, may_nulls_set1, _) (must_nulls_set2, may_nulls_set2, _) = function + let string_comparison (must_nulls_set1, may_nulls_set1, size1) (must_nulls_set2, may_nulls_set2, size2) n = + let compare n n_exists = + (* if s1 = s2 = empty string, i.e. certain null byte at index 0, or n = 0, return 0 *) + if (MustNulls.mem Z.zero must_nulls_set1 && (MustNulls.mem Z.zero must_nulls_set2)) + || (n_exists && Z.equal Z.zero n) then + Idx.of_int IInt Z.zero + (* if only s1 = empty string, return negative integer *) + else if MustNulls.mem Z.zero must_nulls_set1 && not (MustNulls.mem Z.zero must_nulls_set2) then + Idx.ending IInt Z.minus_one + (* if only s2 = empty string, return positive integer *) + else if MustNulls.mem Z.zero must_nulls_set2 then + Idx.starting IInt Z.one + else + (* if first null bytes are certain, have different indexes and are before index n if n present, return integer <> 0 *) + (try if Z.equal (MustNulls.min_elt must_nulls_set1) (MayNulls.min_elt may_nulls_set1) && (not n_exists || Z.lt (MustNulls.min_elt must_nulls_set1) n) + && Z.equal (MustNulls.min_elt must_nulls_set2) (MayNulls.min_elt may_nulls_set2) && (not n_exists || Z.lt (MustNulls.min_elt must_nulls_set2) n) + && not (Z.equal (MustNulls.min_elt must_nulls_set1) (MustNulls.min_elt must_nulls_set2)) then + Idx.join (Idx.ending IInt Z.minus_one) (Idx.starting IInt Z.one) + else + Idx.top_of IInt + with Not_found -> Idx.top_of IInt) in + + match n with (* strcmp *) | None -> - (* if s1 = s2 = empty string, i.e. certain null byte at index 0, return 0 *) - if MustNulls.mem Z.zero must_nulls_set1 && (MustNulls.mem Z.zero must_nulls_set2) then - Idx.of_int IInt Z.zero - (* if only s1 = empty string, return negative integer *) - else if MustNulls.mem Z.zero must_nulls_set1 && not (MustNulls.mem Z.zero must_nulls_set2) then - Idx.ending IInt Z.minus_one - (* if only s2 = empty string, return positive integer *) - else if MustNulls.mem Z.zero must_nulls_set2 then - Idx.starting IInt Z.one - else - (* if first null bytes are certain and have different indexes, return integer <> 0 *) - (try if Z.equal (MustNulls.min_elt must_nulls_set1) (MayNulls.min_elt may_nulls_set1) - && Z.equal (MustNulls.min_elt must_nulls_set2) (MayNulls.min_elt may_nulls_set2) - && not (Z.equal (MustNulls.min_elt must_nulls_set1) (MustNulls.min_elt must_nulls_set2)) then - Idx.join (Idx.ending IInt Z.minus_one) (Idx.starting IInt Z.one) - else - Idx.top_of IInt - with Not_found -> Idx.top_of IInt) + (* track any potential buffer overflow and issue warning if needed *) + (if MustNulls.is_empty must_nulls_set1 && MayNulls.is_empty may_nulls_set1 then + M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array of string 1 doesn't contain a null byte: buffer overflow" + else if MustNulls.is_empty must_nulls_set1 then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array of string 1 might not contain a null byte: potential buffer overflow"); + (if MustNulls.is_empty must_nulls_set2 && MayNulls.is_empty may_nulls_set2 then + M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array of string 2 doesn't contain a null byte: buffer overflow" + else if MustNulls.is_empty must_nulls_set2 then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array of string 2 might not contain a null byte: potential buffer overflow"); + (* compute abstract value for result of strcmp *) + compare Z.zero false (* strncmp *) - | Some num -> - (* if s1 = empty and s2 = empty string or n = 0, return 0 *) - if MustNulls.mem Z.zero must_nulls_set1 && ((MustNulls.mem Z.zero must_nulls_set2) || Z.equal Z.zero (Z.of_int num)) then - Idx.of_int IInt Z.zero - (* if only s1 = empty string, return negative integer *) - else if MustNulls.mem Z.zero must_nulls_set1 && not (MustNulls.mem Z.zero must_nulls_set2) then - Idx.ending IInt Z.minus_one - (* if only s2 = empty string, return positive integer *) - else if MustNulls.mem Z.zero must_nulls_set2 then - Idx.starting IInt Z.one - else - (* if first null bytes are certain, have different indexes and are before index n for s2, return integer <> 0 *) - (try if Z.equal (MustNulls.min_elt must_nulls_set1) (MayNulls.min_elt may_nulls_set1) - && Z.equal (MustNulls.min_elt must_nulls_set2) (MayNulls.min_elt may_nulls_set2) - && Z.lt (MustNulls.min_elt must_nulls_set2) (Z.of_int num) - && not (Z.equal (MustNulls.min_elt must_nulls_set1) (MustNulls.min_elt must_nulls_set2)) then - Idx.join (Idx.ending IInt Z.minus_one) (Idx.starting IInt Z.one) - else - Idx.top_of IInt - with Not_found -> Idx.top_of IInt) + | Some n -> + if n < 0 then + Idx.top_of IInt + else + let min_size1 = match Idx.minimal size1 with + | Some min_size1 -> min_size1 + | None -> Z.zero in + let min_size2 = match Idx.minimal size2 with + | Some min_size2 -> min_size2 + | None -> Z.zero in + (* issue a warning if n is (potentially) smaller than array sizes *) + (match Idx.maximal size1 with + | Some max_size1 -> + if Z.gt (Z.of_int n) max_size1 then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The size of the array of string 1 is smaller than n bytes" + else if Z.gt (Z.of_int n) min_size1 then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The size of the array of string 1 might be smaller than n bytes" + | None -> + if Z.gt (Z.of_int n) min_size1 then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The size of the array of string 1 might be smaller than n bytes"); + (match Idx.maximal size2 with + | Some max_size2 -> + if Z.gt (Z.of_int n) max_size2 then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The size of the array of string 2 is smaller than n bytes" + else if Z.gt (Z.of_int n) min_size2 then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The size of the array of string 2 might be smaller than n bytes" + | None -> + if Z.gt (Z.of_int n) min_size2 then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The size of the array of string 2 might be smaller than n bytes"); + (* compute abstract value for result of strncmp *) + compare (Z.of_int n) true - let update_length _ x = x + let update_length new_size (must_nulls_set, may_nulls_set, size) = (must_nulls_set, may_nulls_set, new_size) let project ?(varAttr=[]) ?(typAttr=[]) _ t = t end -module AttributeConfiguredArrayDomain(Val: LatticeWithNull) (Idx:IntDomain.Z):S with type value = Val.t and type idx = Idx.t = +module FlagHelperAttributeConfiguredArrayDomain(Val: LatticeWithSmartOps) (Idx:IntDomain.Z):S with type value = Val.t and type idx = Idx.t = struct module P = PartitionedWithLength(Val)(Idx) module T = TrivialWithLength(Val)(Idx) module U = UnrollWithLength(Val)(Idx) - module N = NullByte(Val)(Idx) type idx = Idx.t type value = Val.t @@ -1438,7 +1483,6 @@ struct module I = struct include LatticeFlagHelper (T) (U) (K) let name () = "" end include LatticeFlagHelper (P) (I) (K) - (* include Lattice.Prod (LatticeFlagHelper (P) (I) (K)) (N) *) let domain_of_t = function | (Some p, None) -> PartitionedDomain @@ -1470,7 +1514,7 @@ struct let smart_widen f g = binop_to_t' (P.smart_widen f g) (T.smart_widen f g) (U.smart_widen f g) let smart_leq f g = binop' (P.smart_leq f g) (T.smart_leq f g) (U.smart_leq f g) let update_length newl x = unop_to_t' (P.update_length newl) (T.update_length newl) (U.update_length newl) x - let name () = "AttributeConfiguredArrayDomain" + let name () = "FlagHelperAttributeConfiguredArrayDomain" let bot () = to_t @@ match get_domain ~varAttr:[] ~typAttr:[] with | PartitionedDomain -> (Some (P.bot ()), None, None) @@ -1532,3 +1576,41 @@ struct | UnrolledDomain, (None, Some (None, Some x)) -> to_t @@ (None, None, Some x) | _ -> failwith "AttributeConfiguredArrayDomain received a value where not exactly one component is set" end + +module AttributeConfiguredArrayDomain (Val: LatticeWithNull) (Idx: IntDomain.Z): StrWithDomain with type value = Val.t and type idx = Idx.t = +struct + module F = FlagHelperAttributeConfiguredArrayDomain (Val) (Idx) + module N = NullByte (Val) (Idx) + + include Lattice.Prod (F) (N) + + let name () = "AttributeConfiguredArrayDomain" + type idx = Idx.t + type value = Val.t + + let domain_of_t (t_f, _) = F.domain_of_t t_f + + let get ?(checkBounds=true) (ask: VDQ.t) (t_f, t_n) i = Val.meet (F.get ask t_f i) (N.get ask t_n i) + let set (ask:VDQ.t) (t_f, t_n) i v = (F.set ask t_f i v, N.set ask t_n i v) + let make ?(varAttr=[]) ?(typAttr=[]) i v = (F.make i v, N.make i v) + let length (_, t_n) = N.length t_n + let move_if_affected ?(replace_with_const=false) (ask:VDQ.t) (t_f, t_n) v f = (F.move_if_affected ask t_f v f, N.move_if_affected ask t_n v f) + let get_vars_in_e (t_f, _) = F.get_vars_in_e t_f + let map f (t_f, t_n) = (F.map f t_f, N.map f t_n) + let fold_left f acc (t_f, t_n) = F.fold_left f acc t_f + + let smart_join x y (t_f1, t_n1) (t_f2, t_n2) = (F.smart_join x y t_f1 t_f2, N.smart_join x y t_n1 t_n2) + let smart_widen x y (t_f1, t_n1) (t_f2, t_n2) = (F.smart_widen x y t_f1 t_f2, N.smart_widen x y t_n1 t_n2) + let smart_leq x y (t_f1, t_n1) (t_f2, t_n2) = F.smart_leq x y t_f1 t_f2 && N.smart_leq x y t_n1 t_n2 + + let to_string (_, t_n) = (F.top (), N.to_string t_n) + let to_n_string (_, t_n) n = (F.top (), N.to_n_string t_n n) + let to_string_length (_, t_n) = N.to_string_length t_n + let string_copy (_, t_n1) (_, t_n2) n = (F.top (), N.string_copy t_n1 t_n2 n) + let string_concat (_, t_n1) (_, t_n2) n = (F.top (), N.string_concat t_n1 t_n2 n) + let substring_extraction (_, t_n1) (_, t_n2) = (F.top (), N.substring_extraction t_n1 t_n2) + let string_comparison (_, t_n1) (_, t_n2) n = N.string_comparison t_n1 t_n2 n + + let update_length newl (t_f, t_n) = (F.update_length newl t_f, N.update_length newl t_n) + let project ?(varAttr=[]) ?(typAttr=[]) ask (t_f, t_n) = (F.project ask t_f, N.project ask t_n) +end diff --git a/src/cdomains/arrayDomain.mli b/src/cdomains/arrayDomain.mli index 5df3679cfa..cd22a6a68b 100644 --- a/src/cdomains/arrayDomain.mli +++ b/src/cdomains/arrayDomain.mli @@ -2,7 +2,7 @@ open IntOps open GoblintCil module VDQ = ValueDomainQueries -type domain = TrivialDomain | PartitionedDomain | UnrolledDomain | NullByteDomain +type domain = TrivialDomain | PartitionedDomain | UnrolledDomain val get_domain: varAttr:Cil.attributes -> typAttr:Cil.attributes -> domain (** gets the underlying domain: chosen by the attributes in AttributeConfiguredArrayDomain *) @@ -10,8 +10,7 @@ val get_domain: varAttr:Cil.attributes -> typAttr:Cil.attributes -> domain val can_recover_from_top: domain -> bool (** Some domains such as Trivial cannot recover from their value ever being top. {!ValueDomain} handles intialization differently for these *) -(** Abstract domains representing arrays. *) -module type S = +module type SMinusDomain = sig include Lattice.S type idx @@ -20,9 +19,6 @@ sig type value (** The abstract domain of values stored in the array. *) - val domain_of_t: t -> domain - (* Returns the domain used for the array*) - val get: ?checkBounds:bool -> VDQ.t -> t -> Basetype.CilExp.t option * idx -> value (** Returns the element residing at the given index. *) @@ -58,17 +54,26 @@ sig val project: ?varAttr:Cil.attributes -> ?typAttr:Cil.attributes -> VDQ.t -> t -> t end +(** Abstract domains representing arrays. *) +module type S = +sig + include SMinusDomain + + val domain_of_t: t -> domain + (* Returns the domain used for the array*) +end + (** Abstract domains representing strings a.k.a. null-terminated char arrays. *) module type Str = sig - include S + include SMinusDomain val to_string: t -> t (** Returns an abstract value with at most one null byte marking the end of the string *) val to_n_string: t -> int -> t - (** [to_n_string index_set n no_null_warn] returns an abstract value with a potential null - * byte marking the end of the string and if needed followed by further null bytes to obtain + (** [to_n_string index_set n] returns an abstract value with a potential null byte + * marking the end of the string and if needed followed by further null bytes to obtain * an n bytes string. *) val to_string_length: t -> idx @@ -93,6 +98,14 @@ sig * only compares the first [n] bytes if present *) end +module type StrWithDomain = +sig + include Str + + val domain_of_t: t -> domain + (* Returns the domain used for the array*) +end + module type LatticeWithSmartOps = sig include Lattice.S @@ -103,7 +116,7 @@ end module type LatticeWithNull = sig - include Lattice.S + include LatticeWithSmartOps val null: unit -> t val not_null: unit -> t val is_null: t -> bool @@ -129,7 +142,7 @@ module Partitioned (Val: LatticeWithSmartOps) (Idx: IntDomain.Z): S with type va module PartitionedWithLength (Val: LatticeWithSmartOps) (Idx:IntDomain.Z): S with type value = Val.t and type idx = Idx.t (** Like partitioned but additionally manages the length of the array. *) -module NullByte (Val: Lattice.S) (Idx: IntDomain.Z): S with type value = Val.t and type idx = Idx.t +module NullByte (Val: LatticeWithNull) (Idx: IntDomain.Z): SMinusDomain with type value = Val.t and type idx = Idx.t (** This functor creates an array representation by the indexes of all null bytes * the array must and may contain. This is useful to analyze strings, i.e. null- * terminated char arrays, and particularly to determine if operations on strings @@ -137,6 +150,8 @@ module NullByte (Val: Lattice.S) (Idx: IntDomain.Z): S with type value = Val.t a * for this domain. It additionally tracks the array size. *) -module AttributeConfiguredArrayDomain(Val: LatticeWithSmartOps) (Idx:IntDomain.Z):S with type value = Val.t and type idx = Idx.t -(** Switches between PartitionedWithLength, TrivialWithLength and Unroll based on variable, type, and flag. - * Always runs NullByte in parallel. *) +module FlagHelperAttributeConfiguredArrayDomain (Val: LatticeWithSmartOps) (Idx: IntDomain.Z): S with type value = Val.t and type idx = Idx.t +(** Switches between PartitionedWithLength, TrivialWithLength and Unroll based on variable, type, and flag. *) + +module AttributeConfiguredArrayDomain (Val: LatticeWithNull) (Idx: IntDomain.Z): StrWithDomain with type value = Val.t and type idx = Idx.t +(** Like FlagHelperAttributeConfiguredArrayDomain but additionally runs NullByte in parallel. *) diff --git a/src/cdomains/valueDomain.ml b/src/cdomains/valueDomain.ml index 882b66859e..1826602b29 100644 --- a/src/cdomains/valueDomain.ml +++ b/src/cdomains/valueDomain.ml @@ -35,6 +35,10 @@ sig val is_top_value: t -> typ -> bool val zero_init_value: ?varAttr:attributes -> typ -> t + val null: unit -> t + val not_null: unit -> t + val is_null: t -> bool + val project: VDQ.t -> int_precision option-> ( attributes * attributes ) option -> t -> t val mark_jmpbufs_as_copied: t -> t end @@ -85,6 +89,8 @@ module rec Compound: S with type t = [ | `Thread of Threads.t | `JmpBuf of JmpBufs.t | `Mutex + | `NullByte + | `NotNullByte | `Bot ] and type offs = (fieldinfo,IndexDomain.t) Lval.offs = struct @@ -100,6 +106,8 @@ struct | `Thread of Threads.t | `JmpBuf of JmpBufs.t | `Mutex + | `NullByte + | `NotNullByte | `Bot ] [@@deriving eq, ord, hash] @@ -153,6 +161,8 @@ struct | `Thread x -> Threads.is_bot x | `JmpBuf x -> JmpBufs.is_bot x | `Mutex -> true + | `NullByte -> true (* TODO: is this correct? *) + | `NotNullByte -> true (* TODO: is this correct? *) | `Bot -> true | `Top -> false @@ -203,6 +213,8 @@ struct | `Thread x -> Threads.is_top x | `JmpBuf x -> JmpBufs.is_top x | `Mutex -> true + | `NullByte -> true + | `NotNullByte -> true | `Top -> true | `Bot -> false @@ -233,7 +245,7 @@ struct | _ -> `Top let tag_name : t -> string = function - | `Top -> "Top" | `Int _ -> "Int" | `Float _ -> "Float" | `Address _ -> "Address" | `Struct _ -> "Struct" | `Union _ -> "Union" | `Array _ -> "Array" | `Blob _ -> "Blob" | `Thread _ -> "Thread" | `Mutex -> "Mutex" | `JmpBuf _ -> "JmpBuf" | `Bot -> "Bot" + | `Top -> "Top" | `Int _ -> "Int" | `Float _ -> "Float" | `Address _ -> "Address" | `Struct _ -> "Struct" | `Union _ -> "Union" | `Array _ -> "Array" | `Blob _ -> "Blob" | `Thread _ -> "Thread" | `Mutex -> "Mutex" | `NullByte -> "NullByte" | `NotNullByte -> "NotNullByte" | `JmpBuf _ -> "JmpBuf" | `Bot -> "Bot" include Printable.Std let name () = "compound" @@ -248,6 +260,10 @@ struct let is_top x = x = `Top let top_name = "Unknown" + let null () = `NullByte + let not_null () = `NotNullByte + let is_null x = x = `NullByte + let pretty () state = match state with | `Int n -> ID.pretty () n @@ -260,6 +276,8 @@ struct | `Thread n -> Threads.pretty () n | `JmpBuf n -> JmpBufs.pretty () n | `Mutex -> text "mutex" + | `NullByte -> text "null-byte" + | `NotNullByte -> text "not-null-byte" | `Bot -> text bot_name | `Top -> text top_name @@ -275,6 +293,8 @@ struct | `Thread n -> Threads.show n | `JmpBuf n -> JmpBufs.show n | `Mutex -> "mutex" + | `NullByte -> "null-byte" + | `NotNullByte -> "not-null-byte" | `Bot -> bot_name | `Top -> top_name @@ -1131,6 +1151,8 @@ struct | `Thread n -> Threads.printXml f n | `JmpBuf n -> JmpBufs.printXml f n | `Mutex -> BatPrintf.fprintf f "\n\nmutex\n\n\n" + | `NullByte -> BatPrintf.fprintf f "\n\nnull-byte\n\n\n" + | `NotNullByte -> BatPrintf.fprintf f "\n\nnot-null-byte\n\n\n" | `Bot -> BatPrintf.fprintf f "\n\nbottom\n\n\n" | `Top -> BatPrintf.fprintf f "\n\ntop\n\n\n" @@ -1145,6 +1167,8 @@ struct | `Thread n -> Threads.to_yojson n | `JmpBuf n -> JmpBufs.to_yojson n | `Mutex -> `String "mutex" + | `NullByte -> `String "null-byte" + | `NotNullByte -> `String "not-null-byte" | `Bot -> `String "⊥" | `Top -> `String "⊤" @@ -1198,6 +1222,8 @@ struct | `Thread n -> `Thread (Threads.relift n) | `JmpBuf n -> `JmpBuf (JmpBufs.relift n) | `Mutex -> `Mutex + | `NullByte -> `NullByte + | `NotNullByte -> `NotNullByte | `Bot -> `Bot | `Top -> `Top end @@ -1208,7 +1234,7 @@ and Structs: StructDomain.S with type field = fieldinfo and type value = Compoun and Unions: UnionDomain.S with type t = UnionDomain.Field.t * Compound.t and type value = Compound.t = UnionDomain.Simple (Compound) -and CArrays: ArrayDomain.S with type value = Compound.t and type idx = ArrIdxDomain.t = ArrayDomain.AttributeConfiguredArrayDomain(Compound)(ArrIdxDomain) +and CArrays: ArrayDomain.StrWithDomain with type value = Compound.t and type idx = ArrIdxDomain.t = ArrayDomain.AttributeConfiguredArrayDomain(Compound)(ArrIdxDomain) and Blobs: Blob with type size = ID.t and type value = Compound.t and type origin = ZeroInit.t = Blob (Compound) (ID) From 2fe92c6889ce7b383e79a7e48629734289eee975 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Tue, 6 Jun 2023 12:52:16 +0200 Subject: [PATCH 0079/1312] added definition of V --- src/framework/constraints.ml | 52 +++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 37cf9a5f51..14b0126325 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1889,7 +1889,6 @@ module RecursionTermLifter (S: Spec) : Spec with module D = S.D and module G = S.G and module C = S.C - and module G = S.G = struct @@ -1912,7 +1911,8 @@ struct end module M = MapDomain.MapBot (CVal) (CVal) *) - module V = S.V + + module V = GVarF(S.V) (*(*TODO: do I need to change V???*) struct include Printable.Option (S.V) (struct let name = "RecursionTerm" end) @@ -1929,7 +1929,7 @@ struct end - module type FundecType = + (*module type FundecType = sig type t = fundec @@ -1941,7 +1941,7 @@ struct struct let getFundec = F let fname = F.fname - end + end*) (* Tuple of fundec and S.C*) module T = (*Todo: is this Printable.S or S.C*) @@ -1950,7 +1950,9 @@ struct type t = (fundec * S.C.t) let equal (a1, b1) (a2, b2) = if (a1 = a2) && (b1 = b2) then true else false - let show () = " " + let show (a, b) = a.fname ^ b.vname + let name () = "Tuple" + let to_yojson x = `String (show x) end (* Set of Tuples*) @@ -1962,6 +1964,7 @@ struct include Lattice.Prod (S.G) (M) let printXml f (d,m) = BatPrintf.fprintf f "\n%a\n%a\n" S.G.printXml d M.printXml m end*) + let name () = "RecursionTerm (" ^ S.name () ^ ")" type marshal = S.marshal @@ -1978,26 +1981,31 @@ struct if !AnalysisState.postsolving then sideg (f) (G.create_contexts (G.CSet.singleton c))*) - let query ctx = S.query (ctx) - let branch ctx = S.branch (ctx) - let assign ctx = S.assign (ctx) - let vdecl ctx = S.vdecl (ctx) + let conv (ctx: (_, _, _, V.t) ctx): (_, _, _, S.V.t) ctx = (*TODO Change the body*) + { ctx with + global = (fun v -> G.s (ctx.global (V.s v))); + sideg = (fun v g -> ctx.sideg (V.s v) (G.create_s g)); + } + let query ctx = S.query (conv ctx) + let branch ctx = S.branch (conv ctx) + let assign ctx = S.assign (conv ctx) + let vdecl ctx = S.vdecl (conv ctx) let enter ctx = if !AnalysisState.postsolving then printf "hallo hallo"; - S.enter (ctx) (*TODO*) - let paths_as_set ctx = S.paths_as_set (ctx) - let body ctx = S.body (ctx) - let return ctx = S.return (ctx) - let combine_env ctx = S.combine_env (ctx) - let combine_assign ctx = S.combine_assign (ctx) - let special ctx = S.special (ctx) - let threadenter ctx = S.threadenter (ctx) - let threadspawn ctx lv f args fctx = S.threadspawn (ctx) lv f args (fctx) - let sync ctx = S.sync (ctx) - let skip ctx = S.skip (ctx) - let asm ctx = S.asm (ctx) - let event ctx e octx = S.event (ctx) e (octx) + S.enter (conv ctx) (*TODO*) + let paths_as_set ctx = S.paths_as_set (conv ctx) + let body ctx = S.body (conv ctx) + let return ctx = S.return (conv ctx) + let combine_env ctx = S.combine_env (conv ctx) + let combine_assign ctx = S.combine_assign (conv ctx) + let special ctx = S.special (conv ctx) + let threadenter ctx = S.threadenter (conv ctx) + let threadspawn ctx lv f args fctx = S.threadspawn (conv ctx) lv f args (conv fctx) + let sync ctx = S.sync (conv ctx) + let skip ctx = S.skip (conv ctx) + let asm ctx = S.asm (conv ctx) + let event ctx e octx = S.event (conv ctx) e (conv octx) end From a912463b2780fe4256cd82efb421cdf96f0a526d Mon Sep 17 00:00:00 2001 From: Nathan Schmidt Date: Tue, 6 Jun 2023 16:25:32 +0200 Subject: [PATCH 0080/1312] Addressed github-code-scanning suggestions --- src/cdomains/arrayDomain.ml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 98a981f63b..3f6dcdce7f 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -991,7 +991,7 @@ struct if Z.gt i max then true else if MustNulls.mem i must_nulls_set then - all_indexes_must_null (Z.add i Z.one) max + all_indexes_must_null (Z.succ i) max else false in let min interval = match Idx.minimal interval with @@ -1044,7 +1044,7 @@ struct if Z.gt i max then may_nulls_set else - add_indexes (Z.add i Z.one) max (MayNulls.add i may_nulls_set) in + add_indexes (Z.succ i) max (MayNulls.add i may_nulls_set) in let min interval = match Idx.minimal interval with | Some min_num -> if Z.lt min_num Z.zero then @@ -1114,7 +1114,7 @@ struct if Z.equal min_i Z.zero && Z.geq max_i max_size then MayNulls.top () else if Z.geq max_i max_size then - add_indexes min_i (Z.sub max_size Z.one) may_nulls_set + add_indexes min_i (Z.pred max_size) may_nulls_set else add_indexes min_i max_i may_nulls_set in @@ -1129,7 +1129,7 @@ struct (* ... and there is no maximal size, modify may_nulls_set to top *) | None -> (must_nulls_set, MayNulls.top (), size) (* ..., add all i from minimal index to maximal size to may_nulls_set *) - | Some max_size -> (must_nulls_set, add_indexes min_i (Z.sub max_size Z.one) may_nulls_set, size) + | Some max_size -> (must_nulls_set, add_indexes min_i (Z.pred max_size) may_nulls_set, size) (* ... and value <> null, only keep indexes < minimal index in must_nulls_set *) else (MustNulls.filter (Z.gt min_i) must_nulls_set, may_nulls_set, size) @@ -1208,17 +1208,17 @@ struct let min_must_null = MustNulls.min_elt must_nulls_set in (* if smallest index in sets coincides, only this null byte is kept in both sets *) if Z.equal min_must_null (MayNulls.min_elt may_nulls_set) then - (MustNulls.singleton min_must_null, MayNulls.singleton min_must_null, Idx.of_int !Cil.kindOfSizeOf (Z.add min_must_null Z.one)) + (MustNulls.singleton min_must_null, MayNulls.singleton min_must_null, Idx.of_int !Cil.kindOfSizeOf (Z.succ min_must_null)) (* else return empty must_nulls_set and keep every index up to smallest index of must_nulls_set included in may_nulls_set *) else - (MustNulls.empty (), MayNulls.filter (Z.geq min_must_null) may_nulls_set, Idx.of_int !Cil.kindOfSizeOf (Z.add min_must_null Z.one)) + (MustNulls.empty (), MayNulls.filter (Z.geq min_must_null) may_nulls_set, Idx.of_int !Cil.kindOfSizeOf (Z.succ min_must_null)) let to_n_string (must_nulls_set, may_nulls_set, size) n = let rec add_indexes i max set = if Z.geq i max then set else - add_indexes (Z.add i Z.one) max (MayNulls.add i set) in + add_indexes (Z.succ i) max (MayNulls.add i set) in let update_must_indexes min_must_null must_nulls_set = if Z.equal min_must_null Z.zero then MustNulls.bot () From cbf31335fb1341cabe90f982e80b6db6b5c71c1f Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Tue, 6 Jun 2023 16:50:45 +0200 Subject: [PATCH 0081/1312] WIP on check_bounded --- src/analyses/termination_new.ml | 8 +++++--- src/cdomains/intDomain.ml | 13 +++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index 936471ceaf..34e25d49d3 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -18,11 +18,13 @@ let is_loop_exit_indicator (x : varinfo) = (** Checks whether a variable can be bounded *) let check_bounded ctx varinfo = + let open IntDomain.IntDomTuple in (* TODO: Remove *) + let open Cil in let exp = Lval (Var varinfo, NoOffset) in match ctx.ask (EvalInt exp) with - `Top -> false - | `Bot -> raise (PreProcessing "Loop variable is Bot") - | _ -> true (* TODO: Is this sound? *) + `Top -> print_endline (varinfo.vname ^ " is TOP"); false + | `Bot -> print_endline (varinfo.vname ^ " is BOT"); raise (PreProcessing "Loop variable is Bot") + | `Lifted v -> print_endline (varinfo.vname ^ " is " ^ IntDomain.IntDomTuple.show v); not (is_top v) (* TODO: Is this sound? *) module Spec : Analyses.MCPSpec = struct diff --git a/src/cdomains/intDomain.ml b/src/cdomains/intDomain.ml index 3b1eecc27d..dea3daecd8 100644 --- a/src/cdomains/intDomain.ml +++ b/src/cdomains/intDomain.ml @@ -219,6 +219,7 @@ sig val ending : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t val of_int: Cil.ikind -> int_t -> t val of_bool: Cil.ikind -> bool -> t + val to_interval: t -> (int_t * int_t) option val of_interval: ?suppress_ovwarn:bool -> Cil.ikind -> int_t * int_t -> t val of_congruence: Cil.ikind -> int_t * int_t -> t val is_top_of: Cil.ikind -> t -> bool @@ -349,6 +350,8 @@ struct with Failure _ -> top_of ik + let to_interval = failwith "Not implemented!" (* FIXME *) + let starting ?(suppress_ovwarn=false) ik x = try Old.starting ~suppress_ovwarn ik (BI.to_int64 x) with Failure _ -> top_of ik let ending ?(suppress_ovwarn=false) ik x = @@ -428,6 +431,7 @@ struct let of_excl_list ikind is = {v = I.of_excl_list ikind is; ikind} let is_excl_list x = I.is_excl_list x.v let to_incl_list x = I.to_incl_list x.v + let to_interval x = I.to_interval x.v let of_interval ?(suppress_ovwarn=false) ikind (lb,ub) = {v = I.of_interval ~suppress_ovwarn ikind (lb,ub); ikind} let of_congruence ikind (c,m) = {v = I.of_congruence ikind (c,m); ikind} let starting ?(suppress_ovwarn=false) ikind i = {v = I.starting ~suppress_ovwarn ikind i; ikind} @@ -708,6 +712,7 @@ struct (* TODO: change to_int signature so it returns a big_int *) let to_int x = Option.bind x (IArith.to_int) + let to_interval x = x let of_interval ?(suppress_ovwarn=false) ik (x,y) = norm ~suppress_ovwarn ik @@ Some (x,y) let of_int ik (x: int_t) = of_interval ik (x,x) let zero = Some IArith.zero @@ -1265,6 +1270,8 @@ struct let of_bool _ = function true -> one | false -> zero + let to_interval = failwith "Not implemented!" (* FIXME *) + let of_interval ?(suppress_ovwarn=false) ik (x,y) = norm_interval ~suppress_ovwarn ~cast:false ik (x,y) let of_int ik (x: int_t) = of_interval ik (x, x) @@ -2137,6 +2144,7 @@ struct let top_bool = `Excluded (S.empty (), R.of_interval range_ikind (0L, 1L)) let of_interval ?(suppress_ovwarn=false) ik (x,y) = if BigInt.compare x y = 0 then of_int ik x else top_of ik + let to_interval = failwith "Not implemented!" (* FIXME *) let starting ?(suppress_ovwarn=false) ikind x = if BigInt.compare x BigInt.zero > 0 then not_zero ikind else top_of ikind let ending ?(suppress_ovwarn=false) ikind x = if BigInt.compare x BigInt.zero < 0 then not_zero ikind else top_of ikind @@ -2529,6 +2537,7 @@ module Enums : S with type int_t = BigInt.t = struct let of_int ikind x = cast_to ikind (Inc (BISet.singleton x)) let of_interval ?(suppress_ovwarn=false) ik (x,y) = if x = y then of_int ik x else top_of ik + let to_interval = failwith "Not implemented!" (* FIXME *) let join ik = curry @@ function | Inc x, Inc y -> Inc (BISet.union x y) @@ -3263,6 +3272,8 @@ struct let refine_with_incl_list ik a b = a let project ik p t = t + + let to_interval = failwith "Not implemented!" (* FIXME *) end module SOverflowLifter (D : S) : SOverflow with type int_t = D.int_t and type t = D.t = struct @@ -3495,6 +3506,8 @@ module IntDomTupleImpl = struct let flat f x = match to_list_some x with [] -> None | xs -> Some (f xs) + let to_interval (_, i, _, _, _) = I2.to_interval i + let to_excl_list x = let merge ps = let (vs, rs) = List.split ps in From 6d7ec8cd8ec52c994ba6419048ffa0798645e204 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Wed, 7 Jun 2023 15:52:11 +0200 Subject: [PATCH 0082/1312] IT's wOrkingggg :)git status; hopefully V and G are correct now... some functions definitely must be redefined --- src/framework/analyses.ml | 76 +++++++++++++++++++++++++++++++ src/framework/constraints.ml | 86 ++++++++++++++++-------------------- 2 files changed, 114 insertions(+), 48 deletions(-) diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 1d1972ac45..286c33870d 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -120,6 +120,82 @@ struct end +module C_ (C: Printable.S)= + struct + include C + include Printable.Std (* To make it Groupable *) + let printXml f c = BatPrintf.fprintf f "%a" printXml c (* wrap in for HTML printing *) + + end + +(* Tuple of fundec and S.C*) +module T (Base1: Printable.S) (Base2: Printable.S) (C: Printable.S) = (*Todo: is this Printable.S or S.C*) +struct + include Printable.Std + type t = (CilType.Fundec.t * C.t) + + let equal (a1, b1) (a2, b2) = if (a1 = a2) && (b1 = b2) then true else false + let show (a,b) = (Base1.show a) ^ (Base2.show b) + let name () = "Tuple" + let to_yojson x = `String (show x) + let relift (a,b) = (a,b) (*Todo: is this correct?*) + let printXml f (a,b) = Base1.printXml f a (*Todo: what do we have to put here?*) + let compare (a1,b1) (a2,b2) = 3 (*Todo: what do we have to put here?*) + (*let a = Base1.compare a1 a2 in + let b = Base2.compare b1 b2 in + *) + let pretty () (a,b) = Base1.pretty () a(*Todo: what do we have to put here?*) + + let hash (a,b) = 2 (*Todo: what do we have to put here?*) + +end + +module GVarGG (G: Lattice.S) (C: Printable.S) = + struct + module CSet = + struct + include SetDomain.Make ( + struct + include (T (CilType.Fundec) (C) (C)) (* Set of Tuples*) + end + ) + let name () = "contexts" + end + + module CMap = + struct + include MapDomain.MapBot (C_ (C)) (CSet) + let printXml f c = BatPrintf.fprintf f "%a" printXml c (* TODO *) + let printXml_ f c = BatPrintf.fprintf f "%a" CSet.printXml c (* TODO *) + end + + include Lattice.Lift2 (G) (CMap) (Printable.DefaultNames) + + let spec = function + | `Bot -> G.bot () + | `Lifted1 x -> x + | _ -> failwith "GVarGG.spec" + let contexts = function + | `Bot -> CSet.bot () + | `Lifted2 x -> x + | _ -> failwith "GVarGG.contexts" + let create_spec spec = `Lifted1 spec + let create_contexts contexts = `Lifted2 contexts + + let printXml f = function + | `Lifted1 x -> G.printXml f x + | `Lifted2 x -> BatPrintf.fprintf f "%a" CMap.printXml x + | x -> BatPrintf.fprintf f "%a" printXml x + + let s = function (*TODO: does this work? copied from DeadBranch*) + | `Bot -> G.bot () + | `Lifted1 x -> x + | _ -> failwith "RecursionTerm.s" + + let create_s s = `Lifted1 s (*TODO: does this work? copied from DeadBranch*) + end + + module GMapG (G: Lattice.S) (C: Printable.S) = struct module CVal = diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 14b0126325..93aa636847 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1887,20 +1887,34 @@ end (** Add cycle detection in the function call graph to a analysis *) module RecursionTermLifter (S: Spec) : Spec with module D = S.D - and module G = S.G and module C = S.C + and module G = GVarGG (S.G) (S.C) = struct module C = S.C module P = S.P module D = S.D + (*GMapG (S.G) (S.C)*) + (*struct + include Lattice.Prod (S.G) (M) + let printXml f (d,m) = BatPrintf.fprintf f "\n%a\n%a\n" S.G.printXml d M.printXml m + end*) - (*global invariant - - fundec -> Map (S.C) (Set (fundec * S.C)) - So: g -> {c' -> f, c} - in case f, c --> g, c' *) + + (*module type FundecType = + sig + type t = fundec + + val getFundec: t -> fundec + (* Define any other values or types exposed by the module *) + end + module Fundec (F:fundec) : FundecType = + struct + let getFundec = F + let fname = F.fname + end*) (* module CVal = @@ -1910,61 +1924,36 @@ struct let printXml f c = BatPrintf.fprintf f "%a" printXml c (* wrap in for HTML printing *) end module M = MapDomain.MapBot (CVal) (CVal) -*) - - module V = GVarF(S.V) - (*(*TODO: do I need to change V???*) +*) (*(*TODO: do I need to change V???*) struct include Printable.Option (S.V) (struct let name = "RecursionTerm" end) let name () = "RecursionTerm" let is_write_only t = true let s x = `Left x end*) + (*include Lattice.Lift2 (S.G) (MapDomain.MapBot (C_) (TSet)) (Printable.DefaultNames) (*TODO: does MapBot fit?*) + let s = function + | `Bot -> S.G.bot () + | `Lifted1 x -> x + | _ -> failwith "RecursionTerm.s" + end*) - module C_ = - struct - include S.C - include Printable.Std (* To make it Groupable *) - let printXml f c = BatPrintf.fprintf f "%a" printXml c (* wrap in for HTML printing *) - - end - - (*module type FundecType = - sig - type t = fundec - - val getFundec: t -> fundec - (* Define any other values or types exposed by the module *) - end - module Fundec (F:fundec) : FundecType = + module V = struct - let getFundec = F - let fname = F.fname - end*) - - (* Tuple of fundec and S.C*) - module T = (*Todo: is this Printable.S or S.C*) - struct - include Printable.Std - type t = (fundec * S.C.t) - - let equal (a1, b1) (a2, b2) = if (a1 = a2) && (b1 = b2) then true else false - let show (a, b) = a.fname ^ b.vname - let name () = "Tuple" - let to_yojson x = `String (show x) + include GVarF(S.V) + let s x = `Left x end - (* Set of Tuples*) - module TSet = SetDomain.Make (T) - module G = S.G(*Lattice.Lift2 (S.G) (MapDomain.MapBot (C_) (TSet)) (Printable.DefaultNames) (*TODO: does MapBot fit?*)*) - (*GMapG (S.G) (S.C)*) - (*struct - include Lattice.Prod (S.G) (M) - let printXml f (d,m) = BatPrintf.fprintf f "\n%a\n%a\n" S.G.printXml d M.printXml m - end*) + (*global invariant + - fundec -> Map (S.C) (Set (fundec * S.C)) + So: g -> {c' -> f, c} + in case f, c --> g, c' *) + module G = GVarGG (S.G) (S.C) + + let name () = "RecursionTerm (" ^ S.name () ^ ")" type marshal = S.marshal @@ -1981,7 +1970,8 @@ struct if !AnalysisState.postsolving then sideg (f) (G.create_contexts (G.CSet.singleton c))*) - let conv (ctx: (_, _, _, V.t) ctx): (_, _, _, S.V.t) ctx = (*TODO Change the body*) + (*TODO Change the body??*) + let conv (ctx: (_, _, _, V.t) ctx): (_, _, _, S.V.t) ctx = { ctx with global = (fun v -> G.s (ctx.global (V.s v))); sideg = (fun v g -> ctx.sideg (V.s v) (G.create_s g)); From aa1c30a3a7ce2482155dcf682fdfd6420dbca72e Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Wed, 7 Jun 2023 16:12:15 +0200 Subject: [PATCH 0083/1312] changed indentation --- src/framework/analyses.ml | 92 ++++++++++++++-------------- src/framework/constraints.ml | 25 ++++---- src/util/terminationPreprocessing.ml | 1 + 3 files changed, 59 insertions(+), 59 deletions(-) diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 286c33870d..550adb7b16 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -121,12 +121,12 @@ end module C_ (C: Printable.S)= - struct - include C - include Printable.Std (* To make it Groupable *) - let printXml f c = BatPrintf.fprintf f "%a" printXml c (* wrap in for HTML printing *) - - end +struct + include C + include Printable.Std (* To make it Groupable *) + let printXml f c = BatPrintf.fprintf f "%a" printXml c (* wrap in for HTML printing *) + +end (* Tuple of fundec and S.C*) module T (Base1: Printable.S) (Base2: Printable.S) (C: Printable.S) = (*Todo: is this Printable.S or S.C*) @@ -142,7 +142,7 @@ struct let printXml f (a,b) = Base1.printXml f a (*Todo: what do we have to put here?*) let compare (a1,b1) (a2,b2) = 3 (*Todo: what do we have to put here?*) (*let a = Base1.compare a1 a2 in - let b = Base2.compare b1 b2 in + let b = Base2.compare b1 b2 in *) let pretty () (a,b) = Base1.pretty () a(*Todo: what do we have to put here?*) @@ -151,49 +151,49 @@ struct end module GVarGG (G: Lattice.S) (C: Printable.S) = +struct + module CSet = struct - module CSet = - struct - include SetDomain.Make ( - struct - include (T (CilType.Fundec) (C) (C)) (* Set of Tuples*) - end - ) - let name () = "contexts" - end + include SetDomain.Make ( + struct + include (T (CilType.Fundec) (C) (C)) (* Set of Tuples*) + end + ) + let name () = "contexts" + end - module CMap = - struct - include MapDomain.MapBot (C_ (C)) (CSet) - let printXml f c = BatPrintf.fprintf f "%a" printXml c (* TODO *) - let printXml_ f c = BatPrintf.fprintf f "%a" CSet.printXml c (* TODO *) - end + module CMap = + struct + include MapDomain.MapBot (C_ (C)) (CSet) + let printXml f c = BatPrintf.fprintf f "%a" printXml c (* TODO *) + let printXml_ f c = BatPrintf.fprintf f "%a" CSet.printXml c (* TODO *) + end - include Lattice.Lift2 (G) (CMap) (Printable.DefaultNames) - - let spec = function - | `Bot -> G.bot () - | `Lifted1 x -> x - | _ -> failwith "GVarGG.spec" - let contexts = function - | `Bot -> CSet.bot () - | `Lifted2 x -> x - | _ -> failwith "GVarGG.contexts" - let create_spec spec = `Lifted1 spec - let create_contexts contexts = `Lifted2 contexts - - let printXml f = function - | `Lifted1 x -> G.printXml f x - | `Lifted2 x -> BatPrintf.fprintf f "%a" CMap.printXml x - | x -> BatPrintf.fprintf f "%a" printXml x - - let s = function (*TODO: does this work? copied from DeadBranch*) - | `Bot -> G.bot () - | `Lifted1 x -> x - | _ -> failwith "RecursionTerm.s" + include Lattice.Lift2 (G) (CMap) (Printable.DefaultNames) - let create_s s = `Lifted1 s (*TODO: does this work? copied from DeadBranch*) - end + let spec = function + | `Bot -> G.bot () + | `Lifted1 x -> x + | _ -> failwith "GVarGG.spec" + let contexts = function + | `Bot -> CSet.bot () + | `Lifted2 x -> x + | _ -> failwith "GVarGG.contexts" + let create_spec spec = `Lifted1 spec + let create_contexts contexts = `Lifted2 contexts + + let printXml f = function + | `Lifted1 x -> G.printXml f x + | `Lifted2 x -> BatPrintf.fprintf f "%a" CMap.printXml x + | x -> BatPrintf.fprintf f "%a" printXml x + + let s = function (*TODO: does this work? copied from DeadBranch*) + | `Bot -> G.bot () + | `Lifted1 x -> x + | _ -> failwith "RecursionTerm.s" + + let create_s s = `Lifted1 s (*TODO: does this work? copied from DeadBranch*) +end module GMapG (G: Lattice.S) (C: Printable.S) = diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 93aa636847..fde22c4c20 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1883,19 +1883,7 @@ struct let event ctx e octx = S.event (conv ctx) e (conv octx) end *) - -(** Add cycle detection in the function call graph to a analysis *) -module RecursionTermLifter (S: Spec) - : Spec with module D = S.D - and module C = S.C - and module G = GVarGG (S.G) (S.C) -= - -struct - module C = S.C - module P = S.P - module D = S.D - (*GMapG (S.G) (S.C)*) + (*GMapG (S.G) (S.C)*) (*struct include Lattice.Prod (S.G) (M) let printXml f (d,m) = BatPrintf.fprintf f "\n%a\n%a\n" S.G.printXml d M.printXml m @@ -1938,7 +1926,18 @@ struct | _ -> failwith "RecursionTerm.s" end*) +(** Add cycle detection in the function call graph to a analysis *) +module RecursionTermLifter (S: Spec) + : Spec with module D = S.D + and module C = S.C + and module G = GVarGG (S.G) (S.C) += +struct + module C = S.C + module P = S.P + module D = S.D + module V = struct include GVarF(S.V) diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index 684733c05f..5f96483af4 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -35,6 +35,7 @@ class loopCounterVisitor lc lg le (fd : fundec) = object(self) | Loop (b, loc, eloc, _, _) -> let name = "term"^show_location_id loc in let typ = Cil.intType in + (* NOT tested for TODOOOOO*) let v = (Cil.makeLocalVar fd name typ) in let init_stmt = mkStmtOneInstr @@ Set (var v, zero, loc, eloc) in let inc_stmt = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in From 5f3ae26b7c8d58a33208373a9ba1791b56b20eb6 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Wed, 7 Jun 2023 17:23:29 +0200 Subject: [PATCH 0084/1312] intendation --- src/framework/analyses.ml | 2 +- src/framework/constraints.ml | 15 +++++++-------- src/util/terminationPreprocessing.ml | 3 +-- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 550adb7b16..52836f979b 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -158,7 +158,7 @@ struct struct include (T (CilType.Fundec) (C) (C)) (* Set of Tuples*) end - ) + ) let name () = "contexts" end diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index fde22c4c20..88afb5ca91 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1883,14 +1883,14 @@ struct let event ctx e octx = S.event (conv ctx) e (conv octx) end *) - (*GMapG (S.G) (S.C)*) - (*struct +(*GMapG (S.G) (S.C)*) +(*struct include Lattice.Prod (S.G) (M) let printXml f (d,m) = BatPrintf.fprintf f "\n%a\n%a\n" S.G.printXml d M.printXml m - end*) +end*) - (*module type FundecType = +(*module type FundecType = sig type t = fundec @@ -1911,20 +1911,19 @@ end include Printable.Std (* To make it Groupable *) let printXml f c = BatPrintf.fprintf f "%a" printXml c (* wrap in for HTML printing *) end - module M = MapDomain.MapBot (CVal) (CVal) -*) (*(*TODO: do I need to change V???*) + module M = MapDomain.MapBot (CVal) (CVal)*) (*(*TODO: do I need to change V???*) struct include Printable.Option (S.V) (struct let name = "RecursionTerm" end) let name () = "RecursionTerm" let is_write_only t = true let s x = `Left x end*) - (*include Lattice.Lift2 (S.G) (MapDomain.MapBot (C_) (TSet)) (Printable.DefaultNames) (*TODO: does MapBot fit?*) +(*include Lattice.Lift2 (S.G) (MapDomain.MapBot (C_) (TSet)) (Printable.DefaultNames) (*TODO: does MapBot fit?*) let s = function | `Bot -> S.G.bot () | `Lifted1 x -> x | _ -> failwith "RecursionTerm.s" - end*) +end*) (** Add cycle detection in the function call graph to a analysis *) module RecursionTermLifter (S: Spec) diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index 5f96483af4..da4fd2e10b 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -35,8 +35,7 @@ class loopCounterVisitor lc lg le (fd : fundec) = object(self) | Loop (b, loc, eloc, _, _) -> let name = "term"^show_location_id loc in let typ = Cil.intType in - (* NOT tested for TODOOOOO*) - let v = (Cil.makeLocalVar fd name typ) in + let v = (Cil.makeLocalVar fd name typ) in (* NOT tested for TODOOOOO*) let init_stmt = mkStmtOneInstr @@ Set (var v, zero, loc, eloc) in let inc_stmt = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in let exit_stmt = mkStmtOneInstr @@ Set ((var !le), (Lval (var v)), loc, eloc) in From 5717a43867f908989481b5807d3d54b2fd1088c0 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Wed, 7 Jun 2023 17:31:12 +0200 Subject: [PATCH 0085/1312] indent --- src/framework/analyses.ml | 2 +- src/framework/constraints.ml | 4 ++-- src/util/terminationPreprocessing.ml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 52836f979b..2f3f4b34a8 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -158,7 +158,7 @@ struct struct include (T (CilType.Fundec) (C) (C)) (* Set of Tuples*) end - ) + ) let name () = "contexts" end diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 88afb5ca91..774c0abc95 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1887,7 +1887,7 @@ end (*struct include Lattice.Prod (S.G) (M) let printXml f (d,m) = BatPrintf.fprintf f "\n%a\n%a\n" S.G.printXml d M.printXml m -end*) + end*) (*module type FundecType = @@ -1923,7 +1923,7 @@ end*) | `Bot -> S.G.bot () | `Lifted1 x -> x | _ -> failwith "RecursionTerm.s" -end*) + end*) (** Add cycle detection in the function call graph to a analysis *) module RecursionTermLifter (S: Spec) diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index da4fd2e10b..749b9f2f8e 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -35,7 +35,7 @@ class loopCounterVisitor lc lg le (fd : fundec) = object(self) | Loop (b, loc, eloc, _, _) -> let name = "term"^show_location_id loc in let typ = Cil.intType in - let v = (Cil.makeLocalVar fd name typ) in (* NOT tested for TODOOOOO*) + let v = (Cil.makeLocalVar fd name typ) in (* NOT tested for TODOOOOO*) let init_stmt = mkStmtOneInstr @@ Set (var v, zero, loc, eloc) in let inc_stmt = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in let exit_stmt = mkStmtOneInstr @@ Set ((var !le), (Lval (var v)), loc, eloc) in From 856dccb47d6fd05498a9e01d541da00ccdfe2301 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Wed, 7 Jun 2023 18:58:53 +0300 Subject: [PATCH 0086/1312] Add fopencookie to libraryFunctions --- src/analyses/libraryFunctions.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 3eacf9013a..93eb75c09b 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -257,6 +257,7 @@ let glibc_desc_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("strsep", unknown [drop "stringp" [r_deep; w]; drop "delim" [r]]); ("strcasestr", unknown [drop "haystack" [r]; drop "needle" [r]]); ("inet_aton", unknown [drop "cp" [r]; drop "inp" [w]]); + ("fopencookie", unknown [drop "cookie" []; drop "mode" [r]; drop "io_funcs" [s_deep]]); (* doesn't access cookie but passes it to io_funcs *) ] let linux_userspace_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ From 8753565a893c93e399fc233dd3f0234a86e0cf62 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Wed, 7 Jun 2023 19:04:03 +0300 Subject: [PATCH 0087/1312] Remove functions already defined in libraries from invalidate_actions --- src/analyses/libraryFunctions.ml | 48 -------------------------------- 1 file changed, 48 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 93eb75c09b..5a16c9edcf 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -671,10 +671,7 @@ let invalidate_actions = [ "_spin_lock", readsAll;(*safe*) "_spin_unlock", readsAll;(*safe*) "_spin_lock_irqsave", readsAll;(*safe*) - "_spin_unlock_irqrestore", readsAll;(*safe*) - "pthread_mutex_init", readsAll;(*safe*) "pthread_mutex_destroy", readsAll;(*safe*) - "pthread_mutexattr_settype", readsAll;(*safe*) "pthread_mutexattr_init", readsAll;(*safe*) "pthread_spin_init", readsAll;(*safe*) "pthread_spin_destroy", readsAll;(*safe*) @@ -718,7 +715,6 @@ let invalidate_actions = [ "getc", writesAll;(*unsafe*) "_IO_getc", writesAll;(*unsafe*) "closedir", writesAll;(*unsafe*) - "setrlimit", readsAll;(*safe*) "chdir", readsAll;(*safe*) "pipe", writesAll;(*unsafe*) "close", writesAll;(*unsafe*) @@ -732,9 +728,6 @@ let invalidate_actions = [ "pthread_attr_getstacksize", readsAll;(*safe*) "pthread_attr_getscope", readsAll;(*safe*) "pthread_cond_init", readsAll; (*safe*) - "pthread_cond_wait", readsAll; (*safe*) - "pthread_cond_signal", readsAll;(*safe*) - "pthread_cond_broadcast", readsAll;(*safe*) "pthread_cond_destroy", readsAll;(*safe*) "__pthread_cond_init", readsAll; (*safe*) "__pthread_cond_wait", readsAll; (*safe*) @@ -747,7 +740,6 @@ let invalidate_actions = [ "pthread_sigmask", writesAllButFirst 2 readsAll;(*unsafe*) "raise", writesAll;(*unsafe*) "_strlen", readsAll;(*safe*) - "__builtin_object_size", readsAll;(*safe*) "__builtin_alloca", readsAll;(*safe*) "dlopen", readsAll;(*safe*) "dlsym", readsAll;(*safe*) @@ -827,7 +819,6 @@ let invalidate_actions = [ "usleep", readsAll; "svc_run", writesAll;(*unsafe*) "dup", readsAll; (*safe*) - "__builtin_expect", readsAll; (*safe*) "vsnprintf", writesAllButFirst 3 readsAll; (*drop 3*) "__builtin___vsnprintf", writesAllButFirst 3 readsAll; (*drop 3*) "__builtin___vsnprintf_chk", writesAllButFirst 3 readsAll; (*drop 3*) @@ -852,7 +843,6 @@ let invalidate_actions = [ "fgets", writes [1;3]; (*keep [3]*) "__fgets_alias", writes [1;3]; (*keep [3]*) "__fgets_chk", writes [1;3]; (*keep [3]*) - "strtoul", readsAll; (*safe*) "__tolower", readsAll; (*safe*) "signal", writesAll; (*unsafe*) "strsignal", readsAll; @@ -882,12 +872,10 @@ let invalidate_actions = [ "sem_wait", readsAll; (*safe*) "sem_post", readsAll; (*safe*) "PL_NewHashTable", readsAll; (*safe*) - "__assert_fail", readsAll; (*safe*) "assert_failed", readsAll; (*safe*) "htonl", readsAll; (*safe*) "htons", readsAll; (*safe*) "ntohl", readsAll; (*safe*) - "htons", readsAll; (*safe*) "munmap", readsAll;(*safe*) "mmap", readsAll;(*safe*) "clock", readsAll; @@ -908,15 +896,12 @@ let invalidate_actions = [ "__open_too_many_args", readsAll; "usb_submit_urb", readsAll; (* first argument is written to but according to specification must not be read from anymore *) "dev_driver_string", readsAll; - "dev_driver_string", readsAll; "__spin_lock_init", writes [1]; "kmem_cache_create", readsAll; "idr_pre_get", readsAll; "zil_replay", writes [1;2;3;5]; "__VERIFIER_nondet_int", readsAll; (* no args, declare invalidate actions to prevent invalidating globals when extern in regression tests *) (* no args, declare invalidate actions to prevent invalidating globals *) - "__VERIFIER_atomic_begin", readsAll; - "__VERIFIER_atomic_end", readsAll; "isatty", readsAll; "setpriority", readsAll; "getpriority", readsAll; @@ -927,42 +912,24 @@ let invalidate_actions = [ "sema_init", readsAll; "down_trylock", readsAll; "up", readsAll; - "acos", readsAll; - "acosf", readsAll; "acosh", readsAll; "acoshf", readsAll; "acoshl", readsAll; - "acosl", readsAll; - "asin", readsAll; - "asinf", readsAll; "asinh", readsAll; "asinhf", readsAll; "asinhl", readsAll; - "asinl", readsAll; - "atan", readsAll; - "atan2", readsAll; - "atan2f", readsAll; - "atan2l", readsAll; - "atanf", readsAll; "atanh", readsAll; "atanhf", readsAll; "atanhl", readsAll; - "atanl", readsAll; "cbrt", readsAll; "cbrtf", readsAll; "cbrtl", readsAll; - "ceil", readsAll; - "ceilf", readsAll; - "ceill", readsAll; "copysign", readsAll; "copysignf", readsAll; "copysignl", readsAll; - "cos", readsAll; - "cosf", readsAll; "cosh", readsAll; "coshf", readsAll; "coshl", readsAll; - "cosl", readsAll; "erf", readsAll; "erfc", readsAll; "erfcf", readsAll; @@ -984,12 +951,6 @@ let invalidate_actions = [ "fma", readsAll; "fmaf", readsAll; "fmal", readsAll; - "fmax", readsAll; - "fmaxf", readsAll; - "fmaxl", readsAll; - "fmin", readsAll; - "fminf", readsAll; - "fminl", readsAll; "fmod", readsAll; "fmodf", readsAll; "fmodl", readsAll; @@ -1041,9 +1002,6 @@ let invalidate_actions = [ "modf", readsAll; "modff", readsAll; "modfl", readsAll; - "nan", readsAll; - "nanf", readsAll; - "nanl", readsAll; "nearbyint", readsAll; "nearbyintf", readsAll; "nearbyintl", readsAll; @@ -1074,21 +1032,15 @@ let invalidate_actions = [ "scalbn", readsAll; "scalbnf", readsAll; "scalbnl", readsAll; - "sin", readsAll; - "sinf", readsAll; "sinh", readsAll; "sinhf", readsAll; "sinhl", readsAll; - "sinl", readsAll; "sqrt", readsAll; "sqrtf", readsAll; "sqrtl", readsAll; - "tan", readsAll; - "tanf", readsAll; "tanh", readsAll; "tanhf", readsAll; "tanhl", readsAll; - "tanl", readsAll; "tgamma", readsAll; "tgammaf", readsAll; "tgammal", readsAll; From a0b390930b895640301e73d75486e9ba919983ba Mon Sep 17 00:00:00 2001 From: karoliineh Date: Wed, 7 Jun 2023 19:05:17 +0300 Subject: [PATCH 0088/1312] Add failwith to prevent goblint devs from adding functions already in libraries to invalidate_actions --- src/analyses/libraryFunctions.ml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 5a16c9edcf..7940c03045 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -1053,6 +1053,10 @@ let invalidate_actions = [ "__goblint_assume_join", readsAll; ] +let () = List.iter (fun (x, _) -> + if Hashtbl.exists (fun _ b -> List.mem_assoc x b) libraries then + failwith ("You have added a function to invalidate_actions that already exists in libraries. Please undo this for function: " ^ x); + ) invalidate_actions (* used by get_invalidate_action to make sure * that hash of invalidates is built only once From 706a1f516305de78144a1dce17f3bf4142093af1 Mon Sep 17 00:00:00 2001 From: Isidor Zweckstetter Date: Mon, 5 Jun 2023 15:33:19 +0200 Subject: [PATCH 0089/1312] changed loop exit indicator form global variable to a special function --- lib/goblint/runtime/src/goblint.c | 4 ++++ src/analyses/libraryFunctions.ml | 1 + src/analyses/termination_new.ml | 4 ++++ src/util/terminationPreprocessing.ml | 13 ++++--------- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/lib/goblint/runtime/src/goblint.c b/lib/goblint/runtime/src/goblint.c index bc176f93a6..39c18c5b8e 100644 --- a/lib/goblint/runtime/src/goblint.c +++ b/lib/goblint/runtime/src/goblint.c @@ -27,4 +27,8 @@ void __goblint_split_begin(int exp) { void __goblint_split_end(int exp) { +} + +void __goblint_bounded() { + } \ No newline at end of file diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 3eacf9013a..7bb985ae4b 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -302,6 +302,7 @@ let goblint_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__goblint_assert", special [__ "exp" []] @@ fun exp -> Assert { exp; check = true; refine = get_bool "sem.assert.refine" }); ("__goblint_split_begin", unknown [drop "exp" []]); ("__goblint_split_end", unknown [drop "exp" []]); + ("__goblint_bounded", special [__ "exp"[]] @@ fun exp -> Assert { exp; check = true; refine = false }); ] (** zstd functions. diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index a380532afe..bff776b95d 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -80,6 +80,10 @@ end let () = (** Register the preprocessing *) +<<<<<<< HEAD Cilfacade.register_preprocess_cil (Spec.name ()) (new loopCounterVisitor loopCounters upjumpingGotos loopExit); +======= + Cilfacade.register_preprocess_cil (Spec.name ()) (new loopCounterVisitor loopCounters); +>>>>>>> dfa9d6ef8 (changed loop exit indicator form global variable to a special function) (** Register this analysis within the master control program *) MCP.register_analysis (module Spec : MCPSpec) diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index 749b9f2f8e..15a5c948fd 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -8,6 +8,8 @@ open GoblintCil include Printf +let f_bounded = Lval (var (emptyFunction "__goblint_bounded").svar) + let extract_file_name s = (*There still may be a need to filter more chars*) let ls = String.split_on_char '/' s in (*Assuming '/' as path seperator*) let ls = List.rev ls in @@ -21,15 +23,8 @@ let extract_file_name s = (*There still may be a need to filt let show_location_id l = string_of_int l.line ^ "_" ^ string_of_int l.column ^ "-file" ^ "_" ^ extract_file_name l.file -class loopCounterVisitor lc lg le (fd : fundec) = object(self) +class loopCounterVisitor lc (fd : fundec) = object(self) inherit nopCilVisitor - method! vfunc (f:fundec) = - if !le.vname <> "term_exit-" then begin - let exit_name = "term_exit-" in - let typ = Cil.intType in - le := Cil.makeGlobalVar exit_name typ; - end; - DoChildren; (* function definition *) method! vstmt s = let action s = match s.skind with | Loop (b, loc, eloc, _, _) -> @@ -38,7 +33,7 @@ class loopCounterVisitor lc lg le (fd : fundec) = object(self) let v = (Cil.makeLocalVar fd name typ) in (* NOT tested for TODOOOOO*) let init_stmt = mkStmtOneInstr @@ Set (var v, zero, loc, eloc) in let inc_stmt = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in - let exit_stmt = mkStmtOneInstr @@ Set ((var !le), (Lval (var v)), loc, eloc) in + let exit_stmt = mkStmtOneInstr @@ Call (None, f_bounded, [(Lval(var v))], loc, eloc) in (match b.bstmts with | cont :: cond :: ss -> b.bstmts <- cont :: inc_stmt :: cond :: ss; (*cont :: cond :: inc_stmt :: ss = it is also possible, but for loops with cond at the end, inc is also at the end*) From fb65c1cb2c0fb6a4075e71dc9a965ec49339f955 Mon Sep 17 00:00:00 2001 From: Nathan Schmidt Date: Thu, 8 Jun 2023 12:38:15 +0200 Subject: [PATCH 0090/1312] Fixed integration of domain for base analysis - Updated null recognition in Compound of valueDomain - strstr analysis can now detect NULL ptr - fixed get of AttributeConfiguredArrayDomain --- src/analyses/base.ml | 8 ++- src/cdomains/arrayDomain.ml | 96 ++++++++++++++++++++++-------------- src/cdomains/arrayDomain.mli | 38 +++++++------- src/cdomains/valueDomain.ml | 38 ++++++-------- 4 files changed, 98 insertions(+), 82 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 0ce42d48ae..9c5ea89f34 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -532,8 +532,6 @@ struct | Thread _ -> empty (* thread IDs are abstract and nothing known can be reached from them *) | JmpBuf _ -> empty (* Jump buffers are abstract and nothing known can be reached from them *) | Mutex -> empty (* mutexes are abstract and nothing known can be reached from them *) - | NullByte -> empty (* TODO: is this correct? *) - | NotNullByte -> empty (* TODO: is this correct? *) (* Get the list of addresses accessable immediately from a given address, thus * all pointers within a structure should be considered, but we don't follow @@ -664,8 +662,6 @@ struct | Thread _ -> (empty, TS.bot (), false) (* TODO: is this right? *) | JmpBuf _ -> (empty, TS.bot (), false) (* TODO: is this right? *) | Mutex -> (empty, TS.bot (), false) (* TODO: is this right? *) - | NullByte -> (empty, TS.bot (), false) (* TODO: is this right? *) - | NotNullByte -> (empty, TS.bot (), false) (* TODO: is this right? *) in reachable_from_value (get (Analyses.ask_of_ctx ctx) ctx.global ctx.local adr None) in @@ -2135,7 +2131,9 @@ struct if that is the case, assign the substring of haystack starting at the first occurrence of needle to dest, else use top *) let dest_a, dest_typ, value = string_manipulation haystack needle lv true (Some (fun h_a n_a -> Address(AD.substring_extraction h_a n_a))) - (fun h_ar n_ar -> Array(CArrays.substring_extraction h_ar n_ar)) in + (fun h_ar n_ar -> match CArrays.substring_extraction h_ar n_ar with + | Some ar -> Array(ar) + | None -> Address(AD.null_ptr)) in set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value | None -> st end diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 3f6dcdce7f..64b4808aa0 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -39,13 +39,12 @@ let get_domain ~varAttr ~typAttr = let can_recover_from_top x = x <> TrivialDomain -module type SMinusDomain = +module type SMinusDomainAndRet = sig include Lattice.S type idx type value - val get: ?checkBounds:bool -> VDQ.t -> t -> Basetype.CilExp.t option * idx -> value val set: VDQ.t -> t -> Basetype.CilExp.t option * idx -> value -> t val make: ?varAttr:attributes -> ?typAttr:attributes -> idx -> value -> t val length: t -> idx option @@ -65,21 +64,24 @@ end module type S = sig - include SMinusDomain + include SMinusDomainAndRet val domain_of_t: t -> domain + val get: ?checkBounds:bool -> VDQ.t -> t -> Basetype.CilExp.t option * idx -> value end module type Str = sig - include SMinusDomain + include SMinusDomainAndRet + + type ret = Null | NotNull | Top + + val get: ?checkBounds:bool -> VDQ.t -> t -> Basetype.CilExp.t option * idx -> ret - val to_string: t -> t - val to_n_string: t -> int -> t val to_string_length: t -> idx val string_copy: t -> t -> int option -> t val string_concat: t -> t -> int option -> t - val substring_extraction: t -> t -> t + val substring_extraction: t -> t -> t option val string_comparison: t -> t -> int option -> idx end @@ -88,6 +90,7 @@ sig include Str val domain_of_t: t -> domain + val get: ?checkBounds:bool -> VDQ.t -> t -> Basetype.CilExp.t option * idx -> value end module type LatticeWithSmartOps = @@ -101,9 +104,14 @@ end module type LatticeWithNull = sig include LatticeWithSmartOps + val null: unit -> t val not_null: unit -> t val is_null: t -> bool + + val is_int_ikind: t -> Cil.ikind option + val zero_of_ikind: Cil.ikind -> t + val not_zero_of_ikind: Cil.ikind -> t end module Trivial (Val: Lattice.S) (Idx: Lattice.S): S with type value = Val.t and type idx = Idx.t = @@ -986,6 +994,8 @@ struct type idx = Idx.t type value = Val.t + type ret = Null | NotNull | Top + let get ?(checkBounds=true) (ask: VDQ.t) (must_nulls_set, may_nulls_set, size) (e, i) = let rec all_indexes_must_null i max = if Z.gt i max then @@ -1011,33 +1021,33 @@ struct match max_i, Idx.maximal size with (* if there is no maximum value in index interval *) | None, _ -> - (* ... return not_null if no i >= min_i in may_nulls_set *) + (* ... return NotNull if no i >= min_i in may_nulls_set *) if not (MayNulls.exists (Z.leq min_i) may_nulls_set) then - Val.not_null () - (* ... else return top of value *) + NotNull + (* ... else return Top *) else - Val.top () + Top (* if there is no maximum size *) | Some max_i, None when Z.geq max_i Z.zero -> - (* ... and maximum value in index interval < minimal size, return null if all numbers in index interval are in must_nulls_set *) + (* ... and maximum value in index interval < minimal size, return Null if all numbers in index interval are in must_nulls_set *) if Z.lt max_i min_size && all_indexes_must_null min_i max_i then - Val.null () - (* ... return not_null if no number in index interval is in may_nulls_set *) + Null + (* ... return NotNull if no number in index interval is in may_nulls_set *) else if not (MayNulls.exists (fun x -> Z.geq x min_i && Z.leq x max_i) may_nulls_set) then - Val.not_null () + NotNull else - Val.top () + Top | Some max_i, Some max_size when Z.geq max_i Z.zero -> - (* if maximum value in index interval < minimal size, return null if all numbers in index interval are in must_nulls_set *) + (* if maximum value in index interval < minimal size, return Null if all numbers in index interval are in must_nulls_set *) if Z.lt max_i min_size && all_indexes_must_null min_i max_i then - Val.null () - (* if maximum value in index interval < maximal size, return not_null if no number in index interval is in may_nulls_set *) + Null + (* if maximum value in index interval < maximal size, return NotNull if no number in index interval is in may_nulls_set *) else if Z.lt max_i max_size && not (MayNulls.exists (fun x -> Z.geq x min_i && Z.leq x max_i) may_nulls_set) then - Val.not_null () + NotNull else - Val.top () - (* if maximum number in interval is invalid, i.e. negative, return top of value *) - | _ -> Val.top () + Top + (* if maximum number in interval is invalid, i.e. negative, return Top of value *) + | _ -> Top let set (ask: VDQ.t) (must_nulls_set, may_nulls_set, size) (e, i) v = let rec add_indexes i max may_nulls_set = @@ -1195,6 +1205,8 @@ struct let smart_leq _ _ = leq (* string functions *) + + (** Returns an abstract value with at most one null byte marking the end of the string *) let to_string (must_nulls_set, may_nulls_set, size) = (* if must_nulls_set and min_nulls_set empty, definitely no null byte in array => warn about certain buffer overflow and return tuple unchanged *) if MustNulls.is_empty must_nulls_set && MayNulls.is_empty may_nulls_set then @@ -1213,6 +1225,9 @@ struct else (MustNulls.empty (), MayNulls.filter (Z.geq min_must_null) may_nulls_set, Idx.of_int !Cil.kindOfSizeOf (Z.succ min_must_null)) + (** [to_n_string index_set n] returns an abstract value with a potential null byte + * marking the end of the string and if needed followed by further null bytes to obtain + * an n bytes string. *) let to_n_string (must_nulls_set, may_nulls_set, size) n = let rec add_indexes i max set = if Z.geq i max then @@ -1456,19 +1471,18 @@ struct let substring_extraction haystack (must_nulls_set_needle, may_nulls_set_needle, size_needle) = (* if needle is empty string, i.e. certain null byte at index 0, return haystack as string *) if MustNulls.mem Z.zero must_nulls_set_needle then - to_string haystack + Some (to_string haystack) else let haystack_len = to_string_length haystack in let needle_len = to_string_length (must_nulls_set_needle, may_nulls_set_needle, size_needle) in match Idx.maximal haystack_len, Idx.minimal needle_len with | Some haystack_max, Some needle_min -> - (* if strlen(haystack) < strlen(needle), needle can never be substring of haystack => return null pointer *) - (* TODO: how to do that? Maybe pass on something I can identify as standing for null_ptr in base, where I plugin null_ptr *) + (* if strlen(haystack) < strlen(needle), needle can never be substring of haystack => return None *) if Z.lt haystack_max needle_min then - (MustNulls.top (), MayNulls.top (), Idx.of_int !Cil.kindOfSizeOf Z.zero) + None else - (MustNulls.top (), MayNulls.top (), Idx.top_of !Cil.kindOfSizeOf) - | _ -> (MustNulls.top (), MayNulls.top (), Idx.top_of !Cil.kindOfSizeOf) + Some (MustNulls.top (), MayNulls.top (), Idx.top_of !Cil.kindOfSizeOf) + | _ -> Some (MustNulls.top (), MayNulls.top (), Idx.top_of !Cil.kindOfSizeOf) let string_comparison (must_nulls_set1, may_nulls_set1, size1) (must_nulls_set2, may_nulls_set2, size2) n = let compare n n_exists = @@ -1487,7 +1501,7 @@ struct (try if Z.equal (MustNulls.min_elt must_nulls_set1) (MayNulls.min_elt may_nulls_set1) && (not n_exists || Z.lt (MustNulls.min_elt must_nulls_set1) n) && Z.equal (MustNulls.min_elt must_nulls_set2) (MayNulls.min_elt may_nulls_set2) && (not n_exists || Z.lt (MustNulls.min_elt must_nulls_set2) n) && not (Z.equal (MustNulls.min_elt must_nulls_set1) (MustNulls.min_elt must_nulls_set2)) then - Idx.join (Idx.ending IInt Z.minus_one) (Idx.starting IInt Z.one) + Idx.of_excl_list IInt [Z.zero] else Idx.top_of IInt with Not_found -> Idx.top_of IInt) in @@ -1543,8 +1557,7 @@ struct let project ?(varAttr=[]) ?(typAttr=[]) _ t = t - (* TODO: what am I supposed to do here? *) - let invariant ~value_invariant ~offset ~lval x = failwith "TODO" + let invariant ~value_invariant ~offset ~lval x = Invariant.none end module FlagHelperAttributeConfiguredArrayDomain(Val: LatticeWithSmartOps) (Idx:IntDomain.Z):S with type value = Val.t and type idx = Idx.t = @@ -1680,9 +1693,17 @@ struct type idx = Idx.t type value = Val.t + type ret = Null | NotNull | Top + let domain_of_t (t_f, _) = F.domain_of_t t_f - let get ?(checkBounds=true) (ask: VDQ.t) (t_f, t_n) i = Val.meet (F.get ask t_f i) (N.get ask t_n i) + let get ?(checkBounds=true) (ask: VDQ.t) (t_f, t_n) i = + let f_get = F.get ask t_f i in + let n_get = N.get ask t_n i in + match Val.is_int_ikind f_get, n_get with + | Some ik, Null -> Val.meet f_get (Val.zero_of_ikind ik) + | Some ik, NotNull -> Val.meet f_get (Val.not_zero_of_ikind ik) + | _ -> f_get let set (ask:VDQ.t) (t_f, t_n) i v = (F.set ask t_f i v, N.set ask t_n i v) let make ?(varAttr=[]) ?(typAttr=[]) i v = (F.make i v, N.make i v) let length (_, t_n) = N.length t_n @@ -1695,16 +1716,15 @@ struct let smart_widen x y (t_f1, t_n1) (t_f2, t_n2) = (F.smart_widen x y t_f1 t_f2, N.smart_widen x y t_n1 t_n2) let smart_leq x y (t_f1, t_n1) (t_f2, t_n2) = F.smart_leq x y t_f1 t_f2 && N.smart_leq x y t_n1 t_n2 - let to_string (_, t_n) = (F.top (), N.to_string t_n) - let to_n_string (_, t_n) n = (F.top (), N.to_n_string t_n n) let to_string_length (_, t_n) = N.to_string_length t_n let string_copy (_, t_n1) (_, t_n2) n = (F.top (), N.string_copy t_n1 t_n2 n) let string_concat (_, t_n1) (_, t_n2) n = (F.top (), N.string_concat t_n1 t_n2 n) - let substring_extraction (_, t_n1) (_, t_n2) = (F.top (), N.substring_extraction t_n1 t_n2) + let substring_extraction (_, t_n1) (_, t_n2) = match N.substring_extraction t_n1 t_n2 with + | Some res -> Some (F.top (), res) + | None -> None let string_comparison (_, t_n1) (_, t_n2) n = N.string_comparison t_n1 t_n2 n let update_length newl (t_f, t_n) = (F.update_length newl t_f, N.update_length newl t_n) let project ?(varAttr=[]) ?(typAttr=[]) ask (t_f, t_n) = (F.project ask t_f, N.project ask t_n) - (* TODO: what should I do here? *) - let invariant ~value_invariant ~offset ~lval x = failwith "TODO" + let invariant ~value_invariant ~offset ~lval (t_f, _) = F.invariant ~value_invariant ~offset ~lval t_f end diff --git a/src/cdomains/arrayDomain.mli b/src/cdomains/arrayDomain.mli index f5da9c4d35..b62e65ea60 100644 --- a/src/cdomains/arrayDomain.mli +++ b/src/cdomains/arrayDomain.mli @@ -12,7 +12,7 @@ val get_domain: varAttr:Cil.attributes -> typAttr:Cil.attributes -> domain val can_recover_from_top: domain -> bool (** Some domains such as Trivial cannot recover from their value ever being top. {!ValueDomain} handles intialization differently for these *) -module type SMinusDomain = +module type SMinusDomainAndRet = sig include Lattice.S type idx @@ -21,9 +21,6 @@ sig type value (** The abstract domain of values stored in the array. *) - val get: ?checkBounds:bool -> VDQ.t -> t -> Basetype.CilExp.t option * idx -> value - (** Returns the element residing at the given index. *) - val set: VDQ.t -> t -> Basetype.CilExp.t option * idx -> value -> t (** Returns a new abstract value, where the given index is replaced with the * given element. *) @@ -60,24 +57,24 @@ end (** Abstract domains representing arrays. *) module type S = sig - include SMinusDomain + include SMinusDomainAndRet val domain_of_t: t -> domain (* Returns the domain used for the array*) + + val get: ?checkBounds:bool -> VDQ.t -> t -> Basetype.CilExp.t option * idx -> value + (** Returns the element residing at the given index. *) end (** Abstract domains representing strings a.k.a. null-terminated char arrays. *) module type Str = sig - include SMinusDomain + include SMinusDomainAndRet - val to_string: t -> t - (** Returns an abstract value with at most one null byte marking the end of the string *) + type ret = Null | NotNull | Top - val to_n_string: t -> int -> t - (** [to_n_string index_set n] returns an abstract value with a potential null byte - * marking the end of the string and if needed followed by further null bytes to obtain - * an n bytes string. *) + val get: ?checkBounds:bool -> VDQ.t -> t -> Basetype.CilExp.t option * idx -> ret + (* overwrites get of module S *) val to_string_length: t -> idx (** Returns length of string represented by input abstract value *) @@ -91,9 +88,10 @@ sig * concatenation of the input abstract values [s1] and [s2], taking at most [n] bytes of * [s2] if present *) - val substring_extraction: t -> t -> t - (** [substring_extraction haystack needle] returns null if the string represented by the - * abstract value [needle] surely isn't a substring of [haystack], else top *) + val substring_extraction: t -> t -> t option + (** [substring_extraction haystack needle] returns None if the string represented by the + * abstract value [needle] surely isn't a substring of [haystack], Some [to_string haystack] + * if [needle] is empty the empty string, else Some top *) val string_comparison: t -> t -> int option -> idx (** [string_comparison s1 s2 n] returns a negative / positive idx element if the string @@ -106,7 +104,8 @@ sig include Str val domain_of_t: t -> domain - (* Returns the domain used for the array*) + (* Returns the domain used for the array *) + val get: ?checkBounds:bool -> VDQ.t -> t -> Basetype.CilExp.t option * idx -> value end module type LatticeWithSmartOps = @@ -120,9 +119,14 @@ end module type LatticeWithNull = sig include LatticeWithSmartOps + val null: unit -> t val not_null: unit -> t val is_null: t -> bool + + val is_int_ikind: t -> Cil.ikind option + val zero_of_ikind: Cil.ikind -> t + val not_zero_of_ikind: Cil.ikind -> t end module Trivial (Val: Lattice.S) (Idx: Lattice.S): S with type value = Val.t and type idx = Idx.t @@ -145,7 +149,7 @@ module Partitioned (Val: LatticeWithSmartOps) (Idx: IntDomain.Z): S with type va module PartitionedWithLength (Val: LatticeWithSmartOps) (Idx:IntDomain.Z): S with type value = Val.t and type idx = Idx.t (** Like partitioned but additionally manages the length of the array. *) -module NullByte (Val: LatticeWithNull) (Idx: IntDomain.Z): SMinusDomain with type value = Val.t and type idx = Idx.t +module NullByte (Val: LatticeWithNull) (Idx: IntDomain.Z): SMinusDomainAndRet with type value = Val.t and type idx = Idx.t (** This functor creates an array representation by the indexes of all null bytes * the array must and may contain. This is useful to analyze strings, i.e. null- * terminated char arrays, and particularly to determine if operations on strings diff --git a/src/cdomains/valueDomain.ml b/src/cdomains/valueDomain.ml index d8e81032ca..8846a5be1f 100644 --- a/src/cdomains/valueDomain.ml +++ b/src/cdomains/valueDomain.ml @@ -42,6 +42,10 @@ sig val not_null: unit -> t val is_null: t -> bool + val is_int_ikind: t -> Cil.ikind option + val zero_of_ikind: Cil.ikind -> t + val not_zero_of_ikind: Cil.ikind -> t + val project: VDQ.t -> int_precision option-> ( attributes * attributes ) option -> t -> t val mark_jmpbufs_as_copied: t -> t end @@ -94,8 +98,6 @@ module rec Compound: sig | JmpBuf of JmpBufs.t | Mutex | MutexAttr of MutexAttrDomain.t - | NullByte - | NotNullByte | Bot include S with type t := t and type offs = IndexDomain.t Offset.t end = @@ -113,8 +115,6 @@ struct | JmpBuf of JmpBufs.t | Mutex | MutexAttr of MutexAttrDomain.t - | NullByte - | NotNullByte | Bot [@@deriving eq, ord, hash] @@ -173,8 +173,6 @@ struct | JmpBuf x -> JmpBufs.is_bot x | Mutex -> true | MutexAttr x -> MutexAttr.is_bot x - | NullByte -> true - | NotNullByte -> true | Bot -> true | Top -> false @@ -228,8 +226,6 @@ struct | MutexAttr x -> MutexAttr.is_top x | JmpBuf x -> JmpBufs.is_top x | Mutex -> true - | NullByte -> true - | NotNullByte -> true | Top -> true | Bot -> false @@ -261,7 +257,7 @@ struct | _ -> Top let tag_name : t -> string = function - | Top -> "Top" | Int _ -> "Int" | Float _ -> "Float" | Address _ -> "Address" | Struct _ -> "Struct" | Union _ -> "Union" | Array _ -> "Array" | Blob _ -> "Blob" | Thread _ -> "Thread" | Mutex -> "Mutex" | MutexAttr _ -> "MutexAttr" | NullByte -> "NullByte" | NotNullByte -> "NotNullByte" | JmpBuf _ -> "JmpBuf" | Bot -> "Bot" + | Top -> "Top" | Int _ -> "Int" | Float _ -> "Float" | Address _ -> "Address" | Struct _ -> "Struct" | Union _ -> "Union" | Array _ -> "Array" | Blob _ -> "Blob" | Thread _ -> "Thread" | Mutex -> "Mutex" | MutexAttr _ -> "MutexAttr" | JmpBuf _ -> "JmpBuf" | Bot -> "Bot" include Printable.Std let name () = "compound" @@ -275,9 +271,17 @@ struct let is_top x = x = Top let top_name = "Unknown" - let null () = NullByte - let not_null () = NotNullByte - let is_null x = x = NullByte + let null () = Int(ID.of_int IChar Z.zero) + let not_null () = Top + let is_null = function + | Int n -> ID.to_int n = Some Z.zero + | _ -> false + + let is_int_ikind = function + | Int n -> Some (ID.ikind n) + | _ -> None + let zero_of_ikind ik = Int(ID.of_int ik Z.zero) + let not_zero_of_ikind ik = Int(ID.of_excl_list ik [Z.zero]) let pretty () state = match state with @@ -292,8 +296,6 @@ struct | MutexAttr n -> MutexAttr.pretty () n | JmpBuf n -> JmpBufs.pretty () n | Mutex -> text "mutex" - | NullByte -> text "null-byte" - | NotNullByte -> text "not-null-byte" | Bot -> text bot_name | Top -> text top_name @@ -310,8 +312,6 @@ struct | JmpBuf n -> JmpBufs.show n | Mutex -> "mutex" | MutexAttr x -> MutexAttr.show x - | NullByte -> "null-byte" - | NotNullByte -> "not-null-byte" | Bot -> bot_name | Top -> top_name @@ -1175,8 +1175,6 @@ struct | MutexAttr n -> MutexAttr.printXml f n | JmpBuf n -> JmpBufs.printXml f n | Mutex -> BatPrintf.fprintf f "\n\nmutex\n\n\n" - | NullByte -> BatPrintf.fprintf f "\n\nnull-byte\n\n\n" - | NotNullByte -> BatPrintf.fprintf f "\n\nnot-null-byte\n\n\n" | Bot -> BatPrintf.fprintf f "\n\nbottom\n\n\n" | Top -> BatPrintf.fprintf f "\n\ntop\n\n\n" @@ -1192,8 +1190,6 @@ struct | MutexAttr n -> MutexAttr.to_yojson n | JmpBuf n -> JmpBufs.to_yojson n | Mutex -> `String "mutex" - | NullByte -> `String "null-byte" - | NotNullByte -> `String "not-null-byte" | Bot -> `String "⊥" | Top -> `String "⊤" @@ -1244,8 +1240,6 @@ struct | JmpBuf n -> JmpBuf (JmpBufs.relift n) | MutexAttr n -> MutexAttr (MutexAttr.relift n) | Mutex -> Mutex - | NullByte -> NullByte - | NotNullByte -> NotNullByte | Bot -> Bot | Top -> Top end From 09510429a97ac12575c47067d15c18e450a1ae73 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Thu, 8 Jun 2023 13:13:39 +0200 Subject: [PATCH 0091/1312] added first tests for enter; does compile without the enter stuff, but when running we got an Not_found exception due to the definition of V --- src/framework/analyses.ml | 8 +++--- src/framework/constraints.ml | 55 +++++++++++++++++------------------- 2 files changed, 30 insertions(+), 33 deletions(-) diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 2f3f4b34a8..41448768a9 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -119,7 +119,7 @@ struct | x -> BatPrintf.fprintf f "%a" printXml x end - +(* Make the given module Goupable*) module C_ (C: Printable.S)= struct include C @@ -139,12 +139,12 @@ struct let name () = "Tuple" let to_yojson x = `String (show x) let relift (a,b) = (a,b) (*Todo: is this correct?*) - let printXml f (a,b) = Base1.printXml f a (*Todo: what do we have to put here?*) + let printXml f (a,b) = Base1.printXml f a; Base2.printXml f b (*Todo: what do we have to put here?*) let compare (a1,b1) (a2,b2) = 3 (*Todo: what do we have to put here?*) (*let a = Base1.compare a1 a2 in let b = Base2.compare b1 b2 in *) - let pretty () (a,b) = Base1.pretty () a(*Todo: what do we have to put here?*) + let pretty () x = text (show x) let hash (a,b) = 2 (*Todo: what do we have to put here?*) @@ -176,7 +176,7 @@ struct | `Lifted1 x -> x | _ -> failwith "GVarGG.spec" let contexts = function - | `Bot -> CSet.bot () + | `Bot -> CMap.bot () | `Lifted2 x -> x | _ -> failwith "GVarGG.contexts" let create_spec spec = `Lifted1 spec diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 774c0abc95..287726419f 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1930,58 +1930,55 @@ module RecursionTermLifter (S: Spec) : Spec with module D = S.D and module C = S.C and module G = GVarGG (S.G) (S.C) + and module V = GVarF(S.V) = - -struct - module C = S.C - module P = S.P - module D = S.D - - module V = - struct - include GVarF(S.V) - let s x = `Left x - end - - - (*global invariant +(*global invariant - fundec -> Map (S.C) (Set (fundec * S.C)) So: g -> {c' -> f, c} in case f, c --> g, c' *) +struct + include S + module V = GVarF(S.V) + module G = GVarGG (S.G) (S.C) - let name () = "RecursionTerm (" ^ S.name () ^ ")" - type marshal = S.marshal - let init = S.init let finalize = S.finalize (*TODO*) - let startstate v = S.startstate v - let exitstate v = S.exitstate v - let morphstate = S.morphstate - - let context = S.context - (**let side_context sideg f c = if !AnalysisState.postsolving then sideg (f) (G.create_contexts (G.CSet.singleton c))*) (*TODO Change the body??*) - let conv (ctx: (_, _, _, V.t) ctx): (_, _, _, S.V.t) ctx = + let conv (ctx: (_, G.t, _, V.t) ctx): (_, S.G.t, _, S.V.t) ctx = { ctx with - global = (fun v -> G.s (ctx.global (V.s v))); - sideg = (fun v g -> ctx.sideg (V.s v) (G.create_s g)); + global = (fun v -> G.s (ctx.global (V.spec v))); + sideg = (fun v g -> ctx.sideg (V.spec v) (G.create_s g)); } let query ctx = S.query (conv ctx) let branch ctx = S.branch (conv ctx) let assign ctx = S.assign (conv ctx) let vdecl ctx = S.vdecl (conv ctx) - let enter ctx = + + (* c = context + t = set of tuples (fundec * context) + *) + let side_context sideg f c t = if !AnalysisState.postsolving then - printf "hallo hallo"; - S.enter (conv ctx) (*TODO*) + sideg (V.contexts f) (G.create_contexts (G.CMap.singleton (c) (t))) + + let enter ctx lval fundec exprList = (*TODO*) + S.enter (conv ctx) lval fundec exprList; + let c: unit -> S.C.t = snd var |> Obj.obj in (*Callee context*) + let fd = fundec in (*Callee fundec*) + let c' = ctx.context in (*Caller context*) (*TODO is this the caller or callee context???*) + let fd' = in (*Caller fundec*) + let tup = (fundec * c') in (* TODO: is fundec the caller or callee fundec???*) + let t = G.CSet.singleton (tup) in (*TODO do we fill the set correctly???*) + side_context sideg fd (c ()) t + let paths_as_set ctx = S.paths_as_set (conv ctx) let body ctx = S.body (conv ctx) let return ctx = S.return (conv ctx) From b49a043538d4f5d27a451c44d61792714983b86a Mon Sep 17 00:00:00 2001 From: Nathan Schmidt Date: Thu, 8 Jun 2023 15:08:11 +0200 Subject: [PATCH 0092/1312] Fixed incompatible ikinds: changed !Cil.kindOfSizeOf to ILong --- src/analyses/base.ml | 6 ++--- src/cdomains/arrayDomain.ml | 46 ++++++++++++++++++------------------ src/cdomains/arrayDomain.mli | 2 +- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 9c5ea89f34..c83263d445 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2131,9 +2131,9 @@ struct if that is the case, assign the substring of haystack starting at the first occurrence of needle to dest, else use top *) let dest_a, dest_typ, value = string_manipulation haystack needle lv true (Some (fun h_a n_a -> Address(AD.substring_extraction h_a n_a))) - (fun h_ar n_ar -> match CArrays.substring_extraction h_ar n_ar with - | Some ar -> Array(ar) - | None -> Address(AD.null_ptr)) in + (fun h_ar n_ar -> match CArrays.substring_extraction h_ar n_ar with + | Some ar -> Array(ar) + | None -> Address(AD.null_ptr)) in set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value | None -> st end diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 64b4808aa0..b027a57028 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -104,7 +104,7 @@ end module type LatticeWithNull = sig include LatticeWithSmartOps - + val null: unit -> t val not_null: unit -> t val is_null: t -> bool @@ -1017,14 +1017,14 @@ struct let min_size = min size in (* warn if index is (potentially) out of bounds *) - if checkBounds then (array_oob_check (module Idx) (must_nulls_set, size) (e, i)); + if checkBounds then (array_oob_check (module Idx) ((must_nulls_set, may_nulls_set), size) (e, i)); match max_i, Idx.maximal size with (* if there is no maximum value in index interval *) | None, _ -> (* ... return NotNull if no i >= min_i in may_nulls_set *) if not (MayNulls.exists (Z.leq min_i) may_nulls_set) then NotNull - (* ... else return Top *) + (* ... else return Top *) else Top (* if there is no maximum size *) @@ -1032,7 +1032,7 @@ struct (* ... and maximum value in index interval < minimal size, return Null if all numbers in index interval are in must_nulls_set *) if Z.lt max_i min_size && all_indexes_must_null min_i max_i then Null - (* ... return NotNull if no number in index interval is in may_nulls_set *) + (* ... return NotNull if no number in index interval is in may_nulls_set *) else if not (MayNulls.exists (fun x -> Z.geq x min_i && Z.leq x max_i) may_nulls_set) then NotNull else @@ -1041,7 +1041,7 @@ struct (* if maximum value in index interval < minimal size, return Null if all numbers in index interval are in must_nulls_set *) if Z.lt max_i min_size && all_indexes_must_null min_i max_i then Null - (* if maximum value in index interval < maximal size, return NotNull if no number in index interval is in may_nulls_set *) + (* if maximum value in index interval < maximal size, return NotNull if no number in index interval is in may_nulls_set *) else if Z.lt max_i max_size && not (MayNulls.exists (fun x -> Z.geq x min_i && Z.leq x max_i) may_nulls_set) then NotNull else @@ -1177,11 +1177,11 @@ struct | None, None -> Z.zero, None in match max_i, Val.is_null v with (* if value = null, return (bot = all indexes up to minimal size - 1, top = all indexes up to maximal size - 1, size) *) - | Some max_i, true -> (MustNulls.bot (), MayNulls.top (), Idx.of_interval !Cil.kindOfSizeOf (min_i, max_i)) - | None, true -> (MustNulls.bot (), MayNulls.top (), Idx.starting !Cil.kindOfSizeOf min_i) + | Some max_i, true -> (MustNulls.bot (), MayNulls.top (), Idx.of_interval ILong (min_i, max_i)) + | None, true -> (MustNulls.bot (), MayNulls.top (), Idx.starting ILong min_i) (* if value <> null, return (top = no indexes, bot = no indexes, size) *) - | Some max_i, false -> (MustNulls.top (), MayNulls.bot (), Idx.of_interval !Cil.kindOfSizeOf (min_i, max_i)) - | None, false -> (MustNulls.top (), MayNulls.bot (), Idx.starting !Cil.kindOfSizeOf min_i) + | Some max_i, false -> (MustNulls.top (), MayNulls.bot (), Idx.of_interval ILong (min_i, max_i)) + | None, false -> (MustNulls.top (), MayNulls.bot (), Idx.starting ILong min_i) let length (_, _, size) = Some size @@ -1220,10 +1220,10 @@ struct let min_must_null = MustNulls.min_elt must_nulls_set in (* if smallest index in sets coincides, only this null byte is kept in both sets *) if Z.equal min_must_null (MayNulls.min_elt may_nulls_set) then - (MustNulls.singleton min_must_null, MayNulls.singleton min_must_null, Idx.of_int !Cil.kindOfSizeOf (Z.succ min_must_null)) + (MustNulls.singleton min_must_null, MayNulls.singleton min_must_null, Idx.of_int ILong (Z.succ min_must_null)) (* else return empty must_nulls_set and keep every index up to smallest index of must_nulls_set included in may_nulls_set *) else - (MustNulls.empty (), MayNulls.filter (Z.geq min_must_null) may_nulls_set, Idx.of_int !Cil.kindOfSizeOf (Z.succ min_must_null)) + (MustNulls.empty (), MayNulls.filter (Z.geq min_must_null) may_nulls_set, Idx.of_int ILong (Z.succ min_must_null)) (** [to_n_string index_set n] returns an abstract value with a potential null byte * marking the end of the string and if needed followed by further null bytes to obtain @@ -1255,7 +1255,7 @@ struct M.warn "Resulting string might not be null-terminated because src might not contain a null byte in the first n bytes" in if n < 0 then - (MustNulls.top (), MayNulls.top (), Idx.top_of !Cil.kindOfSizeOf) + (MustNulls.top (), MayNulls.top (), Idx.top_of ILong) else ((match Idx.minimal size, Idx.maximal size with | Some min_size, Some max_size -> @@ -1277,36 +1277,36 @@ struct "Resulting string might not be null-terminated because src doesn't contain a null byte"; match Idx.maximal size with (* ... there *may* be null bytes from maximal size to n - 1 if maximal size < n (i.e. past end) *) - | Some max_size when Z.geq max_size Z.zero -> (must_nulls_set, add_indexes max_size (Z.of_int n) may_nulls_set, Idx.of_int !Cil.kindOfSizeOf (Z.of_int n)) - | _ -> (must_nulls_set, may_nulls_set, Idx.of_int !Cil.kindOfSizeOf (Z.of_int n))) + | Some max_size when Z.geq max_size Z.zero -> (must_nulls_set, add_indexes max_size (Z.of_int n) may_nulls_set, Idx.of_int ILong (Z.of_int n)) + | _ -> (must_nulls_set, may_nulls_set, Idx.of_int ILong (Z.of_int n))) (* if only must_nulls_set empty, remove indexes >= n from may_nulls_set and add all indexes from minimal may null index to n - 1; * warn as in any case, resulting array not guaranteed to contain null byte *) else if MustNulls.is_empty must_nulls_set then let min_may_null = MayNulls.min_elt may_nulls_set in warn_no_null Z.zero false min_may_null; - (must_nulls_set, update_may_indexes min_may_null may_nulls_set, Idx.of_int !Cil.kindOfSizeOf (Z.of_int n)) + (must_nulls_set, update_may_indexes min_may_null may_nulls_set, Idx.of_int ILong (Z.of_int n)) else let min_must_null = MustNulls.min_elt must_nulls_set in let min_may_null = MayNulls.min_elt may_nulls_set in (* warn if resulting array may not contain null byte *) warn_no_null min_must_null true min_may_null; (* remove indexes >= n and add all indexes from minimal must/may null to n - 1 in the sets *) - (update_must_indexes min_must_null must_nulls_set, update_may_indexes min_may_null may_nulls_set, Idx.of_int !Cil.kindOfSizeOf (Z.of_int n))) + (update_must_indexes min_must_null must_nulls_set, update_may_indexes min_may_null may_nulls_set, Idx.of_int ILong (Z.of_int n))) let to_string_length (must_nulls_set, may_nulls_set, size) = (* if must_nulls_set and min_nulls_set empty, definitely no null byte in array => return interval [size, inf) and warn *) if MustNulls.is_empty must_nulls_set && MayNulls.is_empty may_nulls_set then (M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array doesn't contain a null byte: buffer overflow"; match Idx.minimal size with - | Some min_size -> Idx.starting !Cil.kindOfSizeOf min_size - | None -> Idx.starting !Cil.kindOfSizeOf Z.zero) + | Some min_size -> Idx.starting ILong min_size + | None -> Idx.starting ILong Z.zero) (* if only must_nulls_set empty, no guarantee that null ever encountered in array => return interval [minimal may null, inf) and *) else if MustNulls.is_empty must_nulls_set then (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array might not contain a null byte: potential buffer overflow"; - Idx.starting !Cil.kindOfSizeOf (MayNulls.min_elt may_nulls_set)) + Idx.starting ILong (MayNulls.min_elt may_nulls_set)) (* else return interval [minimal may null, minimal must null] *) else - Idx.of_interval !Cil.kindOfSizeOf (MayNulls.min_elt may_nulls_set, MustNulls.min_elt must_nulls_set) + Idx.of_interval ILong (MayNulls.min_elt may_nulls_set, MustNulls.min_elt must_nulls_set) let string_copy (must_nulls_set1, may_nulls_set1, size1) ar2 n = (* filter out indexes before strlen(src) from dest sets and after strlen(src) from src sets and build union, keep size of dest *) @@ -1481,8 +1481,8 @@ struct if Z.lt haystack_max needle_min then None else - Some (MustNulls.top (), MayNulls.top (), Idx.top_of !Cil.kindOfSizeOf) - | _ -> Some (MustNulls.top (), MayNulls.top (), Idx.top_of !Cil.kindOfSizeOf) + Some (MustNulls.top (), MayNulls.top (), Idx.top_of ILong) + | _ -> Some (MustNulls.top (), MayNulls.top (), Idx.top_of ILong) let string_comparison (must_nulls_set1, may_nulls_set1, size1) (must_nulls_set2, may_nulls_set2, size2) n = let compare n n_exists = @@ -1501,7 +1501,7 @@ struct (try if Z.equal (MustNulls.min_elt must_nulls_set1) (MayNulls.min_elt may_nulls_set1) && (not n_exists || Z.lt (MustNulls.min_elt must_nulls_set1) n) && Z.equal (MustNulls.min_elt must_nulls_set2) (MayNulls.min_elt may_nulls_set2) && (not n_exists || Z.lt (MustNulls.min_elt must_nulls_set2) n) && not (Z.equal (MustNulls.min_elt must_nulls_set1) (MustNulls.min_elt must_nulls_set2)) then - Idx.of_excl_list IInt [Z.zero] + Idx.of_excl_list IInt [Z.zero] else Idx.top_of IInt with Not_found -> Idx.top_of IInt) in diff --git a/src/cdomains/arrayDomain.mli b/src/cdomains/arrayDomain.mli index b62e65ea60..9bfa85fb5d 100644 --- a/src/cdomains/arrayDomain.mli +++ b/src/cdomains/arrayDomain.mli @@ -119,7 +119,7 @@ end module type LatticeWithNull = sig include LatticeWithSmartOps - + val null: unit -> t val not_null: unit -> t val is_null: t -> bool From 81db59318819a89cea0757dd4082f4bb26acfb75 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Thu, 8 Jun 2023 17:54:12 +0300 Subject: [PATCH 0093/1312] Convert all math.h functions to new specifications --- src/analyses/libraryFunctions.ml | 276 +++++++++++++++---------------- 1 file changed, 138 insertions(+), 138 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 7940c03045..0d432e0efa 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -398,6 +398,144 @@ let math_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("tan", special [__ "x" []] @@ fun x -> Math { fun_args = (Tan (FDouble, x)) }); ("tanf", special [__ "x" []] @@ fun x -> Math { fun_args = (Tan (FFloat, x)) }); ("tanl", special [__ "x" []] @@ fun x -> Math { fun_args = (Tan (FLongDouble, x)) }); + ("acosh", unknown [drop "x" []]); + ("acoshf", unknown [drop "x" []]); + ("acoshl", unknown [drop "x" []]); + ("asinh", unknown [drop "x" []]); + ("asinhf", unknown [drop "x" []]); + ("asinhl", unknown [drop "x" []]); + ("atanh", unknown [drop "x" []]); + ("atanhf", unknown [drop "x" []]); + ("atanhl", unknown [drop "x" []]); + ("cosh", unknown [drop "x" []]); + ("coshf", unknown [drop "x" []]); + ("coshl", unknown [drop "x" []]); + ("sinh", unknown [drop "x" []]); + ("sinhf", unknown [drop "x" []]); + ("sinhl", unknown [drop "x" []]); + ("tanh", unknown [drop "x" []]); + ("tanhf", unknown [drop "x" []]); + ("tanhl", unknown [drop "x" []]); + ("cbrt", unknown [drop "x" []]); + ("cbrtf", unknown [drop "x" []]); + ("cbrtl", unknown [drop "x" []]); + ("copysign", unknown [drop "x" []; drop "y" []]); + ("copysignf", unknown [drop "x" []; drop "y" []]); + ("copysignl", unknown [drop "x" []; drop "y" []]); + ("erf", unknown [drop "x" []]); + ("erff", unknown [drop "x" []]); + ("erfl", unknown [drop "x" []]); + ("erfc", unknown [drop "x" []]); + ("erfcf", unknown [drop "x" []]); + ("erfcl", unknown [drop "x" []]); + ("exp", unknown [drop "x" []]); + ("expf", unknown [drop "x" []]); + ("expl", unknown [drop "x" []]); + ("exp2", unknown [drop "x" []]); + ("exp2f", unknown [drop "x" []]); + ("exp2l", unknown [drop "x" []]); + ("expm1", unknown [drop "x" []]); + ("expm1f", unknown [drop "x" []]); + ("expm1l", unknown [drop "x" []]); + ("fdim", unknown [drop "x" []; drop "y" []]); + ("fdimf", unknown [drop "x" []; drop "y" []]); + ("fdiml", unknown [drop "x" []; drop "y" []]); + ("fma", unknown [drop "x" []; drop "y" []; drop "z" []]); + ("fmaf", unknown [drop "x" []; drop "y" []; drop "z" []]); + ("fmal", unknown [drop "x" []; drop "y" []; drop "z" []]); + ("fmod", unknown [drop "x" []; drop "y" []]); + ("fmodf", unknown [drop "x" []; drop "y" []]); + ("fmodl", unknown [drop "x" []; drop "y" []]); + ("frexp", unknown [drop "arg" []; drop "exp" [w]]); + ("frexpf", unknown [drop "arg" []; drop "exp" [w]]); + ("frexpl", unknown [drop "arg" []; drop "exp" [w]]); + ("hypot", unknown [drop "x" []; drop "y" []]); + ("hypotf", unknown [drop "x" []; drop "y" []]); + ("hypotl", unknown [drop "x" []; drop "y" []]); + ("ilogb", unknown [drop "x" []]); + ("ilogbf", unknown [drop "x" []]); + ("ilogbl", unknown [drop "x" []]); + ("ldexp", unknown [drop "arg" []; drop "exp" []]); + ("ldexpf", unknown [drop "arg" []; drop "exp" []]); + ("ldexpl", unknown [drop "arg" []; drop "exp" []]); + ("lgamma", unknown [drop "x" []]); + ("lgammaf", unknown [drop "x" []]); + ("lgammal", unknown [drop "x" []]); + ("log", unknown [drop "x" []]); + ("logf", unknown [drop "x" []]); + ("logl", unknown [drop "x" []]); + ("log10", unknown [drop "x" []]); + ("log10f", unknown [drop "x" []]); + ("log10l", unknown [drop "x" []]); + ("log1p", unknown [drop "x" []]); + ("log1pf", unknown [drop "x" []]); + ("log1pl", unknown [drop "x" []]); + ("log2", unknown [drop "x" []]); + ("log2f", unknown [drop "x" []]); + ("log2l", unknown [drop "x" []]); + ("logb", unknown [drop "x" []]); + ("logbf", unknown [drop "x" []]); + ("logbl", unknown [drop "x" []]); + ("rint", unknown [drop "x" []]); + ("rintf", unknown [drop "x" []]); + ("rintl", unknown [drop "x" []]); + ("lrint", unknown [drop "x" []]); + ("lrintf", unknown [drop "x" []]); + ("lrintl", unknown [drop "x" []]); + ("llrint", unknown [drop "x" []]); + ("llrintf", unknown [drop "x" []]); + ("llrintl", unknown [drop "x" []]); + ("round", unknown [drop "x" []]); + ("roundf", unknown [drop "x" []]); + ("roundl", unknown [drop "x" []]); + ("lround", unknown [drop "x" []]); + ("lroundf", unknown [drop "x" []]); + ("lroundl", unknown [drop "x" []]); + ("llround", unknown [drop "x" []]); + ("llroundf", unknown [drop "x" []]); + ("llroundl", unknown [drop "x" []]); + ("modf", unknown [drop "arg" []; drop "iptr" [w]]); + ("modff", unknown [drop "arg" []; drop "iptr" [w]]); + ("modfl", unknown [drop "arg" []; drop "iptr" [w]]); + ("nearbyint", unknown [drop "x" []]); + ("nearbyintf", unknown [drop "x" []]); + ("nearbyintl", unknown [drop "x" []]); + ("nextafter", unknown [drop "from" []; drop "to" []]); + ("nextafterf", unknown [drop "from" []; drop "to" []]); + ("nextafterl", unknown [drop "from" []; drop "to" []]); + ("nexttoward", unknown [drop "from" []; drop "to" []]); + ("nexttowardf", unknown [drop "from" []; drop "to" []]); + ("nexttowardl", unknown [drop "from" []; drop "to" []]); + ("pow", unknown [drop "base" []; drop "exponent" []]); + ("powf", unknown [drop "base" []; drop "exponent" []]); + ("powl", unknown [drop "base" []; drop "exponent" []]); + ("remainder", unknown [drop "x" []; drop "y" []]); + ("remainderf", unknown [drop "x" []; drop "y" []]); + ("remainderl", unknown [drop "x" []; drop "y" []]); + ("remquo", unknown [drop "x" []; drop "y" []; drop "quo" [w]]); + ("remquof", unknown [drop "x" []; drop "y" []; drop "quo" [w]]); + ("remquol", unknown [drop "x" []; drop "y" []; drop "quo" [w]]); + ("scalbn", unknown [drop "arg" []; drop "exp" []]); + ("scalbnf", unknown [drop "arg" []; drop "exp" []]); + ("scalbnl", unknown [drop "arg" []; drop "exp" []]); + ("scalbln", unknown [drop "arg" []; drop "exp" []]); + ("scalblnf", unknown [drop "arg" []; drop "exp" []]); + ("scalblnl", unknown [drop "arg" []; drop "exp" []]); + ("sqrt", unknown [drop "x" []]); + ("sqrtf", unknown [drop "x" []]); + ("sqrtl", unknown [drop "x" []]); + ("tgamma", unknown [drop "x" []]); + ("tgammaf", unknown [drop "x" []]); + ("tgammal", unknown [drop "x" []]); + ("trunc", unknown [drop "x" []]); + ("truncf", unknown [drop "x" []]); + ("truncl", unknown [drop "x" []]); + ("j0", unknown [drop "x" []]); (* GNU C Library special function *) + ("j1", unknown [drop "x" []]); (* GNU C Library special function *) + ("jn", unknown [drop "n" []; drop "x" []]); (* GNU C Library special function *) + ("y0", unknown [drop "x" []]); (* GNU C Library special function *) + ("y1", unknown [drop "x" []]); (* GNU C Library special function *) + ("yn", unknown [drop "n" []; drop "x" []]); (* GNU C Library special function *) ("fegetround", unknown []); ("fesetround", unknown [drop "round" []]); (* Our float domain is rounding agnostic *) ("__builtin_fpclassify", unknown [drop "nan" []; drop "infinite" []; drop "normal" []; drop "subnormal" []; drop "zero" []; drop "x" []]); (* TODO: We could do better here *) @@ -912,144 +1050,6 @@ let invalidate_actions = [ "sema_init", readsAll; "down_trylock", readsAll; "up", readsAll; - "acosh", readsAll; - "acoshf", readsAll; - "acoshl", readsAll; - "asinh", readsAll; - "asinhf", readsAll; - "asinhl", readsAll; - "atanh", readsAll; - "atanhf", readsAll; - "atanhl", readsAll; - "cbrt", readsAll; - "cbrtf", readsAll; - "cbrtl", readsAll; - "copysign", readsAll; - "copysignf", readsAll; - "copysignl", readsAll; - "cosh", readsAll; - "coshf", readsAll; - "coshl", readsAll; - "erf", readsAll; - "erfc", readsAll; - "erfcf", readsAll; - "erfcl", readsAll; - "erff", readsAll; - "erfl", readsAll; - "exp", readsAll; - "exp2", readsAll; - "exp2f", readsAll; - "exp2l", readsAll; - "expf", readsAll; - "expl", readsAll; - "expm1", readsAll; - "expm1f", readsAll; - "expm1l", readsAll; - "fdim", readsAll; - "fdimf", readsAll; - "fdiml", readsAll; - "fma", readsAll; - "fmaf", readsAll; - "fmal", readsAll; - "fmod", readsAll; - "fmodf", readsAll; - "fmodl", readsAll; - "frexp", readsAll; - "frexpf", readsAll; - "frexpl", readsAll; - "hypot", readsAll; - "hypotf", readsAll; - "hypotl", readsAll; - "ilogb", readsAll; - "ilogbf", readsAll; - "ilogbl", readsAll; - "j0", readsAll; - "j1", readsAll; - "jn", readsAll; - "ldexp", readsAll; - "ldexpf", readsAll; - "ldexpl", readsAll; - "lgamma", readsAll; - "lgammaf", readsAll; - "lgammal", readsAll; - "llrint", readsAll; - "llrintf", readsAll; - "llrintl", readsAll; - "llround", readsAll; - "llroundf", readsAll; - "llroundl", readsAll; - "log", readsAll; - "log10", readsAll; - "log10f", readsAll; - "log10l", readsAll; - "log1p", readsAll; - "log1pf", readsAll; - "log1pl", readsAll; - "log2", readsAll; - "log2f", readsAll; - "log2l", readsAll; - "logb", readsAll; - "logbf", readsAll; - "logbl", readsAll; - "logf", readsAll; - "logl", readsAll; - "lrint", readsAll; - "lrintf", readsAll; - "lrintl", readsAll; - "lround", readsAll; - "lroundf", readsAll; - "lroundl", readsAll; - "modf", readsAll; - "modff", readsAll; - "modfl", readsAll; - "nearbyint", readsAll; - "nearbyintf", readsAll; - "nearbyintl", readsAll; - "nextafter", readsAll; - "nextafterf", readsAll; - "nextafterl", readsAll; - "nexttoward", readsAll; - "nexttowardf", readsAll; - "nexttowardl", readsAll; - "pow", readsAll; - "powf", readsAll; - "powl", readsAll; - "remainder", readsAll; - "remainderf", readsAll; - "remainderl", readsAll; - "remquo", readsAll; - "remquof", readsAll; - "remquol", readsAll; - "rint", readsAll; - "rintf", readsAll; - "rintl", readsAll; - "round", readsAll; - "roundf", readsAll; - "roundl", readsAll; - "scalbln", readsAll; - "scalblnf", readsAll; - "scalblnl", readsAll; - "scalbn", readsAll; - "scalbnf", readsAll; - "scalbnl", readsAll; - "sinh", readsAll; - "sinhf", readsAll; - "sinhl", readsAll; - "sqrt", readsAll; - "sqrtf", readsAll; - "sqrtl", readsAll; - "tanh", readsAll; - "tanhf", readsAll; - "tanhl", readsAll; - "tgamma", readsAll; - "tgammaf", readsAll; - "tgammal", readsAll; - "trunc", readsAll; - "truncf", readsAll; - "truncl", readsAll; - "y0", readsAll; - "y1", readsAll; - "yn", readsAll; "__goblint_assume_join", readsAll; ] From e94f1c41c5f1b4b4068adc976efd6bf567a90cd6 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Thu, 8 Jun 2023 18:21:10 +0300 Subject: [PATCH 0094/1312] Convert some __builtin functions to new specifications --- src/analyses/libraryFunctions.ml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 0d432e0efa..5ea4fdf3fa 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -182,6 +182,14 @@ let pthread_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ (** GCC builtin functions. These are not builtin versions of functions from other lists. *) let gcc_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ + ("__builtin_bswap16", unknown [drop "x" []]); + ("__builtin_bswap32", unknown [drop "x" []]); + ("__builtin_bswap64", unknown [drop "x" []]); + ("__builtin_bswap128", unknown [drop "x" []]); + ("__builtin_ctz", unknown [drop "x" []]); + ("__builtin_ctzl", unknown [drop "x" []]); + ("__builtin_ctzll", unknown [drop "x" []]); + ("__builtin_clz", unknown [drop "x" []]); ("__builtin_object_size", unknown [drop "ptr" [r]; drop' []]); ("__builtin_prefetch", unknown (drop "addr" [] :: VarArgs (drop' []))); ("__builtin_expect", special [__ "exp" []; drop' []] @@ fun exp -> Identity exp); (* Identity, because just compiler optimization annotation. *) @@ -765,10 +773,6 @@ open Invalidate (* WTF: why are argument numbers 1-indexed (in partition)? *) let invalidate_actions = [ "atoi", readsAll; (*safe*) - "__builtin_ctz", readsAll; - "__builtin_ctzl", readsAll; - "__builtin_ctzll", readsAll; - "__builtin_clz", readsAll; "connect", readsAll; (*safe*) "fclose", readsAll; (*safe*) "fflush", writesAll; (*unsafe*) @@ -1026,10 +1030,6 @@ let invalidate_actions = [ "pthread_rwlock_destroy", readsAll; "pthread_rwlock_init", readsAll; "pthread_rwlock_unlock", readsAll; - "__builtin_bswap16", readsAll; - "__builtin_bswap32", readsAll; - "__builtin_bswap64", readsAll; - "__builtin_bswap128", readsAll; "__builtin_va_arg_pack_len", readsAll; "__open_too_many_args", readsAll; "usb_submit_urb", readsAll; (* first argument is written to but according to specification must not be read from anymore *) From 00941e74bd4995c27b237fe42cf4434348ba64e4 Mon Sep 17 00:00:00 2001 From: Nathan Schmidt Date: Fri, 9 Jun 2023 11:34:51 +0200 Subject: [PATCH 0095/1312] Introduced case for value = bot in make of NullByte --- src/analyses/base.ml | 1 + src/cdomains/arrayDomain.ml | 76 +++++++++---------- src/cdomains/arrayDomain.mli | 1 - src/cdomains/valueDomain.ml | 8 +- .../73-strings/01-string_literals.c | 14 ++-- .../73-strings/02-string_literals_with_null.c | 6 +- .../regression/73-strings/03-string_basics.c | 22 +++--- 7 files changed, 62 insertions(+), 66 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index c83263d445..0090f85b0a 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2118,6 +2118,7 @@ struct (* else compute strlen in array domain *) else begin match eval_rv (Analyses.ask_of_ctx ctx) gs st s with + (* TODO: found out during debugging that case is not picked even when it should -- why?? *) | Array array_s -> Int(CArrays.to_string_length array_s) | _ -> VD.top_value (unrollType dest_typ) end in diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index b027a57028..680ff50566 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -106,7 +106,6 @@ sig include LatticeWithSmartOps val null: unit -> t - val not_null: unit -> t val is_null: t -> bool val is_int_ikind: t -> Cil.ikind option @@ -1005,12 +1004,8 @@ struct else false in let min interval = match Idx.minimal interval with - | Some min_num -> - if Z.lt min_num Z.zero then - Z.zero (* assume worst case minimal natural number *) - else - min_num - | None -> Z.zero in (* assume worst case minimal natural number *) + | Some min_num when Z.geq min_num Z.zero -> min_num + | _ -> Z.zero in (* assume worst case minimal natural number *) let min_i = min i in let max_i = Idx.maximal i in @@ -1056,12 +1051,8 @@ struct else add_indexes (Z.succ i) max (MayNulls.add i may_nulls_set) in let min interval = match Idx.minimal interval with - | Some min_num -> - if Z.lt min_num Z.zero then - Z.zero (* assume worst case minimal natural number *) - else - min_num - | None -> Z.zero in (* assume worst case minimal natural number *) + | Some min_num when Z.geq min_num Z.zero -> min_num + | _ -> Z.zero in (* assume worst case minimal natural number *) let min_size = min size in let min_i = min i in @@ -1153,35 +1144,38 @@ struct let make ?(varAttr=[]) ?(typAttr=[]) i v = let min_i, max_i = match Idx.minimal i, Idx.maximal i with - | Some min, Some max -> - if Z.lt min Z.zero && Z.lt max Z.zero then + | Some min_i, Some max_i -> + if Z.lt min_i Z.zero && Z.lt max_i Z.zero then (M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.before_start "Tries to create an array of negative size"; Z.zero, Some Z.zero) - else if Z.lt min Z.zero then + else if Z.lt min_i Z.zero then (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.before_start "May try to create an array of negative size"; - Z.zero, Some max) + Z.zero, Some max_i) else - min, Some max - | None, Some max -> - if Z.lt max Z.zero then + min_i, Some max_i + | None, Some max_i -> + if Z.lt max_i Z.zero then (M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.before_start "Tries to create an array of negative size"; Z.zero, Some Z.zero) else - Z.zero, Some max - | Some min, None -> - if Z.lt min Z.zero then + Z.zero, Some max_i + | Some min_i, None -> + if Z.lt min_i Z.zero then (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.before_start "May try to create an array of negative size"; Z.zero, None) else - min, None + min_i, None | None, None -> Z.zero, None in - match max_i, Val.is_null v with + match max_i, Val.is_null v, Val.is_bot v with (* if value = null, return (bot = all indexes up to minimal size - 1, top = all indexes up to maximal size - 1, size) *) - | Some max_i, true -> (MustNulls.bot (), MayNulls.top (), Idx.of_interval ILong (min_i, max_i)) - | None, true -> (MustNulls.bot (), MayNulls.top (), Idx.starting ILong min_i) + | Some max_i, true, _ -> (MustNulls.bot (), MayNulls.top (), Idx.of_interval ILong (min_i, max_i)) + | None, true, _ -> (MustNulls.bot (), MayNulls.top (), Idx.starting ILong min_i) + (* if value = bot, return (top = no indexes, top = all indexes up to maximal size - 1, size) *) + | Some max_i, false, true -> (MustNulls.top (), MayNulls.top (), Idx.of_interval ILong (min_i, max_i)) + | None, false, true -> (MustNulls.top (), MayNulls.top (), Idx.starting ILong min_i) (* if value <> null, return (top = no indexes, bot = no indexes, size) *) - | Some max_i, false -> (MustNulls.top (), MayNulls.bot (), Idx.of_interval ILong (min_i, max_i)) - | None, false -> (MustNulls.top (), MayNulls.bot (), Idx.starting ILong min_i) + | Some max_i, false, false -> (MustNulls.top (), MayNulls.bot (), Idx.of_interval ILong (min_i, max_i)) + | None, false, false -> (MustNulls.top (), MayNulls.bot (), Idx.starting ILong min_i) let length (_, _, size) = Some size @@ -1298,15 +1292,15 @@ struct if MustNulls.is_empty must_nulls_set && MayNulls.is_empty may_nulls_set then (M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array doesn't contain a null byte: buffer overflow"; match Idx.minimal size with - | Some min_size -> Idx.starting ILong min_size - | None -> Idx.starting ILong Z.zero) + | Some min_size -> Idx.starting !Cil.kindOfSizeOf min_size + | None -> Idx.starting !Cil.kindOfSizeOf Z.zero) (* if only must_nulls_set empty, no guarantee that null ever encountered in array => return interval [minimal may null, inf) and *) else if MustNulls.is_empty must_nulls_set then (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array might not contain a null byte: potential buffer overflow"; - Idx.starting ILong (MayNulls.min_elt may_nulls_set)) + Idx.starting !Cil.kindOfSizeOf (MayNulls.min_elt may_nulls_set)) (* else return interval [minimal may null, minimal must null] *) else - Idx.of_interval ILong (MayNulls.min_elt may_nulls_set, MustNulls.min_elt must_nulls_set) + Idx.of_interval !Cil.kindOfSizeOf (MayNulls.min_elt may_nulls_set, MustNulls.min_elt must_nulls_set) let string_copy (must_nulls_set1, may_nulls_set1, size1) ar2 n = (* filter out indexes before strlen(src) from dest sets and after strlen(src) from src sets and build union, keep size of dest *) @@ -1370,9 +1364,10 @@ struct let strlen2 = to_string_length ar2 in update_sets must_nulls_set2 may_nulls_set2 (Idx.minimal strlen2) (Idx.maximal strlen2) (* strncpy = exactly n bytes from src are copied to dest *) - | Some n -> + | Some n when n >= 0 -> let must_nulls_set2, may_nulls_set2, _ = to_n_string ar2 n in update_sets must_nulls_set2 may_nulls_set2 (Some (Z.of_int n)) (Some (Z.of_int n)) + | _ -> (MustNulls.top (), MayNulls.top(), size1) let string_concat (must_nulls_set1, may_nulls_set1, size1) (must_nulls_set2, may_nulls_set2, size2) n = let update_sets min_size1 max_size1 max_size1_exists minlen1 maxlen1 maxlen1_exists minlen2 maxlen2 maxlen2_exists must_nulls_set2' may_nulls_set2' = @@ -1456,7 +1451,7 @@ struct let must_nulls_set2', may_nulls_set2', _ = to_string (must_nulls_set2, may_nulls_set2, size2) in compute_concat must_nulls_set2' may_nulls_set2' (* strncat *) - | Some n -> + | Some n when n >= 0 -> (* take at most n bytes from src; if no null byte among them, add null byte at index n *) let must_nulls_set2', may_nulls_set2' = let must_nulls_set2, may_nulls_set2, _ = to_string (must_nulls_set2, may_nulls_set2, size2) in @@ -1467,6 +1462,7 @@ struct else (MustNulls.filter (Z.gt (Z.of_int n)) must_nulls_set2, MayNulls.filter (Z.gt (Z.of_int n)) may_nulls_set2) in compute_concat must_nulls_set2' may_nulls_set2' + | _ -> (MustNulls.top (), MayNulls.top (), size1) let substring_extraction haystack (must_nulls_set_needle, may_nulls_set_needle, size_needle) = (* if needle is empty string, i.e. certain null byte at index 0, return haystack as string *) @@ -1521,14 +1517,11 @@ struct (* compute abstract value for result of strcmp *) compare Z.zero false (* strncmp *) - | Some n -> - if n < 0 then - Idx.top_of IInt - else - let min_size1 = match Idx.minimal size1 with + | Some n when n >= 0 -> + let min_size1 = match Idx.minimal size1 with | Some min_size1 -> min_size1 | None -> Z.zero in - let min_size2 = match Idx.minimal size2 with + let min_size2 = match Idx.minimal size2 with | Some min_size2 -> min_size2 | None -> Z.zero in (* issue a warning if n is (potentially) smaller than array sizes *) @@ -1552,6 +1545,7 @@ struct M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The size of the array of string 2 might be smaller than n bytes"); (* compute abstract value for result of strncmp *) compare (Z.of_int n) true + | _ -> Idx.top_of IInt let update_length new_size (must_nulls_set, may_nulls_set, size) = (must_nulls_set, may_nulls_set, new_size) diff --git a/src/cdomains/arrayDomain.mli b/src/cdomains/arrayDomain.mli index 9bfa85fb5d..ef503248c6 100644 --- a/src/cdomains/arrayDomain.mli +++ b/src/cdomains/arrayDomain.mli @@ -121,7 +121,6 @@ sig include LatticeWithSmartOps val null: unit -> t - val not_null: unit -> t val is_null: t -> bool val is_int_ikind: t -> Cil.ikind option diff --git a/src/cdomains/valueDomain.ml b/src/cdomains/valueDomain.ml index 8846a5be1f..2ae980369e 100644 --- a/src/cdomains/valueDomain.ml +++ b/src/cdomains/valueDomain.ml @@ -39,7 +39,6 @@ sig val zero_init_value: ?varAttr:attributes -> typ -> t val null: unit -> t - val not_null: unit -> t val is_null: t -> bool val is_int_ikind: t -> Cil.ikind option @@ -272,9 +271,12 @@ struct let top_name = "Unknown" let null () = Int(ID.of_int IChar Z.zero) - let not_null () = Top let is_null = function - | Int n -> ID.to_int n = Some Z.zero + | Int n -> + begin match ID.to_int n with + | Some n -> Z.equal n Z.zero + | None -> false + end | _ -> false let is_int_ikind = function diff --git a/tests/regression/73-strings/01-string_literals.c b/tests/regression/73-strings/01-string_literals.c index 36e4ed121c..14f4d43014 100644 --- a/tests/regression/73-strings/01-string_literals.c +++ b/tests/regression/73-strings/01-string_literals.c @@ -22,16 +22,16 @@ int main() { char* s2 = "abcdfg"; char* s3 = hello_world(); - int i = strlen(s1); - __goblint_check(i == 5); + size_t len = strlen(s1); + __goblint_check(len == 5); - i = strlen(s2); - __goblint_check(i == 6); + len = strlen(s2); + __goblint_check(len == 6); - i = strlen(s3); - __goblint_check(i == 12); + len = strlen(s3); + __goblint_check(len == 12); - i = strcmp(s1, s2); + int i = strcmp(s1, s2); __goblint_check(i < 0); i = strcmp(s2, "abcdfg"); diff --git a/tests/regression/73-strings/02-string_literals_with_null.c b/tests/regression/73-strings/02-string_literals_with_null.c index 75d000bbb8..6d6717dcba 100644 --- a/tests/regression/73-strings/02-string_literals_with_null.c +++ b/tests/regression/73-strings/02-string_literals_with_null.c @@ -9,10 +9,10 @@ int main() { char* s3 = "hello world!"; char* s4 = "\0 i am the empty string"; - int i = strlen(s1); - __goblint_check(i == 5); + size_t len = strlen(s1); + __goblint_check(len == 5); - i = strcmp(s1, s2); + int i = strcmp(s1, s2); __goblint_check(i == 0); i = strcmp(s3, s1); diff --git a/tests/regression/73-strings/03-string_basics.c b/tests/regression/73-strings/03-string_basics.c index db196c64b4..88bbe58796 100644 --- a/tests/regression/73-strings/03-string_basics.c +++ b/tests/regression/73-strings/03-string_basics.c @@ -19,23 +19,23 @@ int main() { char s3[10] = "abcd"; char s4[20] = "abcdf"; - int i = strlen(s1); - __goblint_check(i == 6); // UNKNOWN + size_t len = strlen(s1); + __goblint_check(len == 6); // UNKNOWN - i = strlen(s2); - __goblint_check(i == 6); // UNKNOWN + len = strlen(s2); + __goblint_check(len == 6); // UNKNOWN - i = strlen(s3); - __goblint_check(i == 4); // UNKNOWN + len = strlen(s3); + __goblint_check(len == 4); // UNKNOWN strcat(s1, s2); - i = strcmp(s1, "hello world!"); + int i = strcmp(s1, "hello world!"); __goblint_check(i == 0); // UNKNOWN strcpy(s1, "hi "); strncpy(s1, s3, 3); - i = strlen(s1); - __goblint_check(i == 3); // UNKNOWN + len = strlen(s1); + __goblint_check(len == 3); // UNKNOWN strcat(s1, "ababcd"); char* cmp = strstr(s1, "bab"); @@ -52,8 +52,8 @@ int main() { strncpy(s1, "", 20); concat_1(s1, 30); - i = strlen(s1); - __goblint_check(i == 30); // UNKNOWN + len = strlen(s1); + __goblint_check(len == 30); // UNKNOWN cmp = strstr(s1, "0"); __goblint_check(cmp == NULL); // UNKNOWN From 2841622620ccd483e19794b7dc6be15309e2ce52 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Fri, 9 Jun 2023 15:19:07 +0200 Subject: [PATCH 0096/1312] Introduce to_interval --- src/cdomains/intDomain.ml | 13 ++++++++++--- src/cdomains/intDomain.mli | 3 +++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/cdomains/intDomain.ml b/src/cdomains/intDomain.ml index dea3daecd8..ed6ef5a403 100644 --- a/src/cdomains/intDomain.ml +++ b/src/cdomains/intDomain.ml @@ -171,6 +171,7 @@ sig val equal_to: int_t -> t -> [`Eq | `Neq | `Top] val to_bool: t -> bool option + val to_interval: t -> (int_t * int_t) option val to_excl_list: t -> (int_t list * (int64 * int64)) option val of_excl_list: Cil.ikind -> int_t list -> t val is_excl_list: t -> bool @@ -219,7 +220,6 @@ sig val ending : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t val of_int: Cil.ikind -> int_t -> t val of_bool: Cil.ikind -> bool -> t - val to_interval: t -> (int_t * int_t) option val of_interval: ?suppress_ovwarn:bool -> Cil.ikind -> int_t * int_t -> t val of_congruence: Cil.ikind -> int_t * int_t -> t val is_top_of: Cil.ikind -> t -> bool @@ -712,7 +712,7 @@ struct (* TODO: change to_int signature so it returns a big_int *) let to_int x = Option.bind x (IArith.to_int) - let to_interval x = x + let to_interval = Fun.id let of_interval ?(suppress_ovwarn=false) ik (x,y) = norm ~suppress_ovwarn ik @@ Some (x,y) let of_int ik (x: int_t) = of_interval ik (x,x) let zero = Some IArith.zero @@ -1646,6 +1646,7 @@ struct let to_bool x = Some (to_bool' x) let of_int x = x let to_int x = Some x + let to_interval = failwith "Not implemented!" (* FIXME *) let neg = Ints_t.neg let add = Ints_t.add (* TODO: signed overflow is undefined behavior! *) @@ -1720,6 +1721,7 @@ struct let of_excl_list ik x = top_of ik let is_excl_list x = false let to_incl_list x = None + let to_interval x = None let of_interval ?(suppress_ovwarn=false) ik x = top_of ik let of_congruence ik x = top_of ik let starting ?(suppress_ovwarn=false) ikind x = top_of ikind @@ -1803,6 +1805,10 @@ struct | `Bot, `Bot -> `Bot | _ -> `Top + let to_interval = function + | `Lifted x -> Base.to_interval x + | _ -> None + let neg = lift1 Base.neg let add = lift2 Base.add let sub = lift2 Base.sub @@ -2408,6 +2414,7 @@ struct let to_bool x = Some x let of_int x = x = Int64.zero let to_int x = if x then None else Some Int64.zero + let to_interval = failwith "Not implemented!" (* FIXME *) let neg x = x let add x y = x || y @@ -3506,7 +3513,7 @@ module IntDomTupleImpl = struct let flat f x = match to_list_some x with [] -> None | xs -> Some (f xs) - let to_interval (_, i, _, _, _) = I2.to_interval i + let to_interval (_, i, _, _, _) = Option.bind i I2.to_interval let to_excl_list x = let merge ps = diff --git a/src/cdomains/intDomain.mli b/src/cdomains/intDomain.mli index a853c8acca..5a6a4d7c04 100644 --- a/src/cdomains/intDomain.mli +++ b/src/cdomains/intDomain.mli @@ -187,6 +187,9 @@ sig (** Give a boolean interpretation of an abstract value if possible, otherwise * don't return anything.*) + val to_interval: t -> (int_t * int_t) option + (** Gives an interval interpretation if possible. *) + val to_excl_list: t -> (int_t list * (int64 * int64)) option (** Gives a list representation of the excluded values from included range of bits if possible. *) From 96a59cbaadb917ed2f5665fce6df4e6c73a84299 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Fri, 9 Jun 2023 16:24:22 +0300 Subject: [PATCH 0097/1312] Convert most file-related functions to new specifications --- src/analyses/libraryFunctions.ml | 67 ++++++++++++++++---------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 5ea4fdf3fa..8fdc867834 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -15,6 +15,8 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("memcpy", special [__ "dest" [w]; __ "src" [r]; drop "n" []] @@ fun dest src -> Memcpy { dest; src }); ("__builtin_memcpy", special [__ "dest" [w]; __ "src" [r]; drop "n" []] @@ fun dest src -> Memcpy { dest; src }); ("__builtin___memcpy_chk", special [__ "dest" [w]; __ "src" [r]; drop "n" []; drop "os" []] @@ fun dest src -> Memcpy { dest; src }); + ("mempcpy", unknown [drop "dest" [w]; drop "src" [r]; drop "n" []]); + ("__builtin___mempcpy_chk", unknown [drop "dest" [w]; drop "src" [r]; drop "n" []; drop "os" []]); ("strcpy", special [__ "dest" [w]; __ "src" [r]] @@ fun dest src -> Strcpy { dest; src; n = None; }); ("__builtin_strcpy", special [__ "dest" [w]; __ "src" [r]] @@ fun dest src -> Strcpy { dest; src; n = None; }); ("__builtin___strcpy_chk", special [__ "dest" [w]; __ "src" [r]; drop "os" []] @@ fun dest src -> Strcpy { dest; src; n = None; }); @@ -27,6 +29,27 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("strncat", special [__ "dest" [r; w]; __ "src" [r]; __ "n" []] @@ fun dest src n -> Strcat { dest; src; n = Some n; }); ("__builtin_strncat", special [__ "dest" [r; w]; __ "src" [r]; __ "n" []] @@ fun dest src n -> Strcat { dest; src; n = Some n; }); ("__builtin___strncat_chk", special [__ "dest" [r; w]; __ "src" [r]; __ "n" []; drop "os" []] @@ fun dest src n -> Strcat { dest; src; n = Some n; }); + ("free", unknown [drop "ptr" [f]]); + ("fclose", unknown [drop "stream" [r_deep; w_deep; f_deep]]); + ("feof", unknown [drop "stream" [r_deep; w_deep]]); + ("ferror", unknown [drop "stream" [r_deep; w_deep]]); + ("fflush", unknown [drop "stream" [r_deep; w_deep]]); + ("fgetc", unknown [drop "stream" [r_deep; w_deep]]); + ("getc", unknown [drop "stream" [r_deep; w_deep]]); + ("fgets", unknown [drop "str" [r; w]; drop "count" []; drop "stream" [r_deep; w_deep]]); + ("fopen", unknown [drop "pathname" [r]; drop "mode" [r]]); + ("fdopen", unknown [drop "fd" []; drop "mode" [r]]); + ("fprintf", unknown [drop "stream" [r_deep; w_deep]; drop "format" [r]]); + ("fputc", unknown [drop "ch" []; drop "stream" [r_deep; w_deep];]); + ("fputs", unknown [drop "str" [r]; drop "stream" [r_deep; w_deep];]); + ("fread", unknown [drop "buffer" [w_deep]; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); + ("fseek", unknown [drop "stream" [r_deep; w_deep]; drop "offset" []; drop "origin" []]); + ("ftell", unknown [drop "stream" [r_deep]]); + ("fwrite", unknown [drop "buffer" [r_deep]; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); + ("setvbuf", unknown [drop "stream" [r_deep; w_deep]; drop "buffer" [r_deep; w_deep]; drop "mode" []; drop "size" []]); + (* TODO: if this is used to set an input buffer, the buffer (second argument) would need to remain TOP, *) + (* as any future write (or flush) of the stream could result in a write to the buffer *) + ("localtime", unknown [drop "time" [r]]); ("strlen", special [__ "s" [r]] @@ fun s -> Strlen s); ("strstr", special [__ "haystack" [r]; __ "needle" [r]] @@ fun haystack needle -> Strstr { haystack; needle; }); ("strcmp", special [__ "s1" [r]; __ "s2" [r]] @@ fun s1 s2 -> Strcmp { s1; s2; n = None; }); @@ -85,7 +108,10 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("putc_unlocked", unknown [drop "c" []; drop "stream" [w]]); ("putchar_unlocked", unknown [drop "c" []]); ("lseek", unknown [drop "fd" []; drop "offset" []; drop "whence" []]); - ("fseeko", unknown [drop "stream" [w]; drop "offset" []; drop "whence" []]); + ("fcntl", unknown (drop "fd" [] :: drop "cmd" [] :: VarArgs (drop' [r; w]))); + ("fseeko", unknown [drop "stream" [r_deep; w_deep]; drop "offset" []; drop "whence" []]); + ("fileno", unknown [drop "stream" [r_deep; w_deep]]); + ("getopt", unknown [drop "argc" []; drop "argv" [r]; drop "optstring" [r]]); ("iconv_open", unknown [drop "tocode" [r]; drop "fromcode" [r]]); ("iconv", unknown [drop "cd" [r]; drop "inbuf" [r]; drop "inbytesleft" [r;w]; drop "outbuf" [w]; drop "outbytesleft" [r;w]]); ("iconv_close", unknown [drop "cd" [f]]); @@ -98,6 +124,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("gettimeofday", unknown [drop "tv" [w]; drop "tz" [w]]); ("futimens", unknown [drop "fd" []; drop "times" [r]]); ("utimes", unknown [drop "filename" [r]; drop "times" [r]]); + ("utimensat", unknown [drop "dirfd" []; drop "pathname" [r]; drop "times" [r]; drop "flags" []]); ("linkat", unknown [drop "olddirfd" []; drop "oldpath" [r]; drop "newdirfd" []; drop "newpath" [r]; drop "flags" []]); ("dirfd", unknown [drop "dirp" [r]]); ("fdopendir", unknown [drop "fd" []]); @@ -234,12 +261,16 @@ let gcc_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ let glibc_desc_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("fputs_unlocked", unknown [drop "s" [r]; drop "stream" [w]]); - ("futimesat", unknown [drop "dirfd" [w]; drop "pathname" [r]; drop "times" [r]]); + ("futimesat", unknown [drop "dirfd" []; drop "pathname" [r]; drop "times" [r]]); ("error", unknown ((drop "status" []):: (drop "errnum" []) :: (drop "format" [r]) :: (VarArgs (drop' [r])))); ("gettext", unknown [drop "msgid" [r]]); ("euidaccess", unknown [drop "pathname" [r]; drop "mode" []]); ("rpmatch", unknown [drop "response" [r]]); ("getpagesize", unknown []); + ("__fgets_alias", unknown [drop "__s" [r; w]; drop "__n" []; drop "__stream" [r_deep; w_deep]]); + ("__fgets_chk", unknown [drop "__s" [r; w]; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); + ("__fread_alias", unknown [drop "__ptr" [w_deep]; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); + ("__fread_chk", unknown [drop "__ptr" [w_deep]; drop "__ptrlen" []; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); ("__read_chk", unknown [drop "__fd" []; drop "__buf" [w]; drop "__nbytes" []; drop "__buflen" []]); ("__read_alias", unknown [drop "__fd" []; drop "__buf" [w]; drop "__nbytes" []]); ("__readlink_chk", unknown [drop "path" [r]; drop "buf" [w]; drop "len" []; drop "buflen" []]); @@ -276,6 +307,7 @@ let linux_userspace_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("epoll_create", unknown [drop "size" []]); ("epoll_ctl", unknown [drop "epfd" []; drop "op" []; drop "fd" []; drop "event" [w]]); ("epoll_wait", unknown [drop "epfd" []; drop "events" [w]; drop "maxevents" []; drop "timeout" []]); + ("__fprintf_chk", unknown [drop "stream" [r_deep; w_deep]; drop "flag" []; drop "format" [r]]); ("sysinfo", unknown [drop "info" [w_deep]]); ("__xpg_basename", unknown [drop "path" [r]]); ("ptrace", unknown (drop "request" [] :: VarArgs (drop' [r_deep; w_deep]))); (* man page has 4 arguments, but header has varargs and real-world programs may call with <4 *) @@ -774,24 +806,6 @@ open Invalidate let invalidate_actions = [ "atoi", readsAll; (*safe*) "connect", readsAll; (*safe*) - "fclose", readsAll; (*safe*) - "fflush", writesAll; (*unsafe*) - "fopen", readsAll; (*safe*) - "fdopen", readsAll; (*safe*) - "setvbuf", writes[1;2]; (* TODO: if this is used to set an input buffer, the buffer (second argument) would need to remain TOP, *) - (* as any future write (or flush) of the stream could result in a write to the buffer *) - "fprintf", writes [1]; (*keep [1]*) - "__fprintf_chk", writes [1]; (*keep [1]*) - "fread", writes [1;4]; - "__fread_alias", writes [1;4]; - "__fread_chk", writes [1;4]; - "utimensat", readsAll; - "free", frees [1]; (*unsafe*) - "fwrite", readsAll;(*safe*) - "getopt", writes [2];(*keep [2]*) - "localtime", readsAll;(*safe*) - "mempcpy", writes [1];(*keep [1]*) - "__builtin___mempcpy_chk", writes [1]; "printf", readsAll;(*safe*) "__printf_chk", readsAll;(*safe*) "printk", readsAll;(*safe*) @@ -853,8 +867,6 @@ let invalidate_actions = [ "readdir_r", writesAll;(*unsafe*) "atoi__extinline", readsAll;(*safe*) "getpid", readsAll;(*safe*) - "fgetc", writesAll;(*unsafe*) - "getc", writesAll;(*unsafe*) "_IO_getc", writesAll;(*unsafe*) "closedir", writesAll;(*unsafe*) "chdir", readsAll;(*safe*) @@ -904,7 +916,6 @@ let invalidate_actions = [ "open", readsAll;(*safe*) "__open_alias", readsAll;(*safe*) "__open_2", readsAll;(*safe*) - "fcntl", readsAll;(*safe*) "ioctl", writesAll;(*unsafe*) "fstat__extinline", writesAll;(*unsafe*) "umount", readsAll;(*safe*) @@ -922,18 +933,11 @@ let invalidate_actions = [ "dcgettext", readsAll;(*safe*) "syscall", writesAllButFirst 1 readsAll;(*drop 1*) "sysconf", readsAll; - "fputs", readsAll;(*safe*) - "fputc", readsAll;(*safe*) - "fseek", writes[1]; "rewind", writesAll; - "fileno", readsAll; - "ferror", readsAll; - "ftell", readsAll; "putc", readsAll;(*safe*) "putw", readsAll;(*safe*) "putchar", readsAll;(*safe*) "getchar", readsAll;(*safe*) - "feof", readsAll;(*safe*) "__getdelim", writes [3];(*keep [3]*) "vsyslog", readsAll;(*safe*) "gethostbyname_r", readsAll;(*safe*) @@ -982,9 +986,6 @@ let invalidate_actions = [ "getpeername", writes [1]; (*keep [1]*) "times", writesAll; (*unsafe*) "timespec_get", writes [1]; - "fgets", writes [1;3]; (*keep [3]*) - "__fgets_alias", writes [1;3]; (*keep [3]*) - "__fgets_chk", writes [1;3]; (*keep [3]*) "__tolower", readsAll; (*safe*) "signal", writesAll; (*unsafe*) "strsignal", readsAll; From 93ea56e6c6ad00e642bc3f5188f23fc67d87b133 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Fri, 9 Jun 2023 15:50:22 +0200 Subject: [PATCH 0098/1312] IntervalSet implementation for to_interval --- src/cdomains/intDomain.ml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cdomains/intDomain.ml b/src/cdomains/intDomain.ml index ed6ef5a403..c0bb39518e 100644 --- a/src/cdomains/intDomain.ml +++ b/src/cdomains/intDomain.ml @@ -1270,7 +1270,9 @@ struct let of_bool _ = function true -> one | false -> zero - let to_interval = failwith "Not implemented!" (* FIXME *) + let to_interval l = match minimal l, maximal l with + | Some x, Some y -> Some (x, y) + | _ -> None let of_interval ?(suppress_ovwarn=false) ik (x,y) = norm_interval ~suppress_ovwarn ~cast:false ik (x,y) From 6a4c50afd706d64e78e8d8b94ca918d02d03aebd Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Fri, 9 Jun 2023 16:16:02 +0200 Subject: [PATCH 0099/1312] Fix to_interval functions --- src/cdomains/intDomain.ml | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/cdomains/intDomain.ml b/src/cdomains/intDomain.ml index c0bb39518e..28fb4ae9e3 100644 --- a/src/cdomains/intDomain.ml +++ b/src/cdomains/intDomain.ml @@ -350,7 +350,7 @@ struct with Failure _ -> top_of ik - let to_interval = failwith "Not implemented!" (* FIXME *) + let to_interval x = failwith "Not implemented!" (* FIXME *) let starting ?(suppress_ovwarn=false) ik x = try Old.starting ~suppress_ovwarn ik (BI.to_int64 x) with Failure _ -> top_of ik @@ -1648,7 +1648,7 @@ struct let to_bool x = Some (to_bool' x) let of_int x = x let to_int x = Some x - let to_interval = failwith "Not implemented!" (* FIXME *) + let to_interval x = Some (x, x) let neg = Ints_t.neg let add = Ints_t.add (* TODO: signed overflow is undefined behavior! *) @@ -2152,7 +2152,9 @@ struct let top_bool = `Excluded (S.empty (), R.of_interval range_ikind (0L, 1L)) let of_interval ?(suppress_ovwarn=false) ik (x,y) = if BigInt.compare x y = 0 then of_int ik x else top_of ik - let to_interval = failwith "Not implemented!" (* FIXME *) + let to_interval l = match minimal l, maximal l with + | Some x, Some y -> Some (x, y) + | _ -> None let starting ?(suppress_ovwarn=false) ikind x = if BigInt.compare x BigInt.zero > 0 then not_zero ikind else top_of ikind let ending ?(suppress_ovwarn=false) ikind x = if BigInt.compare x BigInt.zero < 0 then not_zero ikind else top_of ikind @@ -2416,7 +2418,7 @@ struct let to_bool x = Some x let of_int x = x = Int64.zero let to_int x = if x then None else Some Int64.zero - let to_interval = failwith "Not implemented!" (* FIXME *) + let to_interval x = if x then None else Some (Int64.zero, Int64.zero) let neg x = x let add x y = x || y @@ -2546,7 +2548,9 @@ module Enums : S with type int_t = BigInt.t = struct let of_int ikind x = cast_to ikind (Inc (BISet.singleton x)) let of_interval ?(suppress_ovwarn=false) ik (x,y) = if x = y then of_int ik x else top_of ik - let to_interval = failwith "Not implemented!" (* FIXME *) + let to_interval l = match minimal l, maximal l with + | Some x, Some y -> Some (x, y) + | _ -> None let join ik = curry @@ function | Inc x, Inc y -> Inc (BISet.union x y) @@ -3282,7 +3286,7 @@ struct let project ik p t = t - let to_interval = failwith "Not implemented!" (* FIXME *) + let to_interval x = failwith "Not implemented!" (* FIXME *) end module SOverflowLifter (D : S) : SOverflow with type int_t = D.int_t and type t = D.t = struct From 1d0b8286b61655347728d3edf95a697db432a86e Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Fri, 9 Jun 2023 17:35:45 +0200 Subject: [PATCH 0100/1312] Add to_interval in valueDomainQueries.ml --- src/domains/valueDomainQueries.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/domains/valueDomainQueries.ml b/src/domains/valueDomainQueries.ml index c89e491e58..2dfa72a430 100644 --- a/src/domains/valueDomainQueries.ml +++ b/src/domains/valueDomainQueries.ml @@ -34,6 +34,7 @@ struct let to_int x = unlift_opt I.to_int x let to_bool x = unlift_opt I.to_bool x + let to_interval x = unlift_opt I.to_interval x let is_top_of ik = unlift_is (I.is_top_of ik) From ce50eb4eb88693f98316f5f2a2db1cfac4e2b02a Mon Sep 17 00:00:00 2001 From: karoliineh Date: Fri, 9 Jun 2023 19:11:02 +0300 Subject: [PATCH 0101/1312] Fix fprintf: add VarArgs --- src/analyses/libraryFunctions.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 8fdc867834..ac60ec2643 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -39,7 +39,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("fgets", unknown [drop "str" [r; w]; drop "count" []; drop "stream" [r_deep; w_deep]]); ("fopen", unknown [drop "pathname" [r]; drop "mode" [r]]); ("fdopen", unknown [drop "fd" []; drop "mode" [r]]); - ("fprintf", unknown [drop "stream" [r_deep; w_deep]; drop "format" [r]]); + ("fprintf", unknown (drop "stream" [r_deep; w_deep] :: drop "format" [r] :: VarArgs (drop' []))); (* TODO: why not r for VarArgs?*) ("fputc", unknown [drop "ch" []; drop "stream" [r_deep; w_deep];]); ("fputs", unknown [drop "str" [r]; drop "stream" [r_deep; w_deep];]); ("fread", unknown [drop "buffer" [w_deep]; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); From c272f14e6a530829666841fd53786201176bb906 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Fri, 9 Jun 2023 19:11:49 +0300 Subject: [PATCH 0102/1312] Mark and add thread-unsafe functions in libraryFunctions, relates to #723 --- src/analyses/libraryFunctions.ml | 110 +++++++++++++++++++++++-------- 1 file changed, 84 insertions(+), 26 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index ac60ec2643..9b689b64ad 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -29,6 +29,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("strncat", special [__ "dest" [r; w]; __ "src" [r]; __ "n" []] @@ fun dest src n -> Strcat { dest; src; n = Some n; }); ("__builtin_strncat", special [__ "dest" [r; w]; __ "src" [r]; __ "n" []] @@ fun dest src n -> Strcat { dest; src; n = Some n; }); ("__builtin___strncat_chk", special [__ "dest" [r; w]; __ "src" [r]; __ "n" []; drop "os" []] @@ fun dest src n -> Strcat { dest; src; n = Some n; }); + ("asctime", unknown ~attrs:[ThreadUnsafe] [drop "time_ptr" [r_deep]]); ("free", unknown [drop "ptr" [f]]); ("fclose", unknown [drop "stream" [r_deep; w_deep; f_deep]]); ("feof", unknown [drop "stream" [r_deep; w_deep]]); @@ -49,10 +50,13 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("setvbuf", unknown [drop "stream" [r_deep; w_deep]; drop "buffer" [r_deep; w_deep]; drop "mode" []; drop "size" []]); (* TODO: if this is used to set an input buffer, the buffer (second argument) would need to remain TOP, *) (* as any future write (or flush) of the stream could result in a write to the buffer *) - ("localtime", unknown [drop "time" [r]]); + ("gmtime", unknown ~attrs:[ThreadUnsafe] [drop "timer" [r_deep]]); + ("localeconv", unknown ~attrs:[ThreadUnsafe] []); + ("localtime", unknown ~attrs:[ThreadUnsafe] [drop "time" [r]]); ("strlen", special [__ "s" [r]] @@ fun s -> Strlen s); ("strstr", special [__ "haystack" [r]; __ "needle" [r]] @@ fun haystack needle -> Strstr { haystack; needle; }); ("strcmp", special [__ "s1" [r]; __ "s2" [r]] @@ fun s1 s2 -> Strcmp { s1; s2; n = None; }); + ("strtok", unknown ~attrs:[ThreadUnsafe] [drop "str" [r]; drop "delim" [r]]); ("__builtin_strcmp", special [__ "s1" [r]; __ "s2" [r]] @@ fun s1 s2 -> Strcmp { s1; s2; n = None; }); ("strncmp", special [__ "s1" [r]; __ "s2" [r]; __ "n" []] @@ fun s1 s2 n -> Strcmp { s1; s2; n = Some n; }); ("malloc", special [__ "size" []] @@ fun size -> Malloc size); @@ -69,6 +73,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("iswprint", unknown [drop "wc" []]); ("rename" , unknown [drop "oldpath" [r]; drop "newpath" [r];]); ("puts", unknown [drop "s" [r]]); + ("rand", unknown ~attrs:[ThreadUnsafe] []); ("strspn", unknown [drop "s" [r]; drop "accept" [r]]); ("strcspn", unknown [drop "s" [r]; drop "accept" [r]]); ("strtod", unknown [drop "nptr" [r]; drop "endptr" [w]]); @@ -77,15 +82,17 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("strtoll", unknown [drop "nptr" [r]; drop "endptr" [w]; drop "base" []]); ("strtoul", unknown [drop "nptr" [r]; drop "endptr" [w]; drop "base" []]); ("strtoull", unknown [drop "nptr" [r]; drop "endptr" [w]; drop "base" []]); + ("tmpnam", unknown ~attrs:[ThreadUnsafe] [drop "filename" [r]]); ("mktime", unknown [drop "tm" [r;w]]); - ("ctime", unknown [drop "rm" [r]]); + ("ctime", unknown ~attrs:[ThreadUnsafe] [drop "rm" [r]]); ("clearerr", unknown [drop "stream" [w]]); ("setbuf", unknown [drop "stream" [w]; drop "buf" [w]]); ("swprintf", unknown (drop "wcs" [w] :: drop "maxlen" [] :: drop "fmt" [r] :: VarArgs (drop' []))); ("assert", special [__ "exp" []] @@ fun exp -> Assert { exp; check = true; refine = get_bool "sem.assert.refine" }); (* only used if assert is used without include, e.g. in transformed files *) ("difftime", unknown [drop "time1" []; drop "time2" []]); - ("system", unknown [drop "command" [r]]); + ("system", unknown ~attrs:[ThreadUnsafe] [drop "command" [r]]); ("wcscat", unknown [drop "dest" [r; w]; drop "src" [r]]); + ("wcrtomb", unknown ~attrs:[ThreadUnsafe] [drop "s" [r]; drop "wc" []; drop "ps" [w_deep]]); ("abs", unknown [drop "j" []]); ("localtime_r", unknown [drop "timep" [r]; drop "result" [w]]); ("strpbrk", unknown [drop "s" [r]; drop "accept" [r]]); @@ -101,17 +108,74 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__builtin_bzero", special [__ "dest" [w]; __ "count" []] @@ fun dest count -> Bzero { dest; count; }); ("explicit_bzero", special [__ "dest" [w]; __ "count" []] @@ fun dest count -> Bzero { dest; count; }); ("__explicit_bzero_chk", special [__ "dest" [w]; __ "count" []; drop "os" []] @@ fun dest count -> Bzero { dest; count; }); - ("nl_langinfo", unknown [drop "item" []]); + ("catgets", unknown ~attrs:[ThreadUnsafe] [drop "catalog" [r_deep]; drop "set_number" []; drop "message_number" []; drop "message" [r_deep]]); + ("crypt", unknown ~attrs:[ThreadUnsafe] [drop "key" [r]; drop "salt" [r]]); + ("ctermid", unknown ~attrs:[ThreadUnsafe] [drop "s" [r]]); + ("dbm_clearerr", unknown ~attrs:[ThreadUnsafe] [drop "db" [r_deep; w_deep]]); + ("dbm_close", unknown ~attrs:[ThreadUnsafe] [drop "db" [r_deep; w_deep; f_deep]]); + ("dbm_delete", unknown ~attrs:[ThreadUnsafe] [drop "db" [r_deep; w_deep]; drop "key" []]); + ("dbm_error", unknown ~attrs:[ThreadUnsafe] [drop "db" [r_deep]]); + ("dbm_fetch", unknown ~attrs:[ThreadUnsafe] [drop "db" [r_deep]; drop "key" []]); + ("dbm_firstkey", unknown ~attrs:[ThreadUnsafe] [drop "db" [r_deep]]); + ("dbm_nextkey", unknown ~attrs:[ThreadUnsafe] [drop "db" [r_deep]]); + ("dbm_open", unknown ~attrs:[ThreadUnsafe] [drop "file" [r_deep; w_deep]; drop "open_flags" []; drop "file_mode" []]); + ("dbm_store", unknown ~attrs:[ThreadUnsafe] [drop "db" [r_deep; w_deep]; drop "key" []; drop "content" []; drop "store_mode" []]); + ("dlerror", unknown ~attrs:[ThreadUnsafe] []); + ("drand48", unknown ~attrs:[ThreadUnsafe] []); + ("encrypt", unknown ~attrs:[ThreadUnsafe] [drop "block" []; drop "edflag" []]); + ("endgrent", unknown ~attrs:[ThreadUnsafe] []); + ("endpwent", unknown ~attrs:[ThreadUnsafe] []); + ("fcvt", unknown ~attrs:[ThreadUnsafe] [drop "number" []; drop "ndigits" []; drop "decpt" [w]; drop "sign" [w]]); + ("gcvt", unknown ~attrs:[ThreadUnsafe] [drop "number" []; drop "ndigit" []; drop "buf" [w]]); + ("getdate", unknown ~attrs:[ThreadUnsafe] [drop "string" [r]]); + ("getenv", unknown ~attrs:[ThreadUnsafe] [drop "name" [r]]); + ("getgrent", unknown ~attrs:[ThreadUnsafe] []); + ("getgrgid", unknown ~attrs:[ThreadUnsafe] [drop "gid" []]); + ("getgrnam", unknown ~attrs:[ThreadUnsafe] [drop "name" [r]]); + ("getlogin", unknown ~attrs:[ThreadUnsafe] []); + ("getnetbyaddr", unknown ~attrs:[ThreadUnsafe] [drop "net" []; drop "type" []]); + ("getnetbyname", unknown ~attrs:[ThreadUnsafe] [drop "name" [r]]); + ("getnetent", unknown ~attrs:[ThreadUnsafe] []); + ("getprotobyname", unknown ~attrs:[ThreadUnsafe] [drop "name" [r]]); + ("getprotobynumber", unknown ~attrs:[ThreadUnsafe] [drop "proto" []]); + ("getprotoent", unknown ~attrs:[ThreadUnsafe] []); + ("getpwent", unknown ~attrs:[ThreadUnsafe] []); + ("getpwnam", unknown ~attrs:[ThreadUnsafe] [drop "name" [r]]); + ("getpwuid", unknown ~attrs:[ThreadUnsafe] [drop "uid" []]); + ("getservbyname", unknown ~attrs:[ThreadUnsafe] [drop "name" [r]; drop "proto" [r]]); + ("getservbyport", unknown ~attrs:[ThreadUnsafe] [drop "port" []; drop "proto" [r]]); + ("getservent", unknown ~attrs:[ThreadUnsafe] []); + ("getutxent", unknown ~attrs:[ThreadUnsafe] []); + ("getutxid", unknown ~attrs:[ThreadUnsafe] [drop "utmpx" [r_deep]]); + ("getutxline", unknown ~attrs:[ThreadUnsafe] [drop "utmpx" [r_deep]]); + ("pututxline", unknown ~attrs:[ThreadUnsafe] [drop "utmpx" [r_deep]]); + ("hcreate", unknown ~attrs:[ThreadUnsafe] [drop "nel" []]); + ("hdestroy", unknown ~attrs:[ThreadUnsafe] []); + ("hsearch", unknown ~attrs:[ThreadUnsafe] [drop "item" [r_deep]; drop "action" [r_deep]]); + ("l64a", unknown ~attrs:[ThreadUnsafe] [drop "value" []]); + ("lrand48", unknown ~attrs:[ThreadUnsafe] []); + ("mrand48", unknown ~attrs:[ThreadUnsafe] []); + ("nl_langinfo", unknown ~attrs:[ThreadUnsafe] [drop "item" []]); ("nl_langinfo_l", unknown [drop "item" []; drop "locale" [r_deep]]); - ("getc_unlocked", unknown [drop "stream" [w]]); - ("getchar_unlocked", unknown []); - ("putc_unlocked", unknown [drop "c" []; drop "stream" [w]]); - ("putchar_unlocked", unknown [drop "c" []]); + ("getc_unlocked", unknown ~attrs:[ThreadUnsafe] [drop "stream" [r_deep; w_deep]]); + ("getchar_unlocked", unknown ~attrs:[ThreadUnsafe] []); + ("ptsname", unknown ~attrs:[ThreadUnsafe] [drop "fd" []]); + ("putc_unlocked", unknown ~attrs:[ThreadUnsafe] [drop "c" []; drop "stream" [w]]); + ("putchar_unlocked", unknown ~attrs:[ThreadUnsafe] [drop "c" []]); + ("putenv", unknown ~attrs:[ThreadUnsafe] [drop "string" [r; w]]); + ("readdir", unknown ~attrs:[ThreadUnsafe] [drop "dirp" [r_deep]]); + ("setenv", unknown ~attrs:[ThreadUnsafe] [drop "name" [r]; drop "name" [r]; drop "overwrite" []]); + ("setgrent", unknown ~attrs:[ThreadUnsafe] []); + ("setpwent", unknown ~attrs:[ThreadUnsafe] []); + ("setutxent", unknown ~attrs:[ThreadUnsafe] []); + ("strerror", unknown ~attrs:[ThreadUnsafe] [drop "errnum" []]); + ("strsignal", unknown ~attrs:[ThreadUnsafe] [drop "sig" []]); + ("unsetenv", unknown ~attrs:[ThreadUnsafe] [drop "name" [r]]); ("lseek", unknown [drop "fd" []; drop "offset" []; drop "whence" []]); ("fcntl", unknown (drop "fd" [] :: drop "cmd" [] :: VarArgs (drop' [r; w]))); ("fseeko", unknown [drop "stream" [r_deep; w_deep]; drop "offset" []; drop "whence" []]); ("fileno", unknown [drop "stream" [r_deep; w_deep]]); - ("getopt", unknown [drop "argc" []; drop "argv" [r]; drop "optstring" [r]]); + ("getopt", unknown ~attrs:[ThreadUnsafe] [drop "argc" []; drop "argv" [r]; drop "optstring" [r]]); ("iconv_open", unknown [drop "tocode" [r]; drop "fromcode" [r]]); ("iconv", unknown [drop "cd" [r]; drop "inbuf" [r]; drop "inbytesleft" [r;w]; drop "outbuf" [w]; drop "outbytesleft" [r;w]]); ("iconv_close", unknown [drop "cd" [f]]); @@ -136,15 +200,16 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("alarm", unknown [drop "seconds" []]); ("pwrite", unknown [drop "fd" []; drop "buf" [r]; drop "count" []; drop "offset" []]); ("hstrerror", unknown [drop "err" []]); - ("inet_ntoa", unknown [drop "in" []]); + ("inet_ntoa", unknown ~attrs:[ThreadUnsafe] [drop "in" []]); ("getsockopt", unknown [drop "sockfd" []; drop "level" []; drop "optname" []; drop "optval" [w]; drop "optlen" [w]]); - ("gethostbyaddr", unknown [drop "addr" [r_deep]; drop "len" []; drop "type" []]); + ("gethostbyaddr", unknown ~attrs:[ThreadUnsafe] [drop "addr" [r_deep]; drop "len" []; drop "type" []]); ("gethostbyaddr_r", unknown [drop "addr" [r_deep]; drop "len" []; drop "type" []; drop "ret" [w_deep]; drop "buf" [w]; drop "buflen" []; drop "result" [w]; drop "h_errnop" [w]]); + ("gethostbyname", unknown ~attrs:[ThreadUnsafe] [drop "name" [r]]); ("sigaction", unknown [drop "signum" []; drop "act" [r_deep; s_deep]; drop "oldact" [w_deep]]); ("tcgetattr", unknown [drop "fd" []; drop "termios_p" [w_deep]]); ("tcsetattr", unknown [drop "fd" []; drop "optional_actions" []; drop "termios_p" [r_deep]]); ("access", unknown [drop "pathname" [r]; drop "mode" []]); - ("ttyname", unknown [drop "fd" []]); + ("ttyname", unknown ~attrs:[ThreadUnsafe] [drop "fd" []]); ("shm_open", unknown [drop "name" [r]; drop "oflag" []; drop "mode" []]); ("sched_get_priority_max", unknown [drop "policy" []]); ("mprotect", unknown [drop "addr" []; drop "len" []; drop "prot" []]); @@ -164,14 +229,15 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("strtok_r", unknown [drop "str" [r; w]; drop "delim" [r]; drop "saveptr" [r_deep; w_deep]]); (* deep accesses through saveptr if str is NULL: https://github.com/lattera/glibc/blob/895ef79e04a953cac1493863bcae29ad85657ee1/string/strtok_r.c#L31-L40 *) ("kill", unknown [drop "pid" []; drop "sig" []]); ("closelog", unknown []); - ("dirname", unknown [drop "path" [r]]); + ("dirname", unknown ~attrs:[ThreadUnsafe] [drop "path" [r]]); + ("basename", unknown ~attrs:[ThreadUnsafe] [drop "path" [r]]); ("setpgid", unknown [drop "pid" []; drop "pgid" []]); ("dup2", unknown [drop "oldfd" []; drop "newfd" []]); ("pclose", unknown [drop "stream" [w; f]]); ("getcwd", unknown [drop "buf" [w]; drop "size" []]); ("inet_pton", unknown [drop "af" []; drop "src" [r]; drop "dst" [w]]); ("inet_ntop", unknown [drop "af" []; drop "src" [r]; drop "dst" [w]; drop "size" []]); - ("gethostent", unknown []); + ("gethostent", unknown ~attrs:[ThreadUnsafe] []); ("poll", unknown [drop "fds" [r]; drop "nfds" []; drop "timeout" []]); ("semget", unknown [drop "key" []; drop "nsems" []; drop "semflg" []]); ("semctl", unknown (drop "semid" [] :: drop "semnum" [] :: drop "cmd" [] :: VarArgs (drop "semun" [r_deep]))); @@ -304,6 +370,7 @@ let linux_userspace_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("prctl", unknown (drop "option" [] :: VarArgs (drop' []))); (* man page has 5 arguments, but header has varargs and real-world programs may call with <5 *) ("__ctype_tolower_loc", unknown []); ("__ctype_toupper_loc", unknown []); + ("endutxent", unknown ~attrs:[ThreadUnsafe] []); ("epoll_create", unknown [drop "size" []]); ("epoll_ctl", unknown [drop "epfd" []; drop "op" []; drop "fd" []; drop "event" [w]]); ("epoll_wait", unknown [drop "epfd" []; drop "events" [w]; drop "maxevents" []; drop "timeout" []]); @@ -498,9 +565,9 @@ let math_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("ldexp", unknown [drop "arg" []; drop "exp" []]); ("ldexpf", unknown [drop "arg" []; drop "exp" []]); ("ldexpl", unknown [drop "arg" []; drop "exp" []]); - ("lgamma", unknown [drop "x" []]); - ("lgammaf", unknown [drop "x" []]); - ("lgammal", unknown [drop "x" []]); + ("lgamma", unknown ~attrs:[ThreadUnsafe] [drop "x" []]); + ("lgammaf", unknown ~attrs:[ThreadUnsafe] [drop "x" []]); + ("lgammal", unknown ~attrs:[ThreadUnsafe] [drop "x" []]); ("log", unknown [drop "x" []]); ("logf", unknown [drop "x" []]); ("logl", unknown [drop "x" []]); @@ -898,11 +965,9 @@ let invalidate_actions = [ "dlopen", readsAll;(*safe*) "dlsym", readsAll;(*safe*) "dlclose", readsAll;(*safe*) - "dlerror", readsAll;(*safe*) "stat__extinline", writesAllButFirst 1 readsAll;(*drop 1*) "lstat__extinline", writesAllButFirst 1 readsAll;(*drop 1*) "__builtin_strchr", readsAll;(*safe*) - "strtok", readsAll;(*safe*) "getpgrp", readsAll;(*safe*) "umount2", readsAll;(*safe*) "memchr", readsAll;(*safe*) @@ -944,8 +1009,6 @@ let invalidate_actions = [ "__h_errno_location", readsAll;(*safe*) "__fxstat", readsAll;(*safe*) "getuid", readsAll;(*safe*) - "strerror", readsAll;(*safe*) - "readdir", readsAll;(*safe*) "openlog", readsAll;(*safe*) "getdtablesize", readsAll;(*safe*) "umask", readsAll;(*safe*) @@ -971,15 +1034,12 @@ let invalidate_actions = [ "syslog", readsAll; (*safe*) "strcasecmp", readsAll; (*safe*) "strchr", readsAll; (*safe*) - "getservbyname", readsAll; (*safe*) "__error", readsAll; (*safe*) "__maskrune", writesAll; (*unsafe*) "inet_addr", readsAll; (*safe*) - "gethostbyname", readsAll; (*safe*) "setsockopt", readsAll; (*safe*) "listen", readsAll; (*safe*) "getsockname", writes [1;3]; (*keep [1;3]*) - "getenv", readsAll; (*safe*) "execl", readsAll; (*safe*) "select", writes [1;5]; (*keep [1;5]*) "accept", writesAll; (*keep [1]*) @@ -988,7 +1048,6 @@ let invalidate_actions = [ "timespec_get", writes [1]; "__tolower", readsAll; (*safe*) "signal", writesAll; (*unsafe*) - "strsignal", readsAll; "popen", readsAll; (*safe*) "BF_cfb64_encrypt", writes [1;3;4;5]; (*keep [1;3;4,5]*) "BZ2_bzBuffToBuffDecompress", writes [3;4]; (*keep [3;4]*) @@ -1005,7 +1064,6 @@ let invalidate_actions = [ "sendto", writes [2;4]; (*keep [2;4]*) "recvfrom", writes [4;5]; (*keep [4;5]*) "srand", readsAll; (*safe*) - "rand", readsAll; (*safe*) "gethostname", writesAll; (*unsafe*) "fork", readsAll; (*safe*) "setrlimit", readsAll; (*safe*) From f6fd162321f7468dd371adcc5a221dfb84131e10 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Fri, 9 Jun 2023 18:26:05 +0200 Subject: [PATCH 0103/1312] debugging --- output.txt | 61 ++++++++++++++++++++-------- runningGob.sh | 4 +- src/analyses/termination_new.ml | 8 +--- src/framework/constraints.ml | 6 +-- src/util/terminationPreprocessing.ml | 2 +- 5 files changed, 52 insertions(+), 29 deletions(-) diff --git a/output.txt b/output.txt index 07c71d61b9..6ccb110e96 100644 --- a/output.txt +++ b/output.txt @@ -1,3 +1,26 @@ +2023-06-09 18:19:45 +'./goblint' '-v' 'tests/regression/55-loop-unrolling/01-simple-cases.c' '--set' 'ana.activated[+]' 'termination' '--enable' 'warn.debug' '--set' 'ana.activated[+]' 'apron' '--enable' 'ana.int.interval' '--set' 'ana.apron.domain' 'polyhedra' '--enable' 'justcil' +Custom include dirs: + 1. /home/johanna/goblint/goblint-analyzer/lib/linux/stub/include (exists=true) + 2. /home/johanna/goblint/goblint-analyzer/lib/sv-comp/stub/include (exists=false) + 3. /home/johanna/goblint/goblint-analyzer/lib/libc/stub/include (exists=true) + 4. /home/johanna/goblint/goblint-analyzer/lib/goblint/stub/include (exists=false) + 5. /home/johanna/goblint/goblint-analyzer/lib/linux/runtime/include (exists=false) + 6. /home/johanna/goblint/goblint-analyzer/lib/sv-comp/runtime/include (exists=false) + 7. /home/johanna/goblint/goblint-analyzer/lib/libc/runtime/include (exists=false) + 8. /home/johanna/goblint/goblint-analyzer/lib/goblint/runtime/include (exists=true) + 9. /home/johanna/goblint/goblint-analyzer/lib/linux/stub/src (exists=false) + 10. /home/johanna/goblint/goblint-analyzer/lib/sv-comp/stub/src (exists=true) + 11. /home/johanna/goblint/goblint-analyzer/lib/libc/stub/src (exists=true) + 12. /home/johanna/goblint/goblint-analyzer/lib/goblint/stub/src (exists=false) +Preprocessing files. +Preprocessor cpp: is_bad=false +'cpp' '-I' '/home/johanna/goblint/goblint-analyzer/lib/linux/stub/include' '-I' '/home/johanna/goblint/goblint-analyzer/lib/libc/stub/include' '-I' '/home/johanna/goblint/goblint-analyzer/lib/goblint/runtime/include' '-I' '/home/johanna/goblint/goblint-analyzer/lib/sv-comp/stub/src' '-I' '/home/johanna/goblint/goblint-analyzer/lib/libc/stub/src' '/home/johanna/goblint/goblint-analyzer/lib/libc/stub/src/pthread.c' '-o' '.goblint/preprocessed/pthread.i' +'cpp' '-I' '/home/johanna/goblint/goblint-analyzer/lib/linux/stub/include' '-I' '/home/johanna/goblint/goblint-analyzer/lib/libc/stub/include' '-I' '/home/johanna/goblint/goblint-analyzer/lib/goblint/runtime/include' '-I' '/home/johanna/goblint/goblint-analyzer/lib/sv-comp/stub/src' '-I' '/home/johanna/goblint/goblint-analyzer/lib/libc/stub/src' '/home/johanna/goblint/goblint-analyzer/lib/libc/stub/src/stdlib.c' '-o' '.goblint/preprocessed/stdlib.i' +'cpp' '-I' '/home/johanna/goblint/goblint-analyzer/lib/linux/stub/include' '-I' '/home/johanna/goblint/goblint-analyzer/lib/libc/stub/include' '-I' '/home/johanna/goblint/goblint-analyzer/lib/goblint/runtime/include' '-I' '/home/johanna/goblint/goblint-analyzer/lib/sv-comp/stub/src' '-I' '/home/johanna/goblint/goblint-analyzer/lib/libc/stub/src' 'tests/regression/55-loop-unrolling/01-simple-cases.c' '-o' '.goblint/preprocessed/01-simple-cases.i' +Parsing files. +Constructors: +Adding constructors to: main /* Generated by CIL v. 2.0.1-48-g4df989f */ /* print_CIL_Input is true */ @@ -1123,7 +1146,7 @@ void example1(void) while_break: /* CIL Label */ ; } #line 27 - term_exit- = term27_5-file_01-simple-cases; + __goblint_bounded(term27_5-file_01-simple-cases); } #line 32 __goblint_check(a[0] == 0); @@ -1165,7 +1188,7 @@ void example2(void) while_break: /* CIL Label */ ; } #line 42 - term_exit- = term42_5-file_01-simple-cases; + __goblint_bounded(term42_5-file_01-simple-cases); } #line 47 __goblint_check(a[0] == 0); @@ -1207,7 +1230,7 @@ void example3(void) while_break: /* CIL Label */ ; } #line 57 - term_exit- = term57_5-file_01-simple-cases; + __goblint_bounded(term57_5-file_01-simple-cases); } #line 62 __goblint_check(a[0] == 0); @@ -1266,7 +1289,7 @@ void example4(void) while_break: /* CIL Label */ ; } #line 74 - term_exit- = term74_5-file_01-simple-cases; + __goblint_bounded(term74_5-file_01-simple-cases); } #line 82 __goblint_check(a[0] == 0); @@ -1321,7 +1344,7 @@ void example5(void) while_break: /* CIL Label */ ; } #line 95 - term_exit- = term95_5-file_01-simple-cases; + __goblint_bounded(term95_5-file_01-simple-cases); } #line 107 __goblint_check(a[0] == 0); @@ -1370,7 +1393,7 @@ void example6(void) while_break: /* CIL Label */ ; } #line 119 - term_exit- = term119_5-file_01-simple-cases; + __goblint_bounded(term119_5-file_01-simple-cases); } #line 125 __goblint_check(a[0] == 0); @@ -1433,7 +1456,7 @@ void example7(void) while_break: /* CIL Label */ ; } #line 143 - term_exit- = term143_2-file_01-simple-cases; + __goblint_bounded(term143_2-file_01-simple-cases); } #line 147 __goblint_check(a[0] == 0); @@ -1506,7 +1529,7 @@ void example8(void) while_break___0: /* CIL Label */ ; } #line 160 - term_exit- = term160_9-file_01-simple-cases; + __goblint_bounded(term160_9-file_01-simple-cases); } #line 164 i ++; @@ -1514,7 +1537,7 @@ void example8(void) while_break: /* CIL Label */ ; } #line 157 - term_exit- = term157_2-file_01-simple-cases; + __goblint_bounded(term157_2-file_01-simple-cases); } #line 166 return; @@ -1557,7 +1580,7 @@ void example9(void) while_break: /* CIL Label */ ; } #line 174 - term_exit- = term174_2-file_01-simple-cases; + __goblint_bounded(term174_2-file_01-simple-cases); } #line 179 return; @@ -1602,7 +1625,7 @@ void example10(void) while_break: /* CIL Label */ ; } #line 187 - term_exit- = term187_2-file_01-simple-cases; + __goblint_bounded(term187_2-file_01-simple-cases); } #line 195 return; @@ -2081,7 +2104,7 @@ void qsort(void *ptr , size_t count , size_t size , int (*comp)(void const * , while_break___0: /* CIL Label */ ; } #line 10 - term_exit- = term10_5-file_stdlib; + __goblint_bounded(term10_5-file_stdlib); } #line 9 i ++; @@ -2089,7 +2112,7 @@ void qsort(void *ptr , size_t count , size_t size , int (*comp)(void const * , while_break: /* CIL Label */ ; } #line 9 - term_exit- = term9_3-file_stdlib; + __goblint_bounded(term9_3-file_stdlib); } #line 16 i___0 = (size_t )0; @@ -2157,7 +2180,7 @@ void qsort(void *ptr , size_t count , size_t size , int (*comp)(void const * , while_break___3: /* CIL Label */ ; } #line 21 - term_exit- = term21_9-file_stdlib; + __goblint_bounded(term21_9-file_stdlib); } } #line 17 @@ -2166,7 +2189,7 @@ void qsort(void *ptr , size_t count , size_t size , int (*comp)(void const * , while_break___2: /* CIL Label */ ; } #line 17 - term_exit- = term17_5-file_stdlib; + __goblint_bounded(term17_5-file_stdlib); } #line 16 i___0 ++; @@ -2174,7 +2197,7 @@ void qsort(void *ptr , size_t count , size_t size , int (*comp)(void const * , while_break___1: /* CIL Label */ ; } #line 16 - term_exit- = term16_3-file_stdlib; + __goblint_bounded(term16_3-file_stdlib); } #line 33 return; @@ -2227,9 +2250,13 @@ void *bsearch(void const *key , void const *ptr , size_t count , size_t size while_break: /* CIL Label */ ; } #line 40 - term_exit- = term40_3-file_stdlib; + __goblint_bounded(term40_3-file_stdlib); } #line 47 return ((void *)0); } } + +vars = 0 evals = 0 narrow_reuses = 0 + +Timings: diff --git a/runningGob.sh b/runningGob.sh index 52d0830b81..e9d7ab1405 100755 --- a/runningGob.sh +++ b/runningGob.sh @@ -16,8 +16,8 @@ cfile_goto="tests/incremental/02-cfg-comparison/01-added-return-stmt.c" #./goblint $cfile_loops $options_apron --html # run analysis, write cil output to file and enable visualization via html -./goblint $cfile_loops $options_term --enable justcil > output.txt -./goblint $cfile_loops $options_term --html +#./goblint -v $cfile_loops $options_term --enable justcil > output.txt +./goblint -v $cfile_loops $options_term --html # set up server to see visualizatino python3 -m http.server --directory result 8081 diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index bff776b95d..bd2057effe 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -80,10 +80,6 @@ end let () = (** Register the preprocessing *) -<<<<<<< HEAD - Cilfacade.register_preprocess_cil (Spec.name ()) (new loopCounterVisitor loopCounters upjumpingGotos loopExit); -======= - Cilfacade.register_preprocess_cil (Spec.name ()) (new loopCounterVisitor loopCounters); ->>>>>>> dfa9d6ef8 (changed loop exit indicator form global variable to a special function) - (** Register this analysis within the master control program *) +Cilfacade.register_preprocess_cil (Spec.name ()) (new loopCounterVisitor loopCounters upjumpingGotos loopExit); +(** Register this analysis within the master control program *) MCP.register_analysis (module Spec : MCPSpec) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 287726419f..0d68a1a028 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1970,14 +1970,14 @@ struct sideg (V.contexts f) (G.create_contexts (G.CMap.singleton (c) (t))) let enter ctx lval fundec exprList = (*TODO*) - S.enter (conv ctx) lval fundec exprList; - let c: unit -> S.C.t = snd var |> Obj.obj in (*Callee context*) + S.enter (conv ctx) lval fundec exprList + (*let c: unit -> S.C.t = snd var |> Obj.obj in (*Callee context*) let fd = fundec in (*Callee fundec*) let c' = ctx.context in (*Caller context*) (*TODO is this the caller or callee context???*) let fd' = in (*Caller fundec*) let tup = (fundec * c') in (* TODO: is fundec the caller or callee fundec???*) let t = G.CSet.singleton (tup) in (*TODO do we fill the set correctly???*) - side_context sideg fd (c ()) t + side_context sideg fd (c ()) t*) let paths_as_set ctx = S.paths_as_set (conv ctx) let body ctx = S.body (conv ctx) diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index 15a5c948fd..41c5b3f9d7 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -23,7 +23,7 @@ let extract_file_name s = (*There still may be a need to filt let show_location_id l = string_of_int l.line ^ "_" ^ string_of_int l.column ^ "-file" ^ "_" ^ extract_file_name l.file -class loopCounterVisitor lc (fd : fundec) = object(self) +class loopCounterVisitor lc lg le (fd : fundec) = object(self) inherit nopCilVisitor method! vstmt s = let action s = match s.skind with From 5ae6485e116db3eb00b6653a747262e821b8fc06 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Fri, 9 Jun 2023 18:48:14 +0200 Subject: [PATCH 0104/1312] Fix check_bounded --- src/analyses/termination_new.ml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index f1dde59c64..8629cac17f 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -20,21 +20,21 @@ let is_loop_exit_indicator (x : varinfo) = (* checks if at the current location (=loc) of the analysis an upjumping goto was already reached true: no upjumping goto was reached till now*) -let currrently_no_upjumping_gotos (loc : location) = +let currrently_no_upjumping_gotos (loc : location) = List.for_all (function (l) -> (l >= loc)) upjumpingGotos.contents -let no_upjumping_gotos () = +let no_upjumping_gotos () = (List.length upjumpingGotos.contents) <= 0 (** Checks whether a variable can be bounded *) let check_bounded ctx varinfo = - let open IntDomain.IntDomTuple in (* TODO: Remove *) - let open Cil in + let open IntDomain.IntDomTuple in let exp = Lval (Var varinfo, NoOffset) in match ctx.ask (EvalInt exp) with - `Top -> print_endline (varinfo.vname ^ " is TOP"); false + | `Top -> print_endline (varinfo.vname ^ " is TOP"); false | `Bot -> print_endline (varinfo.vname ^ " is BOT"); raise (PreProcessing "Loop variable is Bot") - | `Lifted v -> print_endline (varinfo.vname ^ " is " ^ IntDomain.IntDomTuple.show v); not (is_top v) (* TODO: Is this sound? *) + | `Lifted v -> print_endline (varinfo.vname ^ " is " ^ IntDomain.IntDomTuple.show v); + not (is_top_of (ikind v) v) module Spec : Analyses.MCPSpec = struct From b6e08f2d3efb4feebf4bf82f2d346666e2b9d9ee Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Fri, 9 Jun 2023 18:50:22 +0200 Subject: [PATCH 0105/1312] Remove debug output --- src/analyses/termination_new.ml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index 8629cac17f..1eac676d91 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -31,10 +31,9 @@ let check_bounded ctx varinfo = let open IntDomain.IntDomTuple in let exp = Lval (Var varinfo, NoOffset) in match ctx.ask (EvalInt exp) with - | `Top -> print_endline (varinfo.vname ^ " is TOP"); false - | `Bot -> print_endline (varinfo.vname ^ " is BOT"); raise (PreProcessing "Loop variable is Bot") - | `Lifted v -> print_endline (varinfo.vname ^ " is " ^ IntDomain.IntDomTuple.show v); - not (is_top_of (ikind v) v) + | `Top -> false + | `Lifted v -> not (is_top_of (ikind v) v) + | `Bot -> raise (PreProcessing "Loop variable is Bot") module Spec : Analyses.MCPSpec = struct From beb56834d29cdb0dd2497dfb9e3dc113b17ca8e5 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Fri, 9 Jun 2023 20:00:41 +0200 Subject: [PATCH 0106/1312] it runs! :)) but it is not possible to see the global invariant on the html output yet --- runningGob.sh | 2 +- src/framework/analyses.ml | 5 +- src/framework/constraints.ml | 189 ++++++++++++++++++++++++++++++++--- src/framework/control.ml | 1 + 4 files changed, 180 insertions(+), 17 deletions(-) diff --git a/runningGob.sh b/runningGob.sh index e9d7ab1405..848d69c341 100755 --- a/runningGob.sh +++ b/runningGob.sh @@ -17,7 +17,7 @@ cfile_goto="tests/incremental/02-cfg-comparison/01-added-return-stmt.c" # run analysis, write cil output to file and enable visualization via html #./goblint -v $cfile_loops $options_term --enable justcil > output.txt -./goblint -v $cfile_loops $options_term --html +./goblint $cfile_loops $options_term --html # set up server to see visualizatino python3 -m http.server --directory result 8081 diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 41448768a9..993aaf6421 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -166,7 +166,6 @@ struct struct include MapDomain.MapBot (C_ (C)) (CSet) let printXml f c = BatPrintf.fprintf f "%a" printXml c (* TODO *) - let printXml_ f c = BatPrintf.fprintf f "%a" CSet.printXml c (* TODO *) end include Lattice.Lift2 (G) (CMap) (Printable.DefaultNames) @@ -184,8 +183,8 @@ struct let printXml f = function | `Lifted1 x -> G.printXml f x - | `Lifted2 x -> BatPrintf.fprintf f "%a" CMap.printXml x - | x -> BatPrintf.fprintf f "%a" printXml x + | `Lifted2 x -> BatPrintf.fprintf f "%a" CMap.printXml x + | x -> BatPrintf.fprintf f "%a" printXml x let s = function (*TODO: does this work? copied from DeadBranch*) | `Bot -> G.bot () diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 0d68a1a028..1614354cfa 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1457,6 +1457,145 @@ struct let event ctx e octx = S.event (conv ctx) e (conv octx) end + +module DeadBranchLifter2 (S: Spec): Spec = +struct + include S + + let name () = "DeadBranch2 (" ^ S.name () ^ ")" + + (* Two global invariants: + 1. S.V -> S.G -- used for S + 2. node -> (exp -> flat bool) -- used for warnings *) + + module V = + struct + include Printable.Either (S.V) (Node) + let name () = "DeadBranch2" + let s x = `Left x + let node x = `Right x + let is_write_only = function + | `Left x -> S.V.is_write_only x + | `Right _ -> true + end + + module EM = + struct + include MapDomain.MapBot (Basetype.CilExp) (Basetype.Bools) + let name () = "branches2" + end + + module G = + struct + include Lattice.Lift2 (S.G) (EM) (Printable.DefaultNames) + let name () = "deadbranch2" + + let s = function + | `Bot -> S.G.bot () + | `Lifted1 x -> x + | _ -> failwith "DeadBranchLifter2.s" + let node = function + | `Bot -> EM.bot () + | `Lifted2 x -> x + | _ -> failwith "DeadBranchLifter2.node" + let create_s s = `Lifted1 s + let create_node node = `Lifted2 node + + let printXml f = function + | `Lifted1 x -> S.G.printXml f x + | `Lifted2 x -> BatPrintf.fprintf f "%a" EM.printXml x + | x -> BatPrintf.fprintf f "%a" printXml x + end + + let conv (ctx: (_, G.t, _, V.t) ctx): (_, S.G.t, _, S.V.t) ctx = + { ctx with + global = (fun v -> G.s (ctx.global (V.s v))); + sideg = (fun v g -> ctx.sideg (V.s v) (G.create_s g)); + } + + let query ctx (type a) (q: a Queries.t): a Queries.result = + match q with + | WarnGlobal g -> + let g: V.t = Obj.obj g in + begin match g with + | `Left g -> + S.query (conv ctx) (WarnGlobal (Obj.repr g)) + | `Right g -> + let em = G.node (ctx.global (V.node g)) in + EM.iter (fun exp tv -> + match tv with + | `Lifted tv -> + let loc = Node.location g in (* TODO: looking up location now doesn't work nicely with incremental *) + let cilinserted = if loc.synthetic then "(possibly inserted by CIL) " else "" in + M.warn ~loc:(Node g) ~tags:[CWE (if tv then 571 else 570)] ~category:Deadcode "condition '%a' %sis always %B" d_exp exp cilinserted tv + | `Bot when not (CilType.Exp.equal exp one) -> (* all branches dead *) + M.error ~loc:(Node g) ~category:Analyzer ~tags:[Category Unsound] "both branches over condition '%a' are dead" d_exp exp + | `Bot (* all branches dead, fine at our inserted Neg(1)-s because no Pos(1) *) + | `Top -> (* may be both true and false *) + () + ) em; + end + | InvariantGlobal g -> + let g: V.t = Obj.obj g in + begin match g with + | `Left g -> + S.query (conv ctx) (InvariantGlobal (Obj.repr g)) + | `Right g -> + Queries.Result.top q + end + | IterSysVars (vq, vf) -> + (* vars for S *) + let vf' x = vf (Obj.repr (V.s (Obj.obj x))) in + S.query (conv ctx) (IterSysVars (vq, vf')); + + (* node vars for dead branches *) + begin match vq with + | Node {node; _} -> + vf (Obj.repr (V.node node)) + | _ -> + () + end + | _ -> + S.query (conv ctx) q + + + let branch ctx = S.branch (conv ctx) + + let branch ctx exp tv = + if !AnalysisState.postsolving then ( + try + let r = branch ctx exp tv in + (* branch is live *) + ctx.sideg (V.node ctx.prev_node) (G.create_node (EM.singleton exp (`Lifted tv))); (* record expression with reached tv *) + r + with Deadcode -> + (* branch is dead *) + ctx.sideg (V.node ctx.prev_node) (G.create_node (EM.singleton exp `Bot)); (* record expression without reached tv *) + raise Deadcode + ) + else ( + ctx.sideg (V.node ctx.prev_node) (G.create_node (EM.bot ())); (* create global variable during solving, to allow postsolving leq hack to pass verify *) + branch ctx exp tv + ) + + let assign ctx = S.assign (conv ctx) + let vdecl ctx = S.vdecl (conv ctx) + let enter ctx = S.enter (conv ctx) + let paths_as_set ctx = S.paths_as_set (conv ctx) + let body ctx = S.body (conv ctx) + let return ctx = S.return (conv ctx) + let combine_env ctx = S.combine_env (conv ctx) + let combine_assign ctx = S.combine_assign (conv ctx) + let special ctx = S.special (conv ctx) + let threadenter ctx = S.threadenter (conv ctx) + let threadspawn ctx lv f args fctx = S.threadspawn (conv ctx) lv f args (conv fctx) + let sync ctx = S.sync (conv ctx) + let skip ctx = S.skip (conv ctx) + let asm ctx = S.asm (conv ctx) + let event ctx e octx = S.event (conv ctx) e (conv octx) +end + + module LongjmpLifter (S: Spec): Spec = struct include S @@ -1929,8 +2068,6 @@ end module RecursionTermLifter (S: Spec) : Spec with module D = S.D and module C = S.C - and module G = GVarGG (S.G) (S.C) - and module V = GVarF(S.V) = (*global invariant - fundec -> Map (S.C) (Set (fundec * S.C)) @@ -1939,7 +2076,11 @@ module RecursionTermLifter (S: Spec) struct include S - module V = GVarF(S.V) + module V = + struct + include GVarF(S.V) + let s = spec + end module G = GVarGG (S.G) (S.C) @@ -1957,7 +2098,26 @@ struct global = (fun v -> G.s (ctx.global (V.spec v))); sideg = (fun v g -> ctx.sideg (V.spec v) (G.create_s g)); } - let query ctx = S.query (conv ctx) + let query ctx (type a) (q: a Queries.t): a Queries.result = + match q with + | WarnGlobal g -> + let g: V.t = Obj.obj g in + begin match g with + | `Left g -> + S.query (conv ctx) (WarnGlobal (Obj.repr g)) + | `Right g -> + Queries.Result.top q + end + | InvariantGlobal g -> + let g: V.t = Obj.obj g in + begin match g with + | `Left g -> + S.query (conv ctx) (InvariantGlobal (Obj.repr g)) + | `Right g -> + Queries.Result.top q + end + | _ -> S.query (conv ctx) q + let branch ctx = S.branch (conv ctx) let assign ctx = S.assign (conv ctx) let vdecl ctx = S.vdecl (conv ctx) @@ -1969,15 +2129,18 @@ struct if !AnalysisState.postsolving then sideg (V.contexts f) (G.create_contexts (G.CMap.singleton (c) (t))) - let enter ctx lval fundec exprList = (*TODO*) - S.enter (conv ctx) lval fundec exprList - (*let c: unit -> S.C.t = snd var |> Obj.obj in (*Callee context*) - let fd = fundec in (*Callee fundec*) - let c' = ctx.context in (*Caller context*) (*TODO is this the caller or callee context???*) - let fd' = in (*Caller fundec*) - let tup = (fundec * c') in (* TODO: is fundec the caller or callee fundec???*) - let t = G.CSet.singleton (tup) in (*TODO do we fill the set correctly???*) - side_context sideg fd (c ()) t*) + let enter ctx lval fu exprList = (*TODO*) + if !AnalysisState.postsolving then + let c_r: unit -> S.C.t = ctx.context in (*Caller context*) (*TODO is this the caller or callee context???*) + let fd_r : fundec = fu in (*Caller fundec*) (*TODO: Falsch??*) + let c_e : unit -> S.C.t = ctx.context in (*Callee context*) (*TODO: Falsch??*) + let fd_e : fundec = fu in (*Callee fundec*) + let tup: (fundec * S.C.t) = (fd_r, (c_r ())) in (* TODO: is fundec the caller or callee fundec???*) + let t = G.CSet.singleton (tup) in (*TODO do we fill the set correctly???*) + side_context ctx.sideg fd_e (c_e ()) t; + S.enter (conv ctx) lval fu exprList + else + S.enter (conv ctx) lval fu exprList let paths_as_set ctx = S.paths_as_set (conv ctx) let body ctx = S.body (conv ctx) diff --git a/src/framework/control.ml b/src/framework/control.ml index bd26fa7129..0a36d6c989 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -37,6 +37,7 @@ let spec_module: (module Spec) Lazy.t = lazy ( |> lift (get_bool "ana.widen.tokens") (module WideningTokens.Lifter) |> lift true (module LongjmpLifter) |> lift true (module RecursionTermLifter)(*TODO: should we really always evaluate it???*) + |> lift (get_bool "ana.dead-code.branches") (module DeadBranchLifter2) ) in GobConfig.building_spec := false; ControlSpecC.control_spec_c := (module S1.C); From 884763609adc2e457727d089745c679a4d42a7be Mon Sep 17 00:00:00 2001 From: karoliineh Date: Fri, 9 Jun 2023 21:38:44 +0300 Subject: [PATCH 0107/1312] Add (function) Call to accessKind --- src/analyses/libraryFunctions.ml | 23 ++++++++++++----------- src/analyses/mutexAnalysis.ml | 1 + src/domains/accessKind.ml | 4 +++- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 9b689b64ad..31ed3adb65 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -797,70 +797,70 @@ struct let writesAllButFirst n f a x = match a with - | Write | Spawn -> f a x @ drop n x + | Write | Call | Spawn -> f a x @ drop n x | Read -> f a x | Free -> [] let readsAllButFirst n f a x = match a with - | Write | Spawn -> f a x + | Write | Call | Spawn -> f a x | Read -> f a x @ drop n x | Free -> [] let reads ns a x = let i, o = partition ns x in match a with - | Write | Spawn -> o + | Write | Call | Spawn -> o | Read -> i | Free -> [] let writes ns a x = let i, o = partition ns x in match a with - | Write | Spawn -> i + | Write | Call | Spawn -> i | Read -> o | Free -> [] let frees ns a x = let i, o = partition ns x in match a with - | Write | Spawn -> [] + | Write | Call | Spawn -> [] | Read -> o | Free -> i let readsFrees rs fs a x = match a with - | Write | Spawn -> [] + | Write | Call | Spawn -> [] | Read -> keep rs x | Free -> keep fs x let onlyReads ns a x = match a with - | Write | Spawn -> [] + | Write | Call | Spawn -> [] | Read -> keep ns x | Free -> [] let onlyWrites ns a x = match a with - | Write | Spawn -> keep ns x + | Write | Call | Spawn -> keep ns x | Read -> [] | Free -> [] let readsWrites rs ws a x = match a with - | Write | Spawn -> keep ws x + | Write | Call | Spawn -> keep ws x | Read -> keep rs x | Free -> [] let readsAll a x = match a with - | Write | Spawn -> [] + | Write | Call | Spawn -> [] | Read -> x | Free -> [] let writesAll a x = match a with - | Write | Spawn -> x + | Write | Call | Spawn -> x | Read -> [] | Free -> [] end @@ -1158,6 +1158,7 @@ let unknown_desc ~f name = (* TODO: remove name argument, unknown function shoul | Read when GobConfig.get_bool "sem.unknown_function.read.args" -> args | Read -> [] | Free -> [] + | Call -> [] (* TODO: option *) | Spawn when get_bool "sem.unknown_function.spawn" -> args | Spawn -> [] in diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 9029c1b4a1..6d39be150c 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -254,6 +254,7 @@ struct let write = match kind with | Write | Free -> true | Read -> false + | Call | Spawn -> false (* TODO: nonsense? *) in let s = GProtecting.make ~write ~recovered:is_recovered_to_st locks in diff --git a/src/domains/accessKind.ml b/src/domains/accessKind.ml index 576581af02..b36e8f3eca 100644 --- a/src/domains/accessKind.ml +++ b/src/domains/accessKind.ml @@ -1,9 +1,10 @@ (** Kinds of memory accesses. *) type t = - | Write (** argument may be read or written to *) + | Write (** argument may be written to *) | Read (** argument may be read *) | Free (** argument may be freed *) + | Call (** argument may be called *) | Spawn (** argument may be spawned *) [@@deriving eq, ord, hash] (** Specifies what is known about an argument. *) @@ -12,6 +13,7 @@ let show: t -> string = function | Write -> "write" | Read -> "read" | Free -> "free" + | Call -> "call" | Spawn -> "spawn" include Printable.SimpleShow ( From d58c1ca6e0182b86daf083180d986aa52315654b Mon Sep 17 00:00:00 2001 From: karoliineh Date: Fri, 9 Jun 2023 22:17:30 +0300 Subject: [PATCH 0108/1312] Add tests for thread-unsafe function call --- .../04-mutex/77-thread-unsafe_fun_rc.c | 22 +++++++++++++++++++ .../04-mutex/78-thread-unsafe_fun_nr.c | 21 ++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 tests/regression/04-mutex/77-thread-unsafe_fun_rc.c create mode 100644 tests/regression/04-mutex/78-thread-unsafe_fun_nr.c diff --git a/tests/regression/04-mutex/77-thread-unsafe_fun_rc.c b/tests/regression/04-mutex/77-thread-unsafe_fun_rc.c new file mode 100644 index 0000000000..8f2f01fc6d --- /dev/null +++ b/tests/regression/04-mutex/77-thread-unsafe_fun_rc.c @@ -0,0 +1,22 @@ +#include +#include + +pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; +pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER; + +void *t_fun(void *arg) { + pthread_mutex_lock(&mutex1); + rand(); // RACE! + pthread_mutex_unlock(&mutex1); + return NULL; +} + +int main(void) { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + pthread_mutex_lock(&mutex2); + rand(); // RACE! + pthread_mutex_unlock(&mutex2); + pthread_join (id, NULL); + return 0; +} diff --git a/tests/regression/04-mutex/78-thread-unsafe_fun_nr.c b/tests/regression/04-mutex/78-thread-unsafe_fun_nr.c new file mode 100644 index 0000000000..df02d23db9 --- /dev/null +++ b/tests/regression/04-mutex/78-thread-unsafe_fun_nr.c @@ -0,0 +1,21 @@ +#include +#include + +pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; + +void *t_fun(void *arg) { + pthread_mutex_lock(&mutex1); + rand(); // NORACE + pthread_mutex_unlock(&mutex1); + return NULL; +} + +int main(void) { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + pthread_mutex_lock(&mutex1); + rand(); // NORACE + pthread_mutex_unlock(&mutex1); + pthread_join (id, NULL); + return 0; +} From 874c1864bc2f4e4cdc14138de09c9a408c94f319 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Fri, 9 Jun 2023 22:18:38 +0300 Subject: [PATCH 0109/1312] Handle thread-unsafe function calls in raceAnalysis #723 Co-authored-by: Simmo Saan --- src/analyses/raceAnalysis.ml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index 481ccbf60b..30cf03cfa7 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -153,6 +153,19 @@ struct | _ -> ctx.local + let special ctx (lvalOpt: lval option) (f:varinfo) (arglist:exp list) : D.t = + (* perform shallow and deep invalidate according to Library descriptors *) + let desc = LibraryFunctions.find f in + if List.mem LibraryDesc.ThreadUnsafe desc.attrs then ( + let e = Lval (Var f, NoOffset) in + let conf = 110 in + let loc = Option.get !Node.current_node in + let vo = Some f in + let a = Obj.obj (ctx.ask (PartAccess (Memory {exp=e; var_opt=vo; kind=Call}))) in + side_access ctx (`Type f.vtype) (Some (f, `NoOffset)) (conf, Call, loc, e, a); + ); + ctx.local + let finalize () = let total = !safe + !unsafe + !vulnerable in if total > 0 then ( From 03085f5c16a2cbe267f6ef82764152ee3df2f725 Mon Sep 17 00:00:00 2001 From: Nathan Schmidt Date: Sun, 11 Jun 2023 21:28:24 +0200 Subject: [PATCH 0110/1312] Handle bot for MustNulls / top for MayNulls properly --- src/analyses/base.ml | 38 +-- src/cdomains/arrayDomain.ml | 245 +++++++++++++----- .../regression/73-strings/03-string_basics.c | 23 +- 3 files changed, 220 insertions(+), 86 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 0090f85b0a..4cd2f61c53 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2053,18 +2053,18 @@ struct end (* else compute value in array domain *) else - let eval_dst = eval_rv (Analyses.ask_of_ctx ctx) gs st s1 in - let eval_src = eval_rv (Analyses.ask_of_ctx ctx) gs st s2 in - match eval_dst, eval_src with - | Array array_dst, Array array_src -> - begin match lv with - | Some lv_val -> - let lv_a = eval_lv (Analyses.ask_of_ctx ctx) gs st lv_val in - let lv_typ = Cilfacade.typeOfLval lv_val in - lv_a, lv_typ, op_array array_dst array_src - | None -> s1_a, s1_typ, op_array array_dst array_src + let lv_a, lv_typ = match lv with + | Some lv_val -> eval_lv (Analyses.ask_of_ctx ctx) gs st lv_val, Cilfacade.typeOfLval lv_val + | None -> s1_a, s1_typ in + let s1_lval = mkMem ~addr:(Cil.stripCasts s1) ~off:NoOffset in + let s2_lval = mkMem ~addr:(Cil.stripCasts s2) ~off:NoOffset in + match s1_lval, s2_lval with + | (Var v_s1, _), (Var v_s2, _) -> + begin match CPA.find_opt v_s1 st.cpa, CPA.find_opt v_s2 st.cpa with + | Some (Array array_s1), Some (Array array_s2) -> lv_a, lv_typ, op_array array_s1 array_s2 + | _ -> lv_a, lv_typ, VD.top_value (unrollType lv_typ) end - | _ -> s1_a, s1_typ, VD.top_value (unrollType s1_typ) + | _ -> lv_a, lv_typ, VD.top_value (unrollType lv_typ) in let st = match desc.special args, f.vname with | Memset { dest; ch; count; }, _ -> @@ -2099,6 +2099,7 @@ struct in set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value | Strcpy { dest = dst; src; n }, _ -> + (* TODO: This doesn't work, need to convert to Address? If yes, how? *) let dest_a, dest_typ, value = string_manipulation dst src None false None (fun ar1 ar2 -> Array(CArrays.string_copy ar1 ar2 (eval_n n))) in set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value | Strcat { dest = dst; src; n }, _ -> @@ -2115,11 +2116,18 @@ struct (* if s string literal, compute strlen in string literals domain *) if AD.type_of address = charPtrType then Int(AD.to_string_length address) - (* else compute strlen in array domain *) + (* else compute strlen in array domain; TODO: is there any more elegant way than this? The following didn't work :( *) + (* let eval_dst = eval_rv (Analyses.ask_of_ctx ctx) gs st s1 in + let eval_src = eval_rv (Analyses.ask_of_ctx ctx) gs st s2 in + match eval_dst, eval_src with + | Array array_dst, Array array_src -> ... *) else - begin match eval_rv (Analyses.ask_of_ctx ctx) gs st s with - (* TODO: found out during debugging that case is not picked even when it should -- why?? *) - | Array array_s -> Int(CArrays.to_string_length array_s) + begin match lval with + | (Var v, _) -> + begin match CPA.find_opt v st.cpa with + | Some (Array array_s) -> Int(CArrays.to_string_length array_s) + | _ -> VD.top_value (unrollType dest_typ) + end | _ -> VD.top_value (unrollType dest_typ) end in set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 680ff50566..8b8e5c39e9 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1044,6 +1044,58 @@ struct (* if maximum number in interval is invalid, i.e. negative, return Top of value *) | _ -> Top + (* helper functions *) + let must_nulls_remove i must_nulls_set min_size = + let rec compute_set acc i = + if Z.geq i min_size then + acc + else + compute_set (MustNulls.add i acc) (Z.succ i) in + if MustNulls.is_bot must_nulls_set then + MustNulls.remove i (compute_set (MustNulls.empty ()) Z.zero) + else + MustNulls.remove i must_nulls_set + let must_nulls_filter cond must_nulls_set min_size = + let rec compute_set acc i = + if Z.geq i min_size then + acc + else + compute_set (MustNulls.add i acc) (Z.succ i) in + if MustNulls.is_bot must_nulls_set then + MustNulls.filter cond (compute_set (MustNulls.empty ()) Z.zero) + else + MustNulls.filter cond must_nulls_set + let must_nulls_min_elt must_nulls_set = + if MustNulls.is_bot must_nulls_set then + Z.zero + else + MustNulls.min_elt must_nulls_set + let may_nulls_remove i may_nulls_set max_size = + let rec compute_set acc i = + if Z.geq i max_size then + acc + else + compute_set (MayNulls.add i acc) (Z.succ i) in + if MayNulls.is_top may_nulls_set then + MayNulls.remove i (compute_set (MayNulls.empty ()) Z.zero) + else + MayNulls.remove i may_nulls_set + let may_nulls_filter cond may_nulls_set max_size = + let rec compute_set acc i = + if Z.geq i max_size then + acc + else + compute_set (MayNulls.add i acc) (Z.succ i) in + if MayNulls.is_top may_nulls_set then + MayNulls.filter cond (compute_set (MayNulls.empty ()) Z.zero) + else + MayNulls.filter cond may_nulls_set + let may_nulls_min_elt may_nulls_set = + if MayNulls.is_top may_nulls_set then + Z.zero + else + MayNulls.min_elt may_nulls_set + let set (ask: VDQ.t) (must_nulls_set, may_nulls_set, size) (e, i) v = let rec add_indexes i max may_nulls_set = if Z.gt i max then @@ -1067,26 +1119,26 @@ struct (MustNulls.add i must_nulls_set, MayNulls.add i may_nulls_set, size) (* ..., i < minimal size and value <> null, remove i from must_nulls_set and may_nulls_set *) else if Z.lt i min_size then - (MustNulls.remove i must_nulls_set, MayNulls.remove i may_nulls_set, size) + (must_nulls_remove i must_nulls_set min_size, MayNulls.remove i may_nulls_set, size) (* ..., i >= minimal size and value = null, add i only to may_nulls_set *) else if Val.is_null v then (must_nulls_set, MayNulls.add i may_nulls_set, size) (* ..., i >= minimal size and value <> null, remove i only from must_nulls_set *) else - (MustNulls.remove i must_nulls_set, may_nulls_set, size) + (must_nulls_remove i must_nulls_set min_size, may_nulls_set, size) | Some max_size -> (* if i < minimal size and value = null, add i to must_nulls_set and may_nulls_set *) if Z.lt i min_size && Val.is_null v then (MustNulls.add i must_nulls_set, MayNulls.add i may_nulls_set, size) (* if i < minimal size and value <> null, remove i from must_nulls_set and may_nulls_set *) else if Z.lt i min_size then - (MustNulls.remove i must_nulls_set, MayNulls.remove i may_nulls_set, size) + (must_nulls_remove i must_nulls_set min_size, may_nulls_remove i may_nulls_set max_size, size) (* if minimal size <= i < maximal size and value = null, add i only to may_nulls_set *) else if Z.lt i max_size && Val.is_null v then (must_nulls_set, MayNulls.add i may_nulls_set, size) (* if minimal size <= i < maximal size and value <> null, remove i only from must_nulls_set *) else if Z.lt i max_size then - (MustNulls.remove i must_nulls_set, may_nulls_set, size) + (must_nulls_remove i must_nulls_set min_size, may_nulls_set, size) (* if i >= maximal size, return tuple unmodified *) else (must_nulls_set, may_nulls_set, size) in @@ -1099,7 +1151,7 @@ struct else if Z.equal min_i Z.zero && Z.geq max_i min_size then MustNulls.top () else - MustNulls.filter (fun x -> (Z.lt x min_i || Z.gt x max_i) && Z.lt x min_size) must_nulls_set in + must_nulls_filter (fun x -> (Z.lt x min_i || Z.gt x max_i) && Z.lt x min_size) must_nulls_set min_size in let set_interval_may min_i max_i = (* if value <> null, return may_nulls_set unmodified as not clear which index is set to value *) @@ -1133,7 +1185,7 @@ struct | Some max_size -> (must_nulls_set, add_indexes min_i (Z.pred max_size) may_nulls_set, size) (* ... and value <> null, only keep indexes < minimal index in must_nulls_set *) else - (MustNulls.filter (Z.gt min_i) must_nulls_set, may_nulls_set, size) + (must_nulls_filter (Z.gt min_i) must_nulls_set min_size, may_nulls_set, size) | Some max_i when Z.geq max_i Z.zero -> if Z.equal min_i max_i then set_exact min_i @@ -1211,13 +1263,24 @@ struct (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "May access array past end: potential buffer overflow"; (must_nulls_set, may_nulls_set, size)) else - let min_must_null = MustNulls.min_elt must_nulls_set in + let min_must_null = must_nulls_min_elt must_nulls_set in (* if smallest index in sets coincides, only this null byte is kept in both sets *) - if Z.equal min_must_null (MayNulls.min_elt may_nulls_set) then + if Z.equal min_must_null (may_nulls_min_elt may_nulls_set) then (MustNulls.singleton min_must_null, MayNulls.singleton min_must_null, Idx.of_int ILong (Z.succ min_must_null)) (* else return empty must_nulls_set and keep every index up to smallest index of must_nulls_set included in may_nulls_set *) else - (MustNulls.empty (), MayNulls.filter (Z.geq min_must_null) may_nulls_set, Idx.of_int ILong (Z.succ min_must_null)) + match Idx.maximal size with + | Some max_size -> (MustNulls.empty (), may_nulls_filter (Z.geq min_must_null) may_nulls_set max_size, Idx.of_int ILong (Z.succ min_must_null)) + | None -> + if MayNulls.is_top may_nulls_set then + let rec add_indexes acc i = + if Z.gt i min_must_null then + acc + else + add_indexes (MayNulls.add i acc) (Z.succ i) in + (MustNulls.empty (), add_indexes (MayNulls.empty ()) Z.zero, Idx.of_int ILong (Z.succ min_must_null)) + else + (MustNulls.empty (), MayNulls.filter (Z.geq min_must_null) may_nulls_set, Idx.of_int ILong (Z.succ min_must_null)) (** [to_n_string index_set n] returns an abstract value with a potential null byte * marking the end of the string and if needed followed by further null bytes to obtain @@ -1276,12 +1339,12 @@ struct (* if only must_nulls_set empty, remove indexes >= n from may_nulls_set and add all indexes from minimal may null index to n - 1; * warn as in any case, resulting array not guaranteed to contain null byte *) else if MustNulls.is_empty must_nulls_set then - let min_may_null = MayNulls.min_elt may_nulls_set in + let min_may_null = may_nulls_min_elt may_nulls_set in warn_no_null Z.zero false min_may_null; (must_nulls_set, update_may_indexes min_may_null may_nulls_set, Idx.of_int ILong (Z.of_int n)) else - let min_must_null = MustNulls.min_elt must_nulls_set in - let min_may_null = MayNulls.min_elt may_nulls_set in + let min_must_null = must_nulls_min_elt must_nulls_set in + let min_may_null = may_nulls_min_elt may_nulls_set in (* warn if resulting array may not contain null byte *) warn_no_null min_must_null true min_may_null; (* remove indexes >= n and add all indexes from minimal must/may null to n - 1 in the sets *) @@ -1297,41 +1360,50 @@ struct (* if only must_nulls_set empty, no guarantee that null ever encountered in array => return interval [minimal may null, inf) and *) else if MustNulls.is_empty must_nulls_set then (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array might not contain a null byte: potential buffer overflow"; - Idx.starting !Cil.kindOfSizeOf (MayNulls.min_elt may_nulls_set)) + Idx.starting !Cil.kindOfSizeOf (may_nulls_min_elt may_nulls_set)) (* else return interval [minimal may null, minimal must null] *) else - Idx.of_interval !Cil.kindOfSizeOf (MayNulls.min_elt may_nulls_set, MustNulls.min_elt must_nulls_set) + Idx.of_interval !Cil.kindOfSizeOf (may_nulls_min_elt may_nulls_set, must_nulls_min_elt must_nulls_set) let string_copy (must_nulls_set1, may_nulls_set1, size1) ar2 n = (* filter out indexes before strlen(src) from dest sets and after strlen(src) from src sets and build union, keep size of dest *) - let update_sets must_nulls_set2 may_nulls_set2 min_len1 min_len2 = - match Idx.minimal size1, Idx.maximal size1, min_len1, min_len2 with + let update_sets must_nulls_set2 may_nulls_set2 size2 len2 = + match Idx.minimal size1, Idx.maximal size1, Idx.minimal len2, Idx.maximal len2 with | Some min_size1, Some max_size1, Some min_len2, Some max_len2 -> (if Z.lt max_size1 min_len2 then M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src is greater than the allocated size for dest" else if Z.lt min_size1 max_len2 then M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src may be greater than the allocated size for dest"); let must_nulls_set_result = + let min_size2 = match Idx.minimal size2 with + | Some min_size2 -> min_size2 + | None -> Z.zero in (* get must nulls from src string < minimal size of dest *) - MustNulls.filter (Z.lt min_size1) must_nulls_set2 + must_nulls_filter (Z.lt min_size1) must_nulls_set2 min_size2 (* and keep indexes of dest >= maximal strlen of src *) - |> MustNulls.union (MustNulls.filter (Z.geq max_len2) must_nulls_set1) in + |> MustNulls.union (must_nulls_filter (Z.geq max_len2) must_nulls_set1 min_size1) in let may_nulls_set_result = + let max_size2 = match Idx.maximal size2 with + | Some max_size2 -> max_size2 + | None -> max_size1 in (* get may nulls from src string < maximal size of dest *) - MayNulls.filter (Z.lt max_size1) may_nulls_set2 + may_nulls_filter (Z.lt max_size1) may_nulls_set2 max_size2 (* and keep indexes of dest >= minimal strlen of src *) - |> MayNulls.union (MayNulls.filter (Z.geq min_len2) may_nulls_set1) in + |> MayNulls.union (may_nulls_filter (Z.geq min_len2) may_nulls_set1 max_size1) in (must_nulls_set_result, may_nulls_set_result, size1) | Some min_size1, None, Some min_len2, Some max_len2 -> (if Z.lt min_size1 max_len2 then M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src may be greater than the allocated size for dest"); let must_nulls_set_result = - MustNulls.filter (Z.lt min_size1) must_nulls_set2 - |> MustNulls.union (MustNulls.filter (Z.geq max_len2) must_nulls_set1) in + let min_size2 = match Idx.minimal size2 with + | Some min_size2 -> min_size2 + | None -> Z.zero in + must_nulls_filter (Z.lt min_size1) must_nulls_set2 min_size2 + |> MustNulls.union (must_nulls_filter (Z.geq max_len2) must_nulls_set1 min_size1) in let may_nulls_set_result = (* get all may nulls from src string as no maximal size of dest *) may_nulls_set2 - |> MayNulls.union (MayNulls.filter (Z.geq min_len2) may_nulls_set1) in + |> MayNulls.union (may_nulls_filter (Z.geq min_len2) may_nulls_set1 (Z.succ min_len2)) in (must_nulls_set_result, may_nulls_set_result, size1) | Some min_size1, Some max_size1, Some min_len2, None -> (if Z.lt max_size1 min_len2 then @@ -1339,20 +1411,31 @@ struct else if Z.lt min_size1 min_len2 then M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src may be greater than the allocated size for dest"); (* do not keep any index of dest as no maximal strlen of src *) - let must_nulls_set_result = MustNulls.filter (Z.lt min_size1) must_nulls_set2 in + let must_nulls_set_result = + let min_size2 = match Idx.minimal size2 with + | Some min_size2 -> min_size2 + | None -> Z.zero in + must_nulls_filter (Z.lt min_size1) must_nulls_set2 min_size2 in let may_nulls_set_result = - MayNulls.filter (Z.lt max_size1) may_nulls_set2 - |> MayNulls.union (MayNulls.filter (Z.geq min_len2) may_nulls_set1) in + let max_size2 = match Idx.maximal size2 with + | Some max_size2 -> max_size2 + | None -> max_size1 in + may_nulls_filter (Z.lt max_size1) may_nulls_set2 max_size2 + |> MayNulls.union (may_nulls_filter (Z.geq min_len2) may_nulls_set1 max_size1) in (must_nulls_set_result, may_nulls_set_result, size1) | Some min_size1, None, Some min_len2, None -> (if Z.lt min_size1 min_len2 then M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src may be greater than the allocated size for dest"); (* do not keep any index of dest as no maximal strlen of src *) - let must_nulls_set_result = MustNulls.filter (Z.lt min_size1) must_nulls_set2 in + let must_nulls_set_result = + let min_size2 = match Idx.minimal size2 with + | Some min_size2 -> min_size2 + | None -> Z.zero in + must_nulls_filter (Z.lt min_size1) must_nulls_set2 min_size2 in let may_nulls_set_result = (* get all may nulls from src string as no maximal size of dest *) may_nulls_set2 - |> MayNulls.union (MayNulls.filter (Z.geq min_len2) may_nulls_set1) in + |> MayNulls.union (may_nulls_filter (Z.geq min_len2) may_nulls_set1 (Z.succ min_len2)) in (must_nulls_set_result, may_nulls_set_result, size1) (* any other case shouldn't happen as minimal index is always >= 0 *) | _ -> (MustNulls.top (), MayNulls.top (), size1) in @@ -1360,14 +1443,14 @@ struct match n with (* strcpy *) | None -> - let must_nulls_set2, may_nulls_set2, _ = to_string ar2 in + let must_nulls_set2, may_nulls_set2, size2 = to_string ar2 in let strlen2 = to_string_length ar2 in - update_sets must_nulls_set2 may_nulls_set2 (Idx.minimal strlen2) (Idx.maximal strlen2) + update_sets must_nulls_set2 may_nulls_set2 size2 strlen2 (* strncpy = exactly n bytes from src are copied to dest *) | Some n when n >= 0 -> - let must_nulls_set2, may_nulls_set2, _ = to_n_string ar2 n in - update_sets must_nulls_set2 may_nulls_set2 (Some (Z.of_int n)) (Some (Z.of_int n)) - | _ -> (MustNulls.top (), MayNulls.top(), size1) + let must_nulls_set2, may_nulls_set2, size2 = to_n_string ar2 n in + update_sets must_nulls_set2 may_nulls_set2 size2 (Idx.of_int !Cil.kindOfSizeOf (Z.of_int n)) + | _ -> (MustNulls.top (), MayNulls.top (), size1) let string_concat (must_nulls_set1, may_nulls_set1, size1) (must_nulls_set2, may_nulls_set2, size2) n = let update_sets min_size1 max_size1 max_size1_exists minlen1 maxlen1 maxlen1_exists minlen2 maxlen2 maxlen2_exists must_nulls_set2' may_nulls_set2' = @@ -1386,41 +1469,68 @@ struct * and keep indexes > minimal strlen(dest) + strlen(src) of may_nulls_set *) if MustNulls.is_empty must_nulls_set1 || MustNulls.is_empty must_nulls_set2' then let may_nulls_set_result = - MayNulls.filter (fun x -> if maxlen1_exists && maxlen2_exists then Z.leq x (Z.add maxlen1 maxlen2) else true) may_nulls_set1 - |> MayNulls.elements - |> BatList.cartesian_product (MayNulls.elements may_nulls_set2') - |> List.map (fun (i1, i2) -> Z.add i1 i2) - |> MayNulls.of_list - |> MayNulls.union (MayNulls.filter (Z.lt (Z.add minlen1 minlen2)) may_nulls_set1) - |> MayNulls.filter (fun x -> if max_size1_exists then Z.gt max_size1 x else true) in + if max_size1_exists then + may_nulls_filter (fun x -> if maxlen1_exists && maxlen2_exists then Z.leq x (Z.add maxlen1 maxlen2) else true) may_nulls_set1 max_size1 + |> MayNulls.elements + (* if may_nulls_set2' is top, limit it to max_size1 *) + |> BatList.cartesian_product (MayNulls.elements (may_nulls_filter (fun x -> true) may_nulls_set2' max_size1)) + |> List.map (fun (i1, i2) -> Z.add i1 i2) + |> MayNulls.of_list + |> MayNulls.union (may_nulls_filter (Z.lt (Z.add minlen1 minlen2)) may_nulls_set1 max_size1) + |> MayNulls.filter (Z.gt max_size1) + else if not (MayNulls.is_top may_nulls_set1) && not (MayNulls.is_top may_nulls_set2') && maxlen1_exists && maxlen2_exists then + MayNulls.filter (fun x -> if maxlen1_exists && maxlen2_exists then Z.leq x (Z.add maxlen1 maxlen2) else true) may_nulls_set1 + |> MayNulls.elements + |> BatList.cartesian_product (MayNulls.elements may_nulls_set2') + |> List.map (fun (i1, i2) -> Z.add i1 i2) + |> MayNulls.of_list + |> MayNulls.union (MayNulls.filter (Z.lt (Z.add minlen1 minlen2)) may_nulls_set1) + else + MayNulls.top () in (MustNulls.top (), may_nulls_set_result, size1) (* if minimal must null = minimal may null in ar1 and ar2, add them together and keep indexes > strlen(dest) + strlen(src) of ar1 *) - else if Z.equal (MustNulls.min_elt must_nulls_set1) (MayNulls.min_elt may_nulls_set1) && Z.equal (MustNulls.min_elt must_nulls_set2') (MayNulls.min_elt may_nulls_set2') then - let min_i1 = MustNulls.min_elt must_nulls_set1 in - let min_i2 = MustNulls.min_elt must_nulls_set2' in + else if Z.equal (must_nulls_min_elt must_nulls_set1) (may_nulls_min_elt may_nulls_set1) && Z.equal (must_nulls_min_elt must_nulls_set2') (may_nulls_min_elt may_nulls_set2') then + let min_i1 = must_nulls_min_elt must_nulls_set1 in + let min_i2 = must_nulls_min_elt must_nulls_set2' in let min_i = Z.add min_i1 min_i2 in let must_nulls_set_result = - MustNulls.filter (Z.lt min_i) must_nulls_set1 + must_nulls_filter (Z.lt min_i) must_nulls_set1 min_size1 |> MustNulls.add min_i |> MustNulls.filter (Z.gt min_size1) in let may_nulls_set_result = - MayNulls.filter (Z.lt min_i) may_nulls_set1 - |> MayNulls.add min_i - |> MayNulls.filter (fun x -> if max_size1_exists then Z.gt max_size1 x else true) in + if max_size1_exists then + may_nulls_filter (Z.lt min_i) may_nulls_set1 max_size1 + |> MayNulls.add min_i + |> MayNulls.filter (fun x -> if max_size1_exists then Z.gt max_size1 x else true) + else + MayNulls.top () in (must_nulls_set_result, may_nulls_set_result, size1) (* else only add all may nulls together <= strlen(dest) + strlen(src) *) else - let min_i2 = MustNulls.min_elt must_nulls_set2' in - let may_nulls_set2'_until_min_i2 = MayNulls.filter (Z.geq min_i2) may_nulls_set2' in - let must_nulls_set_result = MustNulls.filter (fun x -> if maxlen1_exists && maxlen2_exists then Z.lt (Z.add maxlen1 maxlen2) x else false) must_nulls_set1 in + let min_i2 = must_nulls_min_elt must_nulls_set2' in + let may_nulls_set2'_until_min_i2 = + match Idx.maximal size2 with + | Some max_size2 -> may_nulls_filter (Z.geq min_i2) may_nulls_set2' max_size2 + | None -> may_nulls_filter (Z.geq min_i2) may_nulls_set2' (Z.succ min_i2) in + let must_nulls_set_result = must_nulls_filter (fun x -> if maxlen1_exists && maxlen2_exists then Z.lt (Z.add maxlen1 maxlen2) x else false) must_nulls_set1 min_size1 in let may_nulls_set_result = - MayNulls.filter (fun x -> if maxlen1_exists && maxlen2_exists then Z.leq x (Z.add maxlen1 maxlen2) else true) may_nulls_set1 - |> MayNulls.elements - |> BatList.cartesian_product (MayNulls.elements may_nulls_set2'_until_min_i2) - |> List.map (fun (i1, i2) -> Z.add i1 i2) - |> MayNulls.of_list - |> MayNulls.union (MayNulls.filter (Z.lt (Z.add minlen1 minlen2)) may_nulls_set1) - |> MayNulls.filter (fun x -> if max_size1_exists then Z.gt max_size1 x else true) in + if max_size1_exists then + may_nulls_filter (fun x -> if maxlen1_exists && maxlen2_exists then Z.leq x (Z.add maxlen1 maxlen2) else true) may_nulls_set1 max_size1 + |> MayNulls.elements + |> BatList.cartesian_product (MayNulls.elements may_nulls_set2'_until_min_i2) + |> List.map (fun (i1, i2) -> Z.add i1 i2) + |> MayNulls.of_list + |> MayNulls.union (may_nulls_filter (Z.lt (Z.add minlen1 minlen2)) may_nulls_set1 max_size1) + |> MayNulls.filter (fun x -> if max_size1_exists then Z.gt max_size1 x else true) + else if not (MayNulls.is_top may_nulls_set1) then + MayNulls.filter (fun x -> if maxlen1_exists && maxlen2_exists then Z.leq x (Z.add maxlen1 maxlen2) else true) may_nulls_set1 + |> MayNulls.elements + |> BatList.cartesian_product (MayNulls.elements may_nulls_set2'_until_min_i2) + |> List.map (fun (i1, i2) -> Z.add i1 i2) + |> MayNulls.of_list + |> MayNulls.union (MayNulls.filter (Z.lt (Z.add minlen1 minlen2)) may_nulls_set1) + else + MayNulls.top () in (must_nulls_set_result, may_nulls_set_result, size1) in let compute_concat must_nulls_set2' may_nulls_set2' = @@ -1454,13 +1564,22 @@ struct | Some n when n >= 0 -> (* take at most n bytes from src; if no null byte among them, add null byte at index n *) let must_nulls_set2', may_nulls_set2' = - let must_nulls_set2, may_nulls_set2, _ = to_string (must_nulls_set2, may_nulls_set2, size2) in + let must_nulls_set2, may_nulls_set2, size2 = to_string (must_nulls_set2, may_nulls_set2, size2) in if not (MayNulls.exists (Z.gt (Z.of_int n)) may_nulls_set2) then (MustNulls.singleton (Z.of_int n), MayNulls.singleton (Z.of_int n)) else if not (MustNulls.exists (Z.gt (Z.of_int n)) must_nulls_set2) then - (MustNulls.empty (), MayNulls.add (Z.of_int n) (MayNulls.filter (Z.geq (Z.of_int n)) may_nulls_set2)) + let max_size2 = match Idx.maximal size2 with + | Some max_size2 -> max_size2 + | None -> Z.succ (Z.of_int n) in + (MustNulls.empty (), MayNulls.add (Z.of_int n) (may_nulls_filter (Z.geq (Z.of_int n)) may_nulls_set2 max_size2)) else - (MustNulls.filter (Z.gt (Z.of_int n)) must_nulls_set2, MayNulls.filter (Z.gt (Z.of_int n)) may_nulls_set2) in + let min_size2 = match Idx.minimal size2 with + | Some min_size2 -> min_size2 + | None -> Z.zero in + let max_size2 = match Idx.maximal size2 with + | Some max_size2 -> max_size2 + | None -> Z.of_int n in + (must_nulls_filter (Z.gt (Z.of_int n)) must_nulls_set2 min_size2, may_nulls_filter (Z.gt (Z.of_int n)) may_nulls_set2 max_size2) in compute_concat must_nulls_set2' may_nulls_set2' | _ -> (MustNulls.top (), MayNulls.top (), size1) @@ -1494,9 +1613,9 @@ struct Idx.starting IInt Z.one else (* if first null bytes are certain, have different indexes and are before index n if n present, return integer <> 0 *) - (try if Z.equal (MustNulls.min_elt must_nulls_set1) (MayNulls.min_elt may_nulls_set1) && (not n_exists || Z.lt (MustNulls.min_elt must_nulls_set1) n) - && Z.equal (MustNulls.min_elt must_nulls_set2) (MayNulls.min_elt may_nulls_set2) && (not n_exists || Z.lt (MustNulls.min_elt must_nulls_set2) n) - && not (Z.equal (MustNulls.min_elt must_nulls_set1) (MustNulls.min_elt must_nulls_set2)) then + (try if Z.equal (must_nulls_min_elt must_nulls_set1) (may_nulls_min_elt may_nulls_set1) && (not n_exists || Z.lt (must_nulls_min_elt must_nulls_set1) n) + && Z.equal (must_nulls_min_elt must_nulls_set2) (may_nulls_min_elt may_nulls_set2) && (not n_exists || Z.lt (must_nulls_min_elt must_nulls_set2) n) + && not (Z.equal (must_nulls_min_elt must_nulls_set1) (must_nulls_min_elt must_nulls_set2)) then Idx.of_excl_list IInt [Z.zero] else Idx.top_of IInt diff --git a/tests/regression/73-strings/03-string_basics.c b/tests/regression/73-strings/03-string_basics.c index 88bbe58796..38eec582d6 100644 --- a/tests/regression/73-strings/03-string_basics.c +++ b/tests/regression/73-strings/03-string_basics.c @@ -13,29 +13,36 @@ void concat_1(char* s, int i) { } int main() { - char* s1 = malloc(40); - strcpy(s1, "hello "); + char s1[40] = "hello "; char s2[] = "world!"; char s3[10] = "abcd"; char s4[20] = "abcdf"; + char* s5 = malloc(40); + strcpy(s5, "hello"); size_t len = strlen(s1); - __goblint_check(len == 6); // UNKNOWN + __goblint_check(len == 6); len = strlen(s2); - __goblint_check(len == 6); // UNKNOWN + __goblint_check(len == 6); len = strlen(s3); - __goblint_check(len == 4); // UNKNOWN + __goblint_check(len == 4); + + len = strlen(s5); + __goblint_check(len == 5); // UNKNOWN strcat(s1, s2); + len = strlen(s1); int i = strcmp(s1, "hello world!"); + __goblint_check(len == 12); __goblint_check(i == 0); // UNKNOWN - strcpy(s1, "hi "); - strncpy(s1, s3, 3); + char tmp[] = "hi "; + strcpy(s1, tmp); + /* strncpy(s1, s3, 3); */ len = strlen(s1); - __goblint_check(len == 3); // UNKNOWN + __goblint_check(len == 3); // UNKNOWN <----- wrong result: calculates 6 instead of 3 probably caused by wrong integration in base strcat(s1, "ababcd"); char* cmp = strstr(s1, "bab"); From d57ac9e014395639dda49f2f99de3a0110197a23 Mon Sep 17 00:00:00 2001 From: Nathan Schmidt Date: Mon, 12 Jun 2023 23:46:11 +0200 Subject: [PATCH 0111/1312] Fixed usage of domain in base and minor fixes in logic - Null Byte domain can now be called for all wished functions in base and values are correctly updated - Base now sets dest to top if string functions receive an array as dest and a string literal as src - Added function setting whole array content to top but still memorizing type and size - Fixed inverted comparisons in string_copy - Fixed wrong claim in string_comparison --- src/analyses/base.ml | 47 +++++--- src/cdomains/arrayDomain.ml | 102 +++++++++++------- src/cdomains/arrayDomain.mli | 3 + .../regression/73-strings/03-string_basics.c | 23 +++- 4 files changed, 118 insertions(+), 57 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 4cd2f61c53..abd266f08d 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2041,15 +2041,15 @@ struct let lv_a = eval_lv (Analyses.ask_of_ctx ctx) gs st lv_val in let lv_typ = Cilfacade.typeOfLval lv_val in if all && typeSig s1_typ = typeSig s2_typ && typeSig s2_typ = typeSig lv_typ then (* all types need to coincide *) - lv_a, lv_typ, (f s1_a s2_a) + lv_a, lv_typ, (f s1_a s2_a), None else if not all && typeSig s1_typ = typeSig s2_typ then (* only the types of s1 and s2 need to coincide *) - lv_a, lv_typ, (f s1_a s2_a) + lv_a, lv_typ, (f s1_a s2_a), None else - lv_a, lv_typ, (VD.top_value (unrollType lv_typ)) + lv_a, lv_typ, (VD.top_value (unrollType lv_typ)), None | _ -> (* check if s1 is potentially a string literal as writing to it would be undefined behavior; then return top *) let _ = AD.string_writing_defined s1_a in - s1_a, s1_typ, VD.top_value (unrollType s1_typ) + s1_a, s1_typ, VD.top_value (unrollType s1_typ), None end (* else compute value in array domain *) else @@ -2061,10 +2061,15 @@ struct match s1_lval, s2_lval with | (Var v_s1, _), (Var v_s2, _) -> begin match CPA.find_opt v_s1 st.cpa, CPA.find_opt v_s2 st.cpa with - | Some (Array array_s1), Some (Array array_s2) -> lv_a, lv_typ, op_array array_s1 array_s2 - | _ -> lv_a, lv_typ, VD.top_value (unrollType lv_typ) + | Some (Array array_s1), Some (Array array_s2) -> lv_a, lv_typ, op_array array_s1 array_s2, Some v_s1 + | _ -> lv_a, lv_typ, VD.top_value (unrollType lv_typ), None end - | _ -> lv_a, lv_typ, VD.top_value (unrollType lv_typ) + | (Var v_s1, _), _ -> + begin match CPA.find_opt v_s1 st.cpa with + | Some (Array array_s1) -> lv_a, lv_typ, Array(CArrays.content_to_top array_s1), Some v_s1 + | _ -> lv_a, lv_typ, VD.top_value (unrollType lv_typ), Some v_s1 + end + | _ -> lv_a, lv_typ, VD.top_value (unrollType lv_typ), None in let st = match desc.special args, f.vname with | Memset { dest; ch; count; }, _ -> @@ -2099,12 +2104,17 @@ struct in set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value | Strcpy { dest = dst; src; n }, _ -> - (* TODO: This doesn't work, need to convert to Address? If yes, how? *) - let dest_a, dest_typ, value = string_manipulation dst src None false None (fun ar1 ar2 -> Array(CArrays.string_copy ar1 ar2 (eval_n n))) in - set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value + let dest_a, dest_typ, value, var = string_manipulation dst src None false None (fun ar1 ar2 -> Array(CArrays.string_copy ar1 ar2 (eval_n n))) in + begin match var with + | Some v -> {st with cpa = CPA.add v value st.cpa} + | None -> set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value + end | Strcat { dest = dst; src; n }, _ -> - let dest_a, dest_typ, value = string_manipulation dst src None false None (fun ar1 ar2 -> Array(CArrays.string_concat ar1 ar2 (eval_n n))) in - set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value + let dest_a, dest_typ, value, var = string_manipulation dst src None false None (fun ar1 ar2 -> Array(CArrays.string_concat ar1 ar2 (eval_n n))) in + begin match var with + | Some v -> {st with cpa = CPA.add v value st.cpa} + | None -> set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value + end | Strlen s, _ -> begin match lv with | Some lv_val -> @@ -2139,18 +2149,25 @@ struct (* when haystack, needle and dest type coincide, check if needle is a substring of haystack: if that is the case, assign the substring of haystack starting at the first occurrence of needle to dest, else use top *) - let dest_a, dest_typ, value = string_manipulation haystack needle lv true (Some (fun h_a n_a -> Address(AD.substring_extraction h_a n_a))) + let dest_a, dest_typ, value, var = string_manipulation haystack needle lv true (Some (fun h_a n_a -> Address(AD.substring_extraction h_a n_a))) (fun h_ar n_ar -> match CArrays.substring_extraction h_ar n_ar with | Some ar -> Array(ar) | None -> Address(AD.null_ptr)) in - set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value + begin match var with + | Some v -> + begin match value with + | Address _ -> set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value + | _ -> {st with cpa = CPA.add v value st.cpa} + end + | None -> set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value + end | None -> st end | Strcmp { s1; s2; n }, _ -> begin match lv with | Some _ -> (* when s1 and s2 type coincide, compare both both strings completely or their first n characters, otherwise use top *) - let dest_a, dest_typ, value = string_manipulation s1 s2 lv false (Some (fun s1_a s2_a -> Int(AD.string_comparison s1_a s2_a (eval_n n)))) + let dest_a, dest_typ, value, _ = string_manipulation s1 s2 lv false (Some (fun s1_a s2_a -> Int(AD.string_comparison s1_a s2_a (eval_n n)))) (fun s1_ar s2_ar -> Int(CArrays.string_comparison s1_ar s2_ar (eval_n n))) in set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value | None -> st diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 8b8e5c39e9..dc25e52db4 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -53,6 +53,7 @@ sig val get_vars_in_e: t -> Cil.varinfo list val map: (value -> value) -> t -> t val fold_left: ('a -> value -> 'a) -> 'a -> t -> 'a + val content_to_top: t -> t val smart_join: (exp -> BI.t option) -> (exp -> BI.t option) -> t -> t -> t val smart_widen: (exp -> BI.t option) -> (exp -> BI.t option) -> t -> t -> t val smart_leq: (exp -> BI.t option) -> (exp -> BI.t option) -> t -> t -> bool @@ -140,6 +141,8 @@ struct let map f x = f x let fold_left f a x = f a x + let content_to_top _ = Val.top () + let printXml f x = BatPrintf.fprintf f "\n\nAny\n%a\n\n\n" Val.printXml x let smart_join _ _ = join let smart_widen _ _ = widen @@ -248,6 +251,7 @@ struct let get_vars_in_e _ = [] let map f (xl, xr) = ((List.map f xl), f xr) let fold_left f a x = f a (join_of_all_parts x) + let content_to_top x = (Base.top (), Val.top ()) let printXml f (xl,xr) = BatPrintf.fprintf f "\n\n unrolled array\n xl\n%a\n\n @@ -340,6 +344,7 @@ struct let is_top = function | Joint x -> Val.is_top x | _-> false + let content_to_top _ = top () let join (x:t) (y:t) = normalize @@ match x, y with @@ -860,6 +865,8 @@ struct let fold_left f a (x, l) = Base.fold_left f a x let get_vars_in_e _ = [] + let content_to_top (x, l) = (Base.content_to_top x, l) + let smart_join _ _ = join let smart_widen _ _ = widen let smart_leq _ _ = leq @@ -907,6 +914,8 @@ struct let fold_left f a (x, l) = Base.fold_left f a x let get_vars_in_e (x, _) = Base.get_vars_in_e x + let content_to_top (x, l) = (Base.content_to_top x, l) + let smart_join x_eval_int y_eval_int (x,xl) (y,yl) = let l = Idx.join xl yl in (Base.smart_join_with_length (Some l) x_eval_int y_eval_int x y , l) @@ -959,6 +968,8 @@ struct let fold_left f a (x, l) = Base.fold_left f a x let get_vars_in_e _ = [] + let content_to_top (x, l) = (Base.content_to_top x, l) + let smart_join _ _ = join let smart_widen _ _ = widen let smart_leq _ _ = leq @@ -995,6 +1006,11 @@ struct type ret = Null | NotNull | Top + (* helper: returns Idx.maximal except for Overflows that are mapped to None *) + let idx_maximal i = match Idx.maximal i with + | Some i -> (try Some (Z.of_int (Z.to_int i)) with Z.Overflow -> None) + | None -> None + let get ?(checkBounds=true) (ask: VDQ.t) (must_nulls_set, may_nulls_set, size) (e, i) = let rec all_indexes_must_null i max = if Z.gt i max then @@ -1008,12 +1024,12 @@ struct | _ -> Z.zero in (* assume worst case minimal natural number *) let min_i = min i in - let max_i = Idx.maximal i in + let max_i = idx_maximal i in let min_size = min size in (* warn if index is (potentially) out of bounds *) if checkBounds then (array_oob_check (module Idx) ((must_nulls_set, may_nulls_set), size) (e, i)); - match max_i, Idx.maximal size with + match max_i, idx_maximal size with (* if there is no maximum value in index interval *) | None, _ -> (* ... return NotNull if no i >= min_i in may_nulls_set *) @@ -1108,10 +1124,10 @@ struct let min_size = min size in let min_i = min i in - let max_i = Idx.maximal i in + let max_i = idx_maximal i in let set_exact i = - match Idx.maximal size with + match idx_maximal size with (* if size has no upper limit *) | None -> (* ..., i < minimal size and value = null, add i to must_nulls_set and may_nulls_set *) @@ -1159,7 +1175,7 @@ struct may_nulls_set (* if value = null *) else - match Idx.maximal size with + match idx_maximal size with (* ... and size has no upper limit, add all indexes of interval to may_nulls_set *) | None -> add_indexes min_i max_i may_nulls_set | Some max_size -> @@ -1177,8 +1193,8 @@ struct (* if no maximum number in index interval *) | None -> (* ..., value = null*) - if Val.is_null v && Idx.maximal size = None then - match Idx.maximal size with + if Val.is_null v && idx_maximal size = None then + match idx_maximal size with (* ... and there is no maximal size, modify may_nulls_set to top *) | None -> (must_nulls_set, MayNulls.top (), size) (* ..., add all i from minimal index to maximal size to may_nulls_set *) @@ -1195,7 +1211,7 @@ struct | _ -> (must_nulls_set, may_nulls_set, size) let make ?(varAttr=[]) ?(typAttr=[]) i v = - let min_i, max_i = match Idx.minimal i, Idx.maximal i with + let min_i, max_i = match Idx.minimal i, idx_maximal i with | Some min_i, Some max_i -> if Z.lt min_i Z.zero && Z.lt max_i Z.zero then (M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.before_start "Tries to create an array of negative size"; @@ -1245,6 +1261,8 @@ struct (MustNulls.top (), MayNulls.top (), size) let fold_left f acc _ = f acc (Val.top ()) + + let content_to_top (_, _, size) = (MustNulls.top (), MayNulls.top (), size) let smart_join _ _ = join let smart_widen _ _ = widen @@ -1269,7 +1287,7 @@ struct (MustNulls.singleton min_must_null, MayNulls.singleton min_must_null, Idx.of_int ILong (Z.succ min_must_null)) (* else return empty must_nulls_set and keep every index up to smallest index of must_nulls_set included in may_nulls_set *) else - match Idx.maximal size with + match idx_maximal size with | Some max_size -> (MustNulls.empty (), may_nulls_filter (Z.geq min_must_null) may_nulls_set max_size, Idx.of_int ILong (Z.succ min_must_null)) | None -> if MayNulls.is_top may_nulls_set then @@ -1307,14 +1325,14 @@ struct |> MayNulls.filter (Z.gt (Z.of_int n)) in let warn_no_null min_must_null exists_min_must_null min_may_null = if Z.geq min_may_null (Z.of_int n) then - M.error "Resulting string might not be null-terminated because src doesn't contain a null byte in the first n bytes" + M.warn "Resulting string might not be null-terminated because src doesn't contain a null byte in the first n bytes" else if (exists_min_must_null && Z.geq min_must_null (Z.of_int n)) || not exists_min_must_null then M.warn "Resulting string might not be null-terminated because src might not contain a null byte in the first n bytes" in if n < 0 then (MustNulls.top (), MayNulls.top (), Idx.top_of ILong) else - ((match Idx.minimal size, Idx.maximal size with + ((match Idx.minimal size, idx_maximal size with | Some min_size, Some max_size -> if Z.gt (Z.of_int n) max_size then M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array size is smaller than n bytes; can cause a buffer overflow" @@ -1330,9 +1348,9 @@ struct (* if definitely no null byte in array, i.e. must_nulls_set = may_nulls_set = empty set *) if MustNulls.is_empty must_nulls_set && MayNulls.is_empty may_nulls_set then - (M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end + (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Resulting string might not be null-terminated because src doesn't contain a null byte"; - match Idx.maximal size with + match idx_maximal size with (* ... there *may* be null bytes from maximal size to n - 1 if maximal size < n (i.e. past end) *) | Some max_size when Z.geq max_size Z.zero -> (must_nulls_set, add_indexes max_size (Z.of_int n) may_nulls_set, Idx.of_int ILong (Z.of_int n)) | _ -> (must_nulls_set, may_nulls_set, Idx.of_int ILong (Z.of_int n))) @@ -1368,7 +1386,7 @@ struct let string_copy (must_nulls_set1, may_nulls_set1, size1) ar2 n = (* filter out indexes before strlen(src) from dest sets and after strlen(src) from src sets and build union, keep size of dest *) let update_sets must_nulls_set2 may_nulls_set2 size2 len2 = - match Idx.minimal size1, Idx.maximal size1, Idx.minimal len2, Idx.maximal len2 with + match Idx.minimal size1, idx_maximal size1, Idx.minimal len2, idx_maximal len2 with | Some min_size1, Some max_size1, Some min_len2, Some max_len2 -> (if Z.lt max_size1 min_len2 then M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src is greater than the allocated size for dest" @@ -1379,17 +1397,17 @@ struct | Some min_size2 -> min_size2 | None -> Z.zero in (* get must nulls from src string < minimal size of dest *) - must_nulls_filter (Z.lt min_size1) must_nulls_set2 min_size2 + must_nulls_filter (Z.gt min_size1) must_nulls_set2 min_size2 (* and keep indexes of dest >= maximal strlen of src *) - |> MustNulls.union (must_nulls_filter (Z.geq max_len2) must_nulls_set1 min_size1) in + |> MustNulls.union (must_nulls_filter (Z.leq max_len2) must_nulls_set1 min_size1) in let may_nulls_set_result = - let max_size2 = match Idx.maximal size2 with + let max_size2 = match idx_maximal size2 with | Some max_size2 -> max_size2 | None -> max_size1 in (* get may nulls from src string < maximal size of dest *) - may_nulls_filter (Z.lt max_size1) may_nulls_set2 max_size2 + may_nulls_filter (Z.gt max_size1) may_nulls_set2 max_size2 (* and keep indexes of dest >= minimal strlen of src *) - |> MayNulls.union (may_nulls_filter (Z.geq min_len2) may_nulls_set1 max_size1) in + |> MayNulls.union (may_nulls_filter (Z.leq min_len2) may_nulls_set1 max_size1) in (must_nulls_set_result, may_nulls_set_result, size1) | Some min_size1, None, Some min_len2, Some max_len2 -> (if Z.lt min_size1 max_len2 then @@ -1398,12 +1416,12 @@ struct let min_size2 = match Idx.minimal size2 with | Some min_size2 -> min_size2 | None -> Z.zero in - must_nulls_filter (Z.lt min_size1) must_nulls_set2 min_size2 - |> MustNulls.union (must_nulls_filter (Z.geq max_len2) must_nulls_set1 min_size1) in + must_nulls_filter (Z.gt min_size1) must_nulls_set2 min_size2 + |> MustNulls.union (must_nulls_filter (Z.leq max_len2) must_nulls_set1 min_size1) in let may_nulls_set_result = (* get all may nulls from src string as no maximal size of dest *) may_nulls_set2 - |> MayNulls.union (may_nulls_filter (Z.geq min_len2) may_nulls_set1 (Z.succ min_len2)) in + |> MayNulls.union (may_nulls_filter (Z.leq min_len2) may_nulls_set1 (Z.succ min_len2)) in (must_nulls_set_result, may_nulls_set_result, size1) | Some min_size1, Some max_size1, Some min_len2, None -> (if Z.lt max_size1 min_len2 then @@ -1415,13 +1433,13 @@ struct let min_size2 = match Idx.minimal size2 with | Some min_size2 -> min_size2 | None -> Z.zero in - must_nulls_filter (Z.lt min_size1) must_nulls_set2 min_size2 in + must_nulls_filter (Z.gt min_size1) must_nulls_set2 min_size2 in let may_nulls_set_result = - let max_size2 = match Idx.maximal size2 with + let max_size2 = match idx_maximal size2 with | Some max_size2 -> max_size2 | None -> max_size1 in - may_nulls_filter (Z.lt max_size1) may_nulls_set2 max_size2 - |> MayNulls.union (may_nulls_filter (Z.geq min_len2) may_nulls_set1 max_size1) in + may_nulls_filter (Z.gt max_size1) may_nulls_set2 max_size2 + |> MayNulls.union (may_nulls_filter (Z.leq min_len2) may_nulls_set1 max_size1) in (must_nulls_set_result, may_nulls_set_result, size1) | Some min_size1, None, Some min_len2, None -> (if Z.lt min_size1 min_len2 then @@ -1431,11 +1449,11 @@ struct let min_size2 = match Idx.minimal size2 with | Some min_size2 -> min_size2 | None -> Z.zero in - must_nulls_filter (Z.lt min_size1) must_nulls_set2 min_size2 in + must_nulls_filter (Z.gt min_size1) must_nulls_set2 min_size2 in let may_nulls_set_result = (* get all may nulls from src string as no maximal size of dest *) may_nulls_set2 - |> MayNulls.union (may_nulls_filter (Z.geq min_len2) may_nulls_set1 (Z.succ min_len2)) in + |> MayNulls.union (may_nulls_filter (Z.leq min_len2) may_nulls_set1 (Z.succ min_len2)) in (must_nulls_set_result, may_nulls_set_result, size1) (* any other case shouldn't happen as minimal index is always >= 0 *) | _ -> (MustNulls.top (), MayNulls.top (), size1) in @@ -1509,7 +1527,7 @@ struct else let min_i2 = must_nulls_min_elt must_nulls_set2' in let may_nulls_set2'_until_min_i2 = - match Idx.maximal size2 with + match idx_maximal size2 with | Some max_size2 -> may_nulls_filter (Z.geq min_i2) may_nulls_set2' max_size2 | None -> may_nulls_filter (Z.geq min_i2) may_nulls_set2' (Z.succ min_i2) in let must_nulls_set_result = must_nulls_filter (fun x -> if maxlen1_exists && maxlen2_exists then Z.lt (Z.add maxlen1 maxlen2) x else false) must_nulls_set1 min_size1 in @@ -1536,7 +1554,7 @@ struct let compute_concat must_nulls_set2' may_nulls_set2' = let strlen1 = to_string_length (must_nulls_set1, may_nulls_set1, size1) in let strlen2 = to_string_length (must_nulls_set2', may_nulls_set2', size2) in - match Idx.minimal size1, Idx.maximal size1, Idx.minimal strlen1, Idx.maximal strlen1, Idx.minimal strlen2, Idx.maximal strlen2 with + match Idx.minimal size1, idx_maximal size1, Idx.minimal strlen1, idx_maximal strlen1, Idx.minimal strlen2, idx_maximal strlen2 with | Some min_size1, Some max_size1, Some minlen1, Some maxlen1, Some minlen2, Some maxlen2 -> update_sets min_size1 max_size1 true minlen1 maxlen1 true minlen2 maxlen2 true must_nulls_set2' may_nulls_set2' (* no upper bound for length of concatenation *) @@ -1568,7 +1586,7 @@ struct if not (MayNulls.exists (Z.gt (Z.of_int n)) may_nulls_set2) then (MustNulls.singleton (Z.of_int n), MayNulls.singleton (Z.of_int n)) else if not (MustNulls.exists (Z.gt (Z.of_int n)) must_nulls_set2) then - let max_size2 = match Idx.maximal size2 with + let max_size2 = match idx_maximal size2 with | Some max_size2 -> max_size2 | None -> Z.succ (Z.of_int n) in (MustNulls.empty (), MayNulls.add (Z.of_int n) (may_nulls_filter (Z.geq (Z.of_int n)) may_nulls_set2 max_size2)) @@ -1576,7 +1594,7 @@ struct let min_size2 = match Idx.minimal size2 with | Some min_size2 -> min_size2 | None -> Z.zero in - let max_size2 = match Idx.maximal size2 with + let max_size2 = match idx_maximal size2 with | Some max_size2 -> max_size2 | None -> Z.of_int n in (must_nulls_filter (Z.gt (Z.of_int n)) must_nulls_set2 min_size2, may_nulls_filter (Z.gt (Z.of_int n)) may_nulls_set2 max_size2) in @@ -1590,7 +1608,7 @@ struct else let haystack_len = to_string_length haystack in let needle_len = to_string_length (must_nulls_set_needle, may_nulls_set_needle, size_needle) in - match Idx.maximal haystack_len, Idx.minimal needle_len with + match idx_maximal haystack_len, Idx.minimal needle_len with | Some haystack_max, Some needle_min -> (* if strlen(haystack) < strlen(needle), needle can never be substring of haystack => return None *) if Z.lt haystack_max needle_min then @@ -1606,7 +1624,7 @@ struct || (n_exists && Z.equal Z.zero n) then Idx.of_int IInt Z.zero (* if only s1 = empty string, return negative integer *) - else if MustNulls.mem Z.zero must_nulls_set1 && not (MustNulls.mem Z.zero must_nulls_set2) then + else if MustNulls.mem Z.zero must_nulls_set1 && not (MayNulls.mem Z.zero may_nulls_set2) then Idx.ending IInt Z.minus_one (* if only s2 = empty string, return positive integer *) else if MustNulls.mem Z.zero must_nulls_set2 then @@ -1644,7 +1662,7 @@ struct | Some min_size2 -> min_size2 | None -> Z.zero in (* issue a warning if n is (potentially) smaller than array sizes *) - (match Idx.maximal size1 with + (match idx_maximal size1 with | Some max_size1 -> if Z.gt (Z.of_int n) max_size1 then M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The size of the array of string 1 is smaller than n bytes" @@ -1653,7 +1671,7 @@ struct | None -> if Z.gt (Z.of_int n) min_size1 then M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The size of the array of string 1 might be smaller than n bytes"); - (match Idx.maximal size2 with + (match idx_maximal size2 with | Some max_size2 -> if Z.gt (Z.of_int n) max_size2 then M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The size of the array of string 2 is smaller than n bytes" @@ -1738,6 +1756,8 @@ struct | TrivialDomain -> (None, Some (T.top ()), None) | UnrolledDomain -> (None, None, Some (U.top ())) + let content_to_top x = unop_to_t' P.content_to_top T.content_to_top U.content_to_top x + let make ?(varAttr=[]) ?(typAttr=[]) i v = to_t @@ match get_domain ~varAttr ~typAttr with | PartitionedDomain -> (Some (P.make i v), None, None) | TrivialDomain -> (None, Some (T.make i v), None) @@ -1825,15 +1845,17 @@ struct let map f (t_f, t_n) = (F.map f t_f, N.map f t_n) let fold_left f acc (t_f, t_n) = F.fold_left f acc t_f + let content_to_top (t_f, t_n) = (F.content_to_top t_f, N.content_to_top t_n) + let smart_join x y (t_f1, t_n1) (t_f2, t_n2) = (F.smart_join x y t_f1 t_f2, N.smart_join x y t_n1 t_n2) let smart_widen x y (t_f1, t_n1) (t_f2, t_n2) = (F.smart_widen x y t_f1 t_f2, N.smart_widen x y t_n1 t_n2) let smart_leq x y (t_f1, t_n1) (t_f2, t_n2) = F.smart_leq x y t_f1 t_f2 && N.smart_leq x y t_n1 t_n2 let to_string_length (_, t_n) = N.to_string_length t_n - let string_copy (_, t_n1) (_, t_n2) n = (F.top (), N.string_copy t_n1 t_n2 n) - let string_concat (_, t_n1) (_, t_n2) n = (F.top (), N.string_concat t_n1 t_n2 n) - let substring_extraction (_, t_n1) (_, t_n2) = match N.substring_extraction t_n1 t_n2 with - | Some res -> Some (F.top (), res) + let string_copy (t_f1, t_n1) (_, t_n2) n = (F.content_to_top t_f1, N.string_copy t_n1 t_n2 n) + let string_concat (t_f1, t_n1) (_, t_n2) n = (F.content_to_top t_f1, N.string_concat t_n1 t_n2 n) + let substring_extraction (t_f1, t_n1) (_, t_n2) = match N.substring_extraction t_n1 t_n2 with + | Some res -> Some (F.content_to_top t_f1, res) | None -> None let string_comparison (_, t_n1) (_, t_n2) n = N.string_comparison t_n1 t_n2 n diff --git a/src/cdomains/arrayDomain.mli b/src/cdomains/arrayDomain.mli index ef503248c6..dc1b381340 100644 --- a/src/cdomains/arrayDomain.mli +++ b/src/cdomains/arrayDomain.mli @@ -46,6 +46,9 @@ sig val fold_left: ('a -> value -> 'a) -> 'a -> t -> 'a (** Left fold (like List.fold_left) over the arrays elements *) + val content_to_top: t -> t + (** Maps the array's content to top of value, but keeps the type and the size if known *) + val smart_join: (Cil.exp -> BigIntOps.t option) -> (Cil.exp -> BigIntOps.t option) -> t -> t -> t val smart_widen: (Cil.exp -> BigIntOps.t option) -> (Cil.exp -> BigIntOps.t option) -> t -> t -> t val smart_leq: (Cil.exp -> BigIntOps.t option) -> (Cil.exp -> BigIntOps.t option) -> t -> t -> bool diff --git a/tests/regression/73-strings/03-string_basics.c b/tests/regression/73-strings/03-string_basics.c index 38eec582d6..1cfa33a689 100644 --- a/tests/regression/73-strings/03-string_basics.c +++ b/tests/regression/73-strings/03-string_basics.c @@ -38,11 +38,18 @@ int main() { __goblint_check(len == 12); __goblint_check(i == 0); // UNKNOWN + strcpy(s1, "hi "); + strncpy(s1, s3, 3); + len = strlen(s1); // TODO: produces a false warning -- any possibility to fix? + __goblint_check(len == 3); // UNKNOWN + char tmp[] = "hi "; + len = strlen(tmp); + __goblint_check(len == 3); strcpy(s1, tmp); - /* strncpy(s1, s3, 3); */ + strncpy(s1, s3, 3); len = strlen(s1); - __goblint_check(len == 3); // UNKNOWN <----- wrong result: calculates 6 instead of 3 probably caused by wrong integration in base + __goblint_check(len == 3); strcat(s1, "ababcd"); char* cmp = strstr(s1, "bab"); @@ -58,6 +65,18 @@ int main() { __goblint_check(i > 0); // UNKNOWN strncpy(s1, "", 20); + strcpy(tmp, "\0hi"); + i = strcmp(s1, tmp); + __goblint_check(i == 0); // UNKNOWN + + char tmp2[] = ""; + strcpy(s1, tmp2); + i = strcmp(s1, tmp2); + __goblint_check(i == 0); + + i = strcmp(s1, tmp); + __goblint_check(i == 0); // UNKNOWN + concat_1(s1, 30); len = strlen(s1); __goblint_check(len == 30); // UNKNOWN From a42b9645785acc38d7aa93fb9757230368c627c0 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 13 Jun 2023 14:03:04 +0300 Subject: [PATCH 0112/1312] Convert all pthread functions to new specifications --- src/analyses/libraryFunctions.ml | 78 ++++++++++++++++---------------- 1 file changed, 38 insertions(+), 40 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 9b689b64ad..a9b1fafa40 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -251,15 +251,47 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ let pthread_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("pthread_create", special [__ "thread" [w]; drop "attr" [r]; __ "start_routine" [s]; __ "arg" []] @@ fun thread start_routine arg -> ThreadCreate { thread; start_routine; arg }); (* For precision purposes arg is not considered accessed here. Instead all accesses (if any) come from actually analyzing start_routine. *) ("pthread_exit", special [__ "retval" []] @@ fun retval -> ThreadExit { ret_val = retval }); (* Doesn't dereference the void* itself, but just passes to pthread_join. *) + ("pthread_cond_init", unknown [drop "cond" [w]; drop "attr" [w]]); ("pthread_cond_signal", special [__ "cond" []] @@ fun cond -> Signal cond); ("pthread_cond_broadcast", special [__ "cond" []] @@ fun cond -> Broadcast cond); ("pthread_cond_wait", special [__ "cond" []; __ "mutex" []] @@ fun cond mutex -> Wait {cond; mutex}); ("pthread_cond_timedwait", special [__ "cond" []; __ "mutex" []; __ "abstime" [r]] @@ fun cond mutex abstime -> TimedWait {cond; mutex; abstime}); + ("pthread_cond_destroy", unknown [drop "cond" [f]]); ("pthread_mutexattr_settype", special [__ "attr" []; __ "type" []] @@ fun attr typ -> MutexAttrSetType {attr; typ}); ("pthread_mutex_init", special [__ "mutex" []; __ "attr" []] @@ fun mutex attr -> MutexInit {mutex; attr}); + ("pthread_mutex_destroy", unknown [drop "mutex" [f]]); + ("pthread_mutex_lock", special [__ "mutex" []] @@ fun mutex -> Lock {lock = mutex; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = false}); + ("pthread_mutex_trylock", special [__ "mutex" []] @@ fun mutex -> Lock {lock = mutex; try_ = true; write = true; return_on_success = false}); + ("pthread_mutex_unlock", special [__ "mutex" []] @@ fun mutex -> Unlock mutex); + ("pthread_mutexattr_init", unknown [drop "attr" [w]]); + ("pthread_mutexattr_destroy", unknown [drop "attr" [f]]); + ("pthread_rwlock_init", unknown [drop "rwlock" [w]; drop "attr" [w]]); + ("pthread_rwlock_destroy", unknown [drop "rwlock" [f]]); + ("pthread_rwlock_rdlock", special [__ "rwlock" []] @@ fun rwlock -> Lock {lock = rwlock; try_ = get_bool "sem.lock.fail"; write = false; return_on_success = true}); + ("pthread_rwlock_tryrdlock", special [__ "rwlock" []] @@ fun rwlock -> Lock {lock = rwlock; try_ = get_bool "sem.lock.fail"; write = false; return_on_success = true}); + ("pthread_rwlock_wrlock", special [__ "rwlock" []] @@ fun rwlock -> Lock {lock = rwlock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true}); + ("pthread_rwlock_trywrlock", special [__ "rwlock" []] @@ fun rwlock -> Lock {lock = rwlock; try_ = true; write = true; return_on_success = false}); + ("pthread_rwlock_unlock", special [__ "rwlock" []] @@ fun rwlock -> Unlock rwlock); + ("pthread_rwlockattr_init", unknown [drop "attr" [w]]); + ("pthread_rwlockattr_destroy", unknown [drop "attr" [f]]); + ("pthread_spin_init", unknown [drop "lock" []; drop "pshared" []]); + ("pthread_spin_destroy", unknown [drop "lock" [f]]); + ("pthread_spin_lock", special [__ "lock" []] @@ fun lock -> Lock {lock = lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true}); + ("pthread_spin_trylock", special [__ "lock" []] @@ fun lock -> Lock {lock = lock; try_ = true; write = true; return_on_success = false}); + ("pthread_spin_unlock", special [__ "lock" []] @@ fun lock -> Unlock lock); + ("pthread_attr_init", unknown [drop "attr" [w]]); ("pthread_attr_destroy", unknown [drop "attr" [f]]); + ("pthread_attr_getdetachstate", unknown [drop "attr" [r]; drop "detachstate" [r]]); + ("pthread_attr_setdetachstate", unknown [drop "attr" [w]; drop "detachstate" []]); + ("pthread_attr_getstacksize", unknown [drop "attr" [r]; drop "stacksize" [r]]); + ("pthread_attr_setstacksize", unknown [drop "attr" [w]; drop "stacksize" []]); + ("pthread_attr_getscope", unknown [drop "attr" [r]; drop "scope" [r]]); + ("pthread_attr_setscope", unknown [drop "attr" [w]; drop "scope" []]); + ("pthread_self", unknown []); + ("pthread_sigmask", unknown [drop "how" []; drop "set" [r]; drop "oldset" [w]]); ("pthread_setspecific", unknown ~attrs:[InvalidateGlobals] [drop "key" []; drop "value" [w_deep]]); ("pthread_getspecific", unknown ~attrs:[InvalidateGlobals] [drop "key" []]); + ("pthread_key_create", unknown [drop "key" [w]; drop "destructor" [s]]); ("pthread_key_delete", unknown [drop "key" [f]]); ("pthread_cancel", unknown [drop "thread" []]); ("pthread_setcanceltype", unknown [drop "type" []; drop "oldtype" [w]]); @@ -753,24 +785,21 @@ let classify fn exps: categories = | "_spin_trylock" | "spin_trylock" | "mutex_trylock" | "_spin_trylock_irqsave" | "down_trylock" -> `Lock(true, true, true) - | "pthread_mutex_trylock" | "pthread_rwlock_trywrlock" | "pthread_spin_trylock" - -> `Lock (true, true, false) | "_spin_lock" | "_spin_lock_irqsave" | "_spin_lock_bh" | "down_write" | "mutex_lock" | "mutex_lock_interruptible" | "_write_lock" | "_raw_write_lock" - | "pthread_rwlock_wrlock" | "GetResource" | "_raw_spin_lock" + | "GetResource" | "_raw_spin_lock" | "_raw_spin_lock_flags" | "_raw_spin_lock_irqsave" | "_raw_spin_lock_irq" | "_raw_spin_lock_bh" - | "spin_lock" | "pthread_spin_lock" + | "spin_lock" -> `Lock (get_bool "sem.lock.fail", true, true) - | "pthread_mutex_lock" | "__pthread_mutex_lock" + | "__pthread_mutex_lock" -> `Lock (get_bool "sem.lock.fail", true, false) - | "pthread_rwlock_tryrdlock" | "pthread_rwlock_rdlock" | "_read_lock" | "_raw_read_lock" - | "down_read" + | "_read_lock" | "_raw_read_lock" | "down_read" -> `Lock (get_bool "sem.lock.fail", false, true) | "__raw_read_unlock" | "__raw_write_unlock" | "raw_spin_unlock" | "_spin_unlock" | "spin_unlock" | "_spin_unlock_irqrestore" | "_spin_unlock_bh" | "_raw_spin_unlock_bh" | "mutex_unlock" | "_write_unlock" | "_read_unlock" - | "pthread_mutex_unlock" | "__pthread_mutex_unlock" | "up_read" | "up_write" - | "up" | "pthread_spin_unlock" + | "__pthread_mutex_unlock" | "up_read" | "up_write" + | "up" -> `Unlock | x -> `Unknown x @@ -877,12 +906,6 @@ let invalidate_actions = [ "__printf_chk", readsAll;(*safe*) "printk", readsAll;(*safe*) "perror", readsAll;(*safe*) - "pthread_mutex_lock", readsAll;(*safe*) - "pthread_mutex_trylock", readsAll; - "pthread_mutex_unlock", readsAll;(*safe*) - "pthread_spin_lock", readsAll;(*safe*) - "pthread_spin_trylock", readsAll; - "pthread_spin_unlock", readsAll;(*safe*) "__pthread_mutex_lock", readsAll;(*safe*) "__pthread_mutex_trylock", readsAll; "__pthread_mutex_unlock", readsAll;(*safe*) @@ -894,11 +917,6 @@ let invalidate_actions = [ "_spin_lock", readsAll;(*safe*) "_spin_unlock", readsAll;(*safe*) "_spin_lock_irqsave", readsAll;(*safe*) - "pthread_mutex_destroy", readsAll;(*safe*) - "pthread_mutexattr_init", readsAll;(*safe*) - "pthread_spin_init", readsAll;(*safe*) - "pthread_spin_destroy", readsAll;(*safe*) - "pthread_self", readsAll;(*safe*) "read", writes [2];(*keep [2]*) "recv", writes [2];(*keep [2]*) "scanf", writesAllButFirst 1 readsAll;(*drop 1*) @@ -941,24 +959,13 @@ let invalidate_actions = [ "close", writesAll;(*unsafe*) "setsid", readsAll;(*safe*) "strerror_r", writesAll;(*unsafe*) - "pthread_attr_init", writesAll; (*unsafe*) - "pthread_attr_setdetachstate", writesAll;(*unsafe*) - "pthread_attr_setstacksize", writesAll;(*unsafe*) - "pthread_attr_setscope", writesAll;(*unsafe*) - "pthread_attr_getdetachstate", readsAll;(*safe*) - "pthread_attr_getstacksize", readsAll;(*safe*) - "pthread_attr_getscope", readsAll;(*safe*) - "pthread_cond_init", readsAll; (*safe*) - "pthread_cond_destroy", readsAll;(*safe*) "__pthread_cond_init", readsAll; (*safe*) "__pthread_cond_wait", readsAll; (*safe*) "__pthread_cond_signal", readsAll;(*safe*) "__pthread_cond_broadcast", readsAll;(*safe*) "__pthread_cond_destroy", readsAll;(*safe*) - "pthread_key_create", writesAll;(*unsafe*) "sigemptyset", writesAll;(*unsafe*) "sigaddset", writesAll;(*unsafe*) - "pthread_sigmask", writesAllButFirst 2 readsAll;(*unsafe*) "raise", writesAll;(*unsafe*) "_strlen", readsAll;(*safe*) "__builtin_alloca", readsAll;(*safe*) @@ -1080,15 +1087,6 @@ let invalidate_actions = [ "munmap", readsAll;(*safe*) "mmap", readsAll;(*safe*) "clock", readsAll; - "pthread_rwlock_wrlock", readsAll; - "pthread_rwlock_trywrlock", readsAll; - "pthread_rwlock_rdlock", readsAll; - "pthread_rwlock_tryrdlock", readsAll; - "pthread_rwlockattr_destroy", writesAll; - "pthread_rwlockattr_init", writesAll; - "pthread_rwlock_destroy", readsAll; - "pthread_rwlock_init", readsAll; - "pthread_rwlock_unlock", readsAll; "__builtin_va_arg_pack_len", readsAll; "__open_too_many_args", readsAll; "usb_submit_urb", readsAll; (* first argument is written to but according to specification must not be read from anymore *) From 8c44ac7d68850372dfe8ffa9693e5a381e1fca5a Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 13 Jun 2023 14:49:40 +0300 Subject: [PATCH 0113/1312] Convert linux kernel functions from classify to new specifications --- src/analyses/libraryFunctions.ml | 44 +++++++++++++++++--------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index a9b1fafa40..3b3af35399 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -417,8 +417,24 @@ let console_sem = AddrOf (Cil.var (Cilfacade.create_var (makeGlobalVar "[console (** Linux kernel functions. *) let linux_kernel_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ + ("down_trylock", special [__ "sem" []] @@ fun sem -> Lock { lock = sem; try_ = true; write = true; return_on_success = true }); + ("down_read", special [__ "sem" []] @@ fun sem -> Lock { lock = sem; try_ = get_bool "sem.lock.fail"; write = false; return_on_success = true }); + ("down_write", special [__ "sem" []] @@ fun sem -> Lock { lock = sem; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true }); + ("up", special [__ "sem" []] @@ fun sem -> Unlock sem); + ("up_read", special [__ "sem" []] @@ fun sem -> Unlock sem); + ("up_write", special [__ "sem" []] @@ fun sem -> Unlock sem); + ("mutex_init", unknown [drop "mutex" []]); + ("mutex_lock", special [__ "lock" []] @@ fun lock -> Lock { lock = lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true }); + ("mutex_trylock", special [__ "lock" []] @@ fun lock -> Lock { lock = lock; try_ = true; write = true; return_on_success = true }); + ("mutex_lock_interruptible", special [__ "lock" []] @@ fun lock -> Lock { lock = lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true }); + ("mutex_unlock", special [__ "lock" []] @@ fun lock -> Unlock lock); + ("spin_lock_init", unknown [drop "lock" []]); + ("spin_lock", special [__ "lock" []] @@ fun lock -> Lock { lock = lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true }); + ("spin_trylock", special [__ "lock" []] @@ fun lock -> Lock { lock = lock; try_ = true; write = true; return_on_success = true }); + ("spin_unlock", special [__ "lock" []] @@ fun lock -> Unlock lock); ("spin_lock_irqsave", special [__ "lock" []; drop "flags" []] @@ fun lock -> Lock { lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true }); ("spin_unlock_irqrestore", special [__ "lock" []; drop "flags" []] @@ fun lock -> Unlock lock); + ("raw_spin_unlock", special [__ "lock" []] @@ fun lock -> Unlock lock); ("_raw_spin_unlock_irqrestore", special [__ "lock" []; drop "flags" []] @@ fun lock -> Unlock lock); ("spinlock_check", special [__ "lock" []] @@ fun lock -> Identity lock); (* Identity, because we don't want lock internals. *) ("_lock_kernel", special [drop "func" [r]; drop "file" [r]; drop "line" []] @@ Lock { lock = big_kernel_lock; try_ = false; write = true; return_on_success = true }); @@ -782,24 +798,19 @@ let classify fn exps: categories = | n::size::_ -> `Calloc (n, size) | _ -> strange_arguments () end - | "_spin_trylock" | "spin_trylock" | "mutex_trylock" | "_spin_trylock_irqsave" - | "down_trylock" + | "_spin_trylock" | "_spin_trylock_irqsave" -> `Lock(true, true, true) - | "_spin_lock" | "_spin_lock_irqsave" | "_spin_lock_bh" | "down_write" - | "mutex_lock" | "mutex_lock_interruptible" | "_write_lock" | "_raw_write_lock" - | "GetResource" | "_raw_spin_lock" + | "_spin_lock" | "_spin_lock_irqsave" | "_spin_lock_bh" + | "_write_lock" | "_raw_write_lock" | "GetResource" | "_raw_spin_lock" | "_raw_spin_lock_flags" | "_raw_spin_lock_irqsave" | "_raw_spin_lock_irq" | "_raw_spin_lock_bh" - | "spin_lock" -> `Lock (get_bool "sem.lock.fail", true, true) | "__pthread_mutex_lock" -> `Lock (get_bool "sem.lock.fail", true, false) - | "_read_lock" | "_raw_read_lock" | "down_read" + | "_read_lock" | "_raw_read_lock" -> `Lock (get_bool "sem.lock.fail", false, true) - | "__raw_read_unlock" | "__raw_write_unlock" | "raw_spin_unlock" - | "_spin_unlock" | "spin_unlock" | "_spin_unlock_irqrestore" | "_spin_unlock_bh" | "_raw_spin_unlock_bh" - | "mutex_unlock" | "_write_unlock" | "_read_unlock" - | "__pthread_mutex_unlock" | "up_read" | "up_write" - | "up" + | "__raw_read_unlock" | "__raw_write_unlock" + | "_spin_unlock" | "_spin_unlock_irqrestore" | "_spin_unlock_bh" | "_raw_spin_unlock_bh" + | "_write_unlock" | "_read_unlock" | "__pthread_mutex_unlock" -> `Unlock | x -> `Unknown x @@ -910,10 +921,6 @@ let invalidate_actions = [ "__pthread_mutex_trylock", readsAll; "__pthread_mutex_unlock", readsAll;(*safe*) "__mutex_init", readsAll;(*safe*) - "mutex_init", readsAll;(*safe*) - "mutex_lock", readsAll;(*safe*) - "mutex_lock_interruptible", readsAll;(*safe*) - "mutex_unlock", readsAll;(*safe*) "_spin_lock", readsAll;(*safe*) "_spin_unlock", readsAll;(*safe*) "_spin_lock_irqsave", readsAll;(*safe*) @@ -1101,12 +1108,7 @@ let invalidate_actions = [ "setpriority", readsAll; "getpriority", readsAll; (* ddverify *) - "spin_lock_init", readsAll; - "spin_lock", readsAll; - "spin_unlock", readsAll; "sema_init", readsAll; - "down_trylock", readsAll; - "up", readsAll; "__goblint_assume_join", readsAll; ] From 2ef2e7f4cb2311c76e135435cccacfb61c9a00de Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 13 Jun 2023 14:51:27 +0300 Subject: [PATCH 0114/1312] Remove GetResource from libraryFunctions --- src/analyses/libraryFunctions.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 3b3af35399..d0bb1edf53 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -801,7 +801,7 @@ let classify fn exps: categories = | "_spin_trylock" | "_spin_trylock_irqsave" -> `Lock(true, true, true) | "_spin_lock" | "_spin_lock_irqsave" | "_spin_lock_bh" - | "_write_lock" | "_raw_write_lock" | "GetResource" | "_raw_spin_lock" + | "_write_lock" | "_raw_write_lock" | "_raw_spin_lock" | "_raw_spin_lock_flags" | "_raw_spin_lock_irqsave" | "_raw_spin_lock_irq" | "_raw_spin_lock_bh" -> `Lock (get_bool "sem.lock.fail", true, true) | "__pthread_mutex_lock" From 30d5aea6ed99832d8b6cafac19ac9e924561adb5 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 13 Jun 2023 15:31:24 +0300 Subject: [PATCH 0115/1312] Convert most functions from classify to new specifications --- src/analyses/libraryFunctions.ml | 43 ++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index d0bb1edf53..31ee8bc497 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -261,8 +261,10 @@ let pthread_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("pthread_mutex_init", special [__ "mutex" []; __ "attr" []] @@ fun mutex attr -> MutexInit {mutex; attr}); ("pthread_mutex_destroy", unknown [drop "mutex" [f]]); ("pthread_mutex_lock", special [__ "mutex" []] @@ fun mutex -> Lock {lock = mutex; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = false}); + ("__pthread_mutex_lock", special [__ "mutex" []] @@ fun mutex -> Lock {lock = mutex; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = false}); ("pthread_mutex_trylock", special [__ "mutex" []] @@ fun mutex -> Lock {lock = mutex; try_ = true; write = true; return_on_success = false}); ("pthread_mutex_unlock", special [__ "mutex" []] @@ fun mutex -> Unlock mutex); + ("__pthread_mutex_unlock", special [__ "mutex" []] @@ fun mutex -> Unlock mutex); ("pthread_mutexattr_init", unknown [drop "attr" [w]]); ("pthread_mutexattr_destroy", unknown [drop "attr" [f]]); ("pthread_rwlock_init", unknown [drop "rwlock" [w]; drop "attr" [w]]); @@ -430,12 +432,34 @@ let linux_kernel_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("mutex_unlock", special [__ "lock" []] @@ fun lock -> Unlock lock); ("spin_lock_init", unknown [drop "lock" []]); ("spin_lock", special [__ "lock" []] @@ fun lock -> Lock { lock = lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true }); + ("_spin_lock", special [__ "lock" []] @@ fun lock -> Lock { lock = lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true }); + ("_spin_lock_bh", special [__ "lock" []] @@ fun lock -> Lock { lock = lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true }); ("spin_trylock", special [__ "lock" []] @@ fun lock -> Lock { lock = lock; try_ = true; write = true; return_on_success = true }); + ("_spin_trylock", special [__ "lock" []] @@ fun lock -> Lock { lock = lock; try_ = true; write = true; return_on_success = true }); ("spin_unlock", special [__ "lock" []] @@ fun lock -> Unlock lock); + ("_spin_unlock", special [__ "lock" []] @@ fun lock -> Unlock lock); + ("_spin_unlock_bh", special [__ "lock" []] @@ fun lock -> Unlock lock); ("spin_lock_irqsave", special [__ "lock" []; drop "flags" []] @@ fun lock -> Lock { lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true }); + ("_spin_lock_irqsave", special [__ "lock" []] @@ fun lock -> Lock { lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true }); + ("_spin_trylock_irqsave", special [__ "lock" []; drop "flags" []] @@ fun lock -> Lock { lock; try_ = true; write = true; return_on_success = true }); ("spin_unlock_irqrestore", special [__ "lock" []; drop "flags" []] @@ fun lock -> Unlock lock); + ("_spin_unlock_irqrestore", special [__ "lock" []; drop "flags" []] @@ fun lock -> Unlock lock); ("raw_spin_unlock", special [__ "lock" []] @@ fun lock -> Unlock lock); ("_raw_spin_unlock_irqrestore", special [__ "lock" []; drop "flags" []] @@ fun lock -> Unlock lock); + ("_raw_spin_lock", special [__ "lock" []] @@ fun lock -> Lock { lock = lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true }); + ("_raw_spin_lock_flags", special [__ "lock" []; drop "flags" []] @@ fun lock -> Lock { lock = lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true }); + ("_raw_spin_lock_irqsave", special [__ "lock" []] @@ fun lock -> Lock { lock = lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true }); + ("_raw_spin_lock_irq", special [__ "lock" []] @@ fun lock -> Lock { lock = lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true }); + ("_raw_spin_lock_bh", special [__ "lock" []] @@ fun lock -> Lock { lock = lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true }); + ("_raw_spin_unlock_bh", special [__ "lock" []] @@ fun lock -> Unlock lock); + ("_read_lock", special [__ "lock" []] @@ fun lock -> Lock { lock = lock; try_ = get_bool "sem.lock.fail"; write = false; return_on_success = true }); + ("_read_unlock", special [__ "lock" []] @@ fun lock -> Unlock lock); + ("_raw_read_lock", special [__ "lock" []] @@ fun lock -> Lock { lock = lock; try_ = get_bool "sem.lock.fail"; write = false; return_on_success = true }); + ("__raw_read_unlock", special [__ "lock" []] @@ fun lock -> Unlock lock); + ("_write_lock", special [__ "lock" []] @@ fun lock -> Lock { lock = lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true }); + ("_write_unlock", special [__ "lock" []] @@ fun lock -> Unlock lock); + ("_raw_write_lock", special [__ "lock" []] @@ fun lock -> Lock { lock = lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true }); + ("__raw_write_unlock", special [__ "lock" []] @@ fun lock -> Unlock lock); ("spinlock_check", special [__ "lock" []] @@ fun lock -> Identity lock); (* Identity, because we don't want lock internals. *) ("_lock_kernel", special [drop "func" [r]; drop "file" [r]; drop "line" []] @@ Lock { lock = big_kernel_lock; try_ = false; write = true; return_on_success = true }); ("_unlock_kernel", special [drop "func" [r]; drop "file" [r]; drop "line" []] @@ Unlock big_kernel_lock); @@ -798,20 +822,6 @@ let classify fn exps: categories = | n::size::_ -> `Calloc (n, size) | _ -> strange_arguments () end - | "_spin_trylock" | "_spin_trylock_irqsave" - -> `Lock(true, true, true) - | "_spin_lock" | "_spin_lock_irqsave" | "_spin_lock_bh" - | "_write_lock" | "_raw_write_lock" | "_raw_spin_lock" - | "_raw_spin_lock_flags" | "_raw_spin_lock_irqsave" | "_raw_spin_lock_irq" | "_raw_spin_lock_bh" - -> `Lock (get_bool "sem.lock.fail", true, true) - | "__pthread_mutex_lock" - -> `Lock (get_bool "sem.lock.fail", true, false) - | "_read_lock" | "_raw_read_lock" - -> `Lock (get_bool "sem.lock.fail", false, true) - | "__raw_read_unlock" | "__raw_write_unlock" - | "_spin_unlock" | "_spin_unlock_irqrestore" | "_spin_unlock_bh" | "_raw_spin_unlock_bh" - | "_write_unlock" | "_read_unlock" | "__pthread_mutex_unlock" - -> `Unlock | x -> `Unknown x @@ -917,13 +927,8 @@ let invalidate_actions = [ "__printf_chk", readsAll;(*safe*) "printk", readsAll;(*safe*) "perror", readsAll;(*safe*) - "__pthread_mutex_lock", readsAll;(*safe*) "__pthread_mutex_trylock", readsAll; - "__pthread_mutex_unlock", readsAll;(*safe*) "__mutex_init", readsAll;(*safe*) - "_spin_lock", readsAll;(*safe*) - "_spin_unlock", readsAll;(*safe*) - "_spin_lock_irqsave", readsAll;(*safe*) "read", writes [2];(*keep [2]*) "recv", writes [2];(*keep [2]*) "scanf", writesAllButFirst 1 readsAll;(*drop 1*) From 01ce50483be81bf863e5284280a73bcebe49e389 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 13 Jun 2023 16:10:44 +0300 Subject: [PATCH 0116/1312] Convert all __pthread functions to new specifications --- src/analyses/libraryFunctions.ml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 31ee8bc497..f2fd7e0d41 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -252,17 +252,23 @@ let pthread_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("pthread_create", special [__ "thread" [w]; drop "attr" [r]; __ "start_routine" [s]; __ "arg" []] @@ fun thread start_routine arg -> ThreadCreate { thread; start_routine; arg }); (* For precision purposes arg is not considered accessed here. Instead all accesses (if any) come from actually analyzing start_routine. *) ("pthread_exit", special [__ "retval" []] @@ fun retval -> ThreadExit { ret_val = retval }); (* Doesn't dereference the void* itself, but just passes to pthread_join. *) ("pthread_cond_init", unknown [drop "cond" [w]; drop "attr" [w]]); + ("__pthread_cond_init", unknown [drop "cond" [w]; drop "attr" [w]]); ("pthread_cond_signal", special [__ "cond" []] @@ fun cond -> Signal cond); + ("__pthread_cond_signal", special [__ "cond" []] @@ fun cond -> Signal cond); ("pthread_cond_broadcast", special [__ "cond" []] @@ fun cond -> Broadcast cond); + ("__pthread_cond_broadcast", special [__ "cond" []] @@ fun cond -> Broadcast cond); ("pthread_cond_wait", special [__ "cond" []; __ "mutex" []] @@ fun cond mutex -> Wait {cond; mutex}); + ("__pthread_cond_wait", special [__ "cond" []; __ "mutex" []] @@ fun cond mutex -> Wait {cond; mutex}); ("pthread_cond_timedwait", special [__ "cond" []; __ "mutex" []; __ "abstime" [r]] @@ fun cond mutex abstime -> TimedWait {cond; mutex; abstime}); ("pthread_cond_destroy", unknown [drop "cond" [f]]); + ("__pthread_cond_destroy", unknown [drop "cond" [f]]); ("pthread_mutexattr_settype", special [__ "attr" []; __ "type" []] @@ fun attr typ -> MutexAttrSetType {attr; typ}); ("pthread_mutex_init", special [__ "mutex" []; __ "attr" []] @@ fun mutex attr -> MutexInit {mutex; attr}); ("pthread_mutex_destroy", unknown [drop "mutex" [f]]); ("pthread_mutex_lock", special [__ "mutex" []] @@ fun mutex -> Lock {lock = mutex; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = false}); ("__pthread_mutex_lock", special [__ "mutex" []] @@ fun mutex -> Lock {lock = mutex; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = false}); ("pthread_mutex_trylock", special [__ "mutex" []] @@ fun mutex -> Lock {lock = mutex; try_ = true; write = true; return_on_success = false}); + ("__pthread_mutex_trylock", special [__ "mutex" []] @@ fun mutex -> Lock {lock = mutex; try_ = true; write = true; return_on_success = false}); ("pthread_mutex_unlock", special [__ "mutex" []] @@ fun mutex -> Unlock mutex); ("__pthread_mutex_unlock", special [__ "mutex" []] @@ fun mutex -> Unlock mutex); ("pthread_mutexattr_init", unknown [drop "attr" [w]]); @@ -927,7 +933,6 @@ let invalidate_actions = [ "__printf_chk", readsAll;(*safe*) "printk", readsAll;(*safe*) "perror", readsAll;(*safe*) - "__pthread_mutex_trylock", readsAll; "__mutex_init", readsAll;(*safe*) "read", writes [2];(*keep [2]*) "recv", writes [2];(*keep [2]*) @@ -971,11 +976,6 @@ let invalidate_actions = [ "close", writesAll;(*unsafe*) "setsid", readsAll;(*safe*) "strerror_r", writesAll;(*unsafe*) - "__pthread_cond_init", readsAll; (*safe*) - "__pthread_cond_wait", readsAll; (*safe*) - "__pthread_cond_signal", readsAll;(*safe*) - "__pthread_cond_broadcast", readsAll;(*safe*) - "__pthread_cond_destroy", readsAll;(*safe*) "sigemptyset", writesAll;(*unsafe*) "sigaddset", writesAll;(*unsafe*) "raise", writesAll;(*unsafe*) From 44bd644bf0ac9951e19a1cc042fe69eac6805552 Mon Sep 17 00:00:00 2001 From: Nathan Schmidt Date: Tue, 13 Jun 2023 23:26:51 +0200 Subject: [PATCH 0117/1312] Added new thorough regression test --- src/cdomains/arrayDomain.ml | 10 +- .../73-strings/01-string_literals.c | 1 + tests/regression/73-strings/04-char_arrays.c | 201 ++++++++++++++++++ 3 files changed, 209 insertions(+), 3 deletions(-) create mode 100644 tests/regression/73-strings/04-char_arrays.c diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index dc25e52db4..2661bb7767 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1326,7 +1326,7 @@ struct let warn_no_null min_must_null exists_min_must_null min_may_null = if Z.geq min_may_null (Z.of_int n) then M.warn "Resulting string might not be null-terminated because src doesn't contain a null byte in the first n bytes" - else if (exists_min_must_null && Z.geq min_must_null (Z.of_int n)) || not exists_min_must_null then + else if (exists_min_must_null && (Z.geq min_must_null (Z.of_int n)) || (Z.gt min_must_null min_may_null)) || not exists_min_must_null then M.warn "Resulting string might not be null-terminated because src might not contain a null byte in the first n bytes" in if n < 0 then @@ -1365,8 +1365,11 @@ struct let min_may_null = may_nulls_min_elt may_nulls_set in (* warn if resulting array may not contain null byte *) warn_no_null min_must_null true min_may_null; - (* remove indexes >= n and add all indexes from minimal must/may null to n - 1 in the sets *) - (update_must_indexes min_must_null must_nulls_set, update_may_indexes min_may_null may_nulls_set, Idx.of_int ILong (Z.of_int n))) + (* if min_must_null = min_may_null, remove indexes >= n and add all indexes from minimal must/may null to n - 1 in the sets *) + if Z.equal min_must_null min_may_null then + (update_must_indexes min_must_null must_nulls_set, update_may_indexes min_may_null may_nulls_set, Idx.of_int ILong (Z.of_int n)) + else + (MustNulls.top (), update_may_indexes min_may_null may_nulls_set, Idx.of_int ILong (Z.of_int n))) let to_string_length (must_nulls_set, may_nulls_set, size) = (* if must_nulls_set and min_nulls_set empty, definitely no null byte in array => return interval [size, inf) and warn *) @@ -1458,6 +1461,7 @@ struct (* any other case shouldn't happen as minimal index is always >= 0 *) | _ -> (MustNulls.top (), MayNulls.top (), size1) in + (* TODO: would it be useful to warn if size of ar2 is (potentially bigger) than size of ar1? *) match n with (* strcpy *) | None -> diff --git a/tests/regression/73-strings/01-string_literals.c b/tests/regression/73-strings/01-string_literals.c index 14f4d43014..42a888d1b4 100644 --- a/tests/regression/73-strings/01-string_literals.c +++ b/tests/regression/73-strings/01-string_literals.c @@ -2,6 +2,7 @@ #include #include +#include char* hello_world() { return "Hello world!"; diff --git a/tests/regression/73-strings/04-char_arrays.c b/tests/regression/73-strings/04-char_arrays.c new file mode 100644 index 0000000000..20e8cababb --- /dev/null +++ b/tests/regression/73-strings/04-char_arrays.c @@ -0,0 +1,201 @@ +// PARAM: --disable ana.base.limit-string-addresses --enable ana.int.interval + +#include +#include +#include + +int main() { + example1(); + example2(); + example3(); + example4(); + example5(); + example6(); + example7(); + example8(); + example9(); + + return 0; +} + +void example1() { + char s1[42]; + char s2[20] = "testing"; // must null at 7, may null starting from 7 + + strcpy(s1, s2); // must null and may null at 7 + + size_t len = strlen(s1); + __goblint_check(len == 7); + + strcat(s1, s2); // "testingtesting" + + len = strlen(s1); + __goblint_check(len == 14); +} + +void example2() { + char s1[42]; + char s2[20] = "testing"; // must null at 7, may null starting from 7 + + if (rand() == 42) + s2[1] = '\0'; + + strcpy(s1, s2); // may null at 1 and starting from 7 + + size_t len = strlen(s1); // WARN: no must null in s1 + __goblint_check(len >= 1); + __goblint_check(len <= 7); // UNKNOWN + + strcpy(s2, s1); // WARN: no must null in s1 +} + +void example3() { + char s1[5] = "abc\0d"; // must and may null at 3 + char s2[] = "a"; // must and may null at 1 + + strcpy(s1, s2); // "a\0c\0d" + + size_t len = strlen(s1); + __goblint_check(len == 1); + + s1[1] = 'b'; // "abc\0d" + len = strlen(s1); + __goblint_check(len == 3); +} + +void example4() { + char s1[7] = "hello!"; // must and may null at 6 + char s2[8] = "goblint"; // must and may null at 7 + + strncpy(s1, s2, 7); // WARN + + size_t len = strlen(s1); // WARN + __goblint_check(len >= 7); // no null byte in s1 +} + +void example5() { + char s1[42] = "a string, i.e. null-terminated char array"; // must and may null at 42 + for (int i = 0; i < 42; i += 3) { + if (rand() != 42) + s1[i] = '\0'; + } + s1[41] = '.'; // no must nulls, only may null a 0, 3, 6... + + char s2[42] = "actually containing some text"; // must and may null at 29 + char s3[60] = "text: "; // must and may null at 6 + + strcat(s3, s1); // WARN: no must nulls, may nulls at 6, 9, 12... + + size_t len = strlen(s3); // WARN + __goblint_check(len >= 6); + __goblint_check(len > 6); // UNKNOWN + + strncat(s2, s3, 10); // WARN: no must nulls, may nulls at 35 and 38 + + len = strlen(s2); // WARN + __goblint_check(len >= 35); + __goblint_check(len > 40); // UNKNOWN +} + +void example6() { + char s1[50] = "hello"; // must and may null at 5 + char s2[] = " world!"; // must and may null at 7 + char s3[] = " goblint."; // must and may null at 9 + + if (rand() < 42) + strcat(s1, s2); // "hello world!" -> must and may null at 12 + else + strncat(s1, s3, 8); // "hello goblint" -> must and may null at 13 + + char s4[20]; + strcpy(s4, s1); // WARN: no must nulls, may nulls at 12 and 13 + + size_t len = strlen(s4); + __goblint_check(len >= 12); + __goblint_check(len == 13); // UNKNOWN + + s4[14] = '\0'; // must null at 14, may nulls at 12, 13 and 14 + len = strlen(s4); + __goblint_check(len >= 12); + __goblint_check(len <= 14); + + char s5[20]; + strncpy(s5, s4, 16); // WARN: no must nulls, may nulls at 12, 13, 14, 15... + len = strlen(s5); // WARN + __goblint_check(len >= 12); + __goblint_check(len <= 14); // UNKNOWN + __goblint_check(len < 20); // UNKNOWN +} + +void example7() { + char s1[6] = "abc"; // must and may null at 3 + if (rand() == 42) + s1[5] = '\0'; // must null at 3, may nulls at 3 and 5 + + char s2[] = "hello world"; // must and may null at 11 + + strncpy(s2, s1, 8); // WARN: 8 > size of s1 -- must and may nulls at 3, 4, 5, 6 and 7 + + size_t len = strlen(s2); + __goblint_check(len == 3); + + s2[3] = 'a'; // must and may nulls at 4, 5, 6 and 7 + len = strlen(s2); + __goblint_check(len == 4); + + for (int i = 4; i <= 7; i++) + s2[i] = 'a'; + s2[11] = 'a'; // no must nulls, may nulls at 4, 5, 6 and 7 + + len = strlen(s2); // WARN + __goblint_check(len >= 12); // UNKNOWN: loop transformed to interval + + s2[4] = s2[5] = s2[6] = s2[7] = 'a'; + len = strlen(s2); // WARN: no must nulls and may nulls + __goblint_check(len >= 12); +} + +void example8() { + char empty[] = ""; + char s1[] = "hello world"; // must and may null at 11 + char s2[] = "test"; // must and may null at 4 + + char cmp[50]; + strcpy(cmp, strstr(s1, empty)); // WARN + size_t len = strlen(cmp); // WARN + __goblint_check(len == 11); // UNKNOWN because can't directly assign result of strstr to cmp, + // TODO: might make handling of this useless in NullByte domain? + + char* cmp_ptr = strstr(s2, s1); + __goblint_check(cmp_ptr == NULL); +} + +void example9() { + char empty1[] = ""; + char empty2[] = "\0 also empty"; + char s1[] = "hi"; + char s2[] = "hello"; + + int i = strcmp(empty1, empty2); + __goblint_check(i == 0); + + i = strcmp(empty1, s1); + __goblint_check(i < 0); + + i = strcmp(s1, empty1); + __goblint_check(i > 0); + + i = strcmp(s1, s2); + __goblint_check(i != 0); + + i = strncmp(s1, s2, 2); + __goblint_check(i != 0); // UNKNOWN + + s1[2] = 'a'; + + i = strcmp(s1, s2); // WARN + __goblint_check(i != 0); // UNKNOWN + + i = strncmp(s1, s2, 10); // WARN + __goblint_check(i != 0); // UNKNOWN +} From b01d69e94d12defa37810b8516e9d9b744cdbbfe Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Wed, 14 Jun 2023 13:11:40 +0200 Subject: [PATCH 0118/1312] Remove unused stuff --- src/analyses/termination_new.ml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index 1eac676d91..ef623b468b 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -3,7 +3,6 @@ open Analyses open GoblintCil open TerminationPreprocessing -include Printf exception PreProcessing of string @@ -63,10 +62,6 @@ struct D.add x is_bounded ctx.local | _ -> ctx.local - let branch ctx (exp : exp) (tv : bool) = - ctx.local (* TODO: Do we actually need a branch transfer function? *) - - (* provides information to Goblint*) let query ctx (type a) (q: a Queries.t): a Queries.result = let open Queries in From 6ee3312220b2838ea34033be3fc81ca995232be6 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Wed, 14 Jun 2023 13:17:44 +0200 Subject: [PATCH 0119/1312] Make things nice, update comments --- src/analyses/termination_new.ml | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index ef623b468b..4eced288df 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -7,7 +7,9 @@ open TerminationPreprocessing exception PreProcessing of string let loopCounters : varinfo list ref = ref [] -let upjumpingGotos : location list ref = ref [] (*contains the locations of the upjumping gotos*) + +(* Contains the locations of the upjumping gotos *) +let upjumpingGotos : location list ref = ref [] let loopExit : varinfo ref = ref (makeVarinfo false "-error" Cil.intType) @@ -17,8 +19,9 @@ let is_loop_counter_var (x : varinfo) = let is_loop_exit_indicator (x : varinfo) = x = !loopExit -(* checks if at the current location (=loc) of the analysis an upjumping goto was already reached - true: no upjumping goto was reached till now*) +(** Checks whether at the current location (=loc) of the analysis an + * upjumping goto was already reached. Returns true if no upjumping goto was + * reached until now *) let currrently_no_upjumping_gotos (loc : location) = List.for_all (function (l) -> (l >= loc)) upjumpingGotos.contents @@ -49,33 +52,33 @@ struct include Analyses.IdentitySpec let assign ctx (lval : lval) (rval : exp) = - (* Detect loop counter variable assignment to 0 *) + (* Detect assignment to loop counter variable *) match lval, rval with - (* Assume that the following loop does not terminate *) (Var x, NoOffset), _ when is_loop_counter_var x -> + (* Assume that the following loop does not terminate *) if not (no_upjumping_gotos ()) then printf "\n4 problem\n"; D.add x false ctx.local - (* Loop exit: Check whether loop counter variable is bounded *) | (Var y, NoOffset), Lval (Var x, NoOffset) when is_loop_exit_indicator y -> + (* Loop exit: Check whether loop counter variable is bounded *) let is_bounded = check_bounded ctx x in if not (no_upjumping_gotos ()) then printf "\n5 problem\n"; D.add x is_bounded ctx.local | _ -> ctx.local - (* provides information to Goblint*) + (** Provides information to Goblint *) let query ctx (type a) (q: a Queries.t): a Queries.result = let open Queries in match q with | Queries.MustTermLoop v when check_bounded ctx v -> - true (* TODO should we use the checl_bound function?*) + true (* TODO should we use the check_bounded function? *) | Queries.MustTermProg -> - true (*TODO check if all values in the domain are true -> true*) + true (*TODO check if all values in the domain are true -> true *) | _ -> Result.top q end let () = - (** Register the preprocessing *) + (* Register the preprocessing *) Cilfacade.register_preprocess_cil (Spec.name ()) (new loopCounterVisitor loopCounters upjumpingGotos loopExit); - (** Register this analysis within the master control program *) + (* Register this analysis within the master control program *) MCP.register_analysis (module Spec : MCPSpec) From 3cc743ab2e55758c0f5e58c51188f24b5d487d37 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Wed, 14 Jun 2023 13:21:49 +0200 Subject: [PATCH 0120/1312] Unify style to snake_case --- src/analyses/termination_new.ml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index 4eced288df..7a7f046c39 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -6,27 +6,27 @@ open TerminationPreprocessing exception PreProcessing of string -let loopCounters : varinfo list ref = ref [] +let loop_counters : varinfo list ref = ref [] (* Contains the locations of the upjumping gotos *) -let upjumpingGotos : location list ref = ref [] +let upjumping_gotos : location list ref = ref [] -let loopExit : varinfo ref = ref (makeVarinfo false "-error" Cil.intType) +let loop_exit : varinfo ref = ref (makeVarinfo false "-error" Cil.intType) let is_loop_counter_var (x : varinfo) = - List.mem x !loopCounters + List.mem x !loop_counters let is_loop_exit_indicator (x : varinfo) = - x = !loopExit + x = !loop_exit (** Checks whether at the current location (=loc) of the analysis an * upjumping goto was already reached. Returns true if no upjumping goto was * reached until now *) let currrently_no_upjumping_gotos (loc : location) = - List.for_all (function (l) -> (l >= loc)) upjumpingGotos.contents + List.for_all (function (l) -> (l >= loc)) upjumping_gotos.contents let no_upjumping_gotos () = - (List.length upjumpingGotos.contents) <= 0 + (List.length upjumping_gotos.contents) <= 0 (** Checks whether a variable can be bounded *) let check_bounded ctx varinfo = @@ -79,6 +79,6 @@ end let () = (* Register the preprocessing *) - Cilfacade.register_preprocess_cil (Spec.name ()) (new loopCounterVisitor loopCounters upjumpingGotos loopExit); + Cilfacade.register_preprocess_cil (Spec.name ()) (new loopCounterVisitor loop_counters upjumping_gotos loop_exit); (* Register this analysis within the master control program *) MCP.register_analysis (module Spec : MCPSpec) From ba6eaa170392bde34e82ac84d533a4e837f48d74 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Wed, 14 Jun 2023 13:24:00 +0200 Subject: [PATCH 0121/1312] Make ocamldoc comment --- src/analyses/termination_new.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index 7a7f046c39..9c87a4038e 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -8,7 +8,7 @@ exception PreProcessing of string let loop_counters : varinfo list ref = ref [] -(* Contains the locations of the upjumping gotos *) +(** Contains the locations of the upjumping gotos *) let upjumping_gotos : location list ref = ref [] let loop_exit : varinfo ref = ref (makeVarinfo false "-error" Cil.intType) From 8f457230d7cee30965a124933963f8908b01a28f Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Wed, 14 Jun 2023 13:39:59 +0200 Subject: [PATCH 0122/1312] Remove debug output --- src/analyses/termination_new.ml | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index 9c87a4038e..19198c4ca9 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -56,12 +56,10 @@ struct match lval, rval with (Var x, NoOffset), _ when is_loop_counter_var x -> (* Assume that the following loop does not terminate *) - if not (no_upjumping_gotos ()) then printf "\n4 problem\n"; D.add x false ctx.local | (Var y, NoOffset), Lval (Var x, NoOffset) when is_loop_exit_indicator y -> (* Loop exit: Check whether loop counter variable is bounded *) let is_bounded = check_bounded ctx x in - if not (no_upjumping_gotos ()) then printf "\n5 problem\n"; D.add x is_bounded ctx.local | _ -> ctx.local From f66d46fac00e6ecbe4d7dbf4cf4367b35e877e74 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Wed, 14 Jun 2023 14:03:26 +0200 Subject: [PATCH 0123/1312] Implement preliminary query function --- src/analyses/termination_new.ml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index 19198c4ca9..503b3f2d51 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -59,18 +59,26 @@ struct D.add x false ctx.local | (Var y, NoOffset), Lval (Var x, NoOffset) when is_loop_exit_indicator y -> (* Loop exit: Check whether loop counter variable is bounded *) + (* TODO: Move *) let is_bounded = check_bounded ctx x in D.add x is_bounded ctx.local | _ -> ctx.local + let special ctx (lval : lval option) (f : varinfo) (arglist : exp list) = + (* TODO: Implement check for our special loop exit indicator function *) + ctx.local + (** Provides information to Goblint *) + (* TODO: Consider gotos and recursion *) let query ctx (type a) (q: a Queries.t): a Queries.result = let open Queries in match q with - | Queries.MustTermLoop v when check_bounded ctx v -> - true (* TODO should we use the check_bounded function? *) + | Queries.MustTermLoop v -> + (match D.find_opt v ctx.local with + Some b -> b + | None -> Result.top q) | Queries.MustTermProg -> - true (*TODO check if all values in the domain are true -> true *) + D.for_all (fun loop term_info -> term_info) ctx.local | _ -> Result.top q end From d0b42d1b5b57fc7c2e0f884ea3ef2c4ea86db8f7 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Wed, 14 Jun 2023 14:20:10 +0200 Subject: [PATCH 0124/1312] Remove function to_interval --- src/cdomains/intDomain.ml | 25 ------------------------- src/cdomains/intDomain.mli | 13 +++++-------- src/domains/valueDomainQueries.ml | 1 - 3 files changed, 5 insertions(+), 34 deletions(-) diff --git a/src/cdomains/intDomain.ml b/src/cdomains/intDomain.ml index 9932e2fa67..df0a4c0507 100644 --- a/src/cdomains/intDomain.ml +++ b/src/cdomains/intDomain.ml @@ -171,7 +171,6 @@ sig val equal_to: int_t -> t -> [`Eq | `Neq | `Top] val to_bool: t -> bool option - val to_interval: t -> (int_t * int_t) option val to_excl_list: t -> (int_t list * (int64 * int64)) option val of_excl_list: Cil.ikind -> int_t list -> t val is_excl_list: t -> bool @@ -350,8 +349,6 @@ struct with Failure _ -> top_of ik - let to_interval x = failwith "Not implemented!" (* FIXME *) - let starting ?(suppress_ovwarn=false) ik x = try Old.starting ~suppress_ovwarn ik (BI.to_int64 x) with Failure _ -> top_of ik let ending ?(suppress_ovwarn=false) ik x = @@ -431,7 +428,6 @@ struct let of_excl_list ikind is = {v = I.of_excl_list ikind is; ikind} let is_excl_list x = I.is_excl_list x.v let to_incl_list x = I.to_incl_list x.v - let to_interval x = I.to_interval x.v let of_interval ?(suppress_ovwarn=false) ikind (lb,ub) = {v = I.of_interval ~suppress_ovwarn ikind (lb,ub); ikind} let of_congruence ikind (c,m) = {v = I.of_congruence ikind (c,m); ikind} let starting ?(suppress_ovwarn=false) ikind i = {v = I.starting ~suppress_ovwarn ikind i; ikind} @@ -712,7 +708,6 @@ struct (* TODO: change to_int signature so it returns a big_int *) let to_int x = Option.bind x (IArith.to_int) - let to_interval = Fun.id let of_interval ?(suppress_ovwarn=false) ik (x,y) = norm ~suppress_ovwarn ik @@ Some (x,y) let of_int ik (x: int_t) = of_interval ik (x,x) let zero = Some IArith.zero @@ -1270,10 +1265,6 @@ struct let of_bool _ = function true -> one | false -> zero - let to_interval l = match minimal l, maximal l with - | Some x, Some y -> Some (x, y) - | _ -> None - let of_interval ?(suppress_ovwarn=false) ik (x,y) = norm_interval ~suppress_ovwarn ~cast:false ik (x,y) let of_int ik (x: int_t) = of_interval ik (x, x) @@ -1648,7 +1639,6 @@ struct let to_bool x = Some (to_bool' x) let of_int x = x let to_int x = Some x - let to_interval x = Some (x, x) let neg = Ints_t.neg let add = Ints_t.add (* TODO: signed overflow is undefined behavior! *) @@ -1723,7 +1713,6 @@ struct let of_excl_list ik x = top_of ik let is_excl_list x = false let to_incl_list x = None - let to_interval x = None let of_interval ?(suppress_ovwarn=false) ik x = top_of ik let of_congruence ik x = top_of ik let starting ?(suppress_ovwarn=false) ikind x = top_of ikind @@ -1807,10 +1796,6 @@ struct | `Bot, `Bot -> `Bot | _ -> `Top - let to_interval = function - | `Lifted x -> Base.to_interval x - | _ -> None - let neg = lift1 Base.neg let add = lift2 Base.add let sub = lift2 Base.sub @@ -2152,9 +2137,6 @@ struct let top_bool = `Excluded (S.empty (), R.of_interval range_ikind (0L, 1L)) let of_interval ?(suppress_ovwarn=false) ik (x,y) = if BigInt.compare x y = 0 then of_int ik x else top_of ik - let to_interval l = match minimal l, maximal l with - | Some x, Some y -> Some (x, y) - | _ -> None let starting ?(suppress_ovwarn=false) ikind x = if BigInt.compare x BigInt.zero > 0 then not_zero ikind else top_of ikind let ending ?(suppress_ovwarn=false) ikind x = if BigInt.compare x BigInt.zero < 0 then not_zero ikind else top_of ikind @@ -2418,7 +2400,6 @@ struct let to_bool x = Some x let of_int x = x = Int64.zero let to_int x = if x then None else Some Int64.zero - let to_interval x = if x then None else Some (Int64.zero, Int64.zero) let neg x = x let add x y = x || y @@ -2548,9 +2529,6 @@ module Enums : S with type int_t = BigInt.t = struct let of_int ikind x = cast_to ikind (Inc (BISet.singleton x)) let of_interval ?(suppress_ovwarn=false) ik (x,y) = if x = y then of_int ik x else top_of ik - let to_interval l = match minimal l, maximal l with - | Some x, Some y -> Some (x, y) - | _ -> None let join ik = curry @@ function | Inc x, Inc y -> Inc (BISet.union x y) @@ -3286,7 +3264,6 @@ struct let project ik p t = t - let to_interval x = failwith "Not implemented!" (* FIXME *) end module SOverflowLifter (D : S) : SOverflow with type int_t = D.int_t and type t = D.t = struct @@ -3519,8 +3496,6 @@ module IntDomTupleImpl = struct let flat f x = match to_list_some x with [] -> None | xs -> Some (f xs) - let to_interval (_, i, _, _, _) = Option.bind i I2.to_interval - let to_excl_list x = let merge ps = let (vs, rs) = List.split ps in diff --git a/src/cdomains/intDomain.mli b/src/cdomains/intDomain.mli index 5a6a4d7c04..c7b59e4c23 100644 --- a/src/cdomains/intDomain.mli +++ b/src/cdomains/intDomain.mli @@ -187,9 +187,6 @@ sig (** Give a boolean interpretation of an abstract value if possible, otherwise * don't return anything.*) - val to_interval: t -> (int_t * int_t) option - (** Gives an interval interpretation if possible. *) - val to_excl_list: t -> (int_t list * (int64 * int64)) option (** Gives a list representation of the excluded values from included range of bits if possible. *) @@ -235,7 +232,7 @@ sig val invariant: Cil.exp -> t -> Invariant.t end (** Interface of IntDomain implementations that do not take ikinds for arithmetic operations yet. - TODO: Should be ported to S in the future. *) + TODO: Should be ported to S in the future. *) module type S = sig @@ -415,10 +412,10 @@ module IntervalSetFunctor(Ints_t : IntOps.IntOps): SOverflow with type int_t = I module Interval32 :Y with (* type t = (IntOps.Int64Ops.t * IntOps.Int64Ops.t) option and *) type int_t = IntOps.Int64Ops.t module BigInt: - sig - include Printable.S with type t = Z.t (* TODO: why doesn't this have a more useful signature like IntOps.BigIntOps? *) - val cast_to: Cil.ikind -> Z.t -> Z.t - end +sig + include Printable.S with type t = Z.t (* TODO: why doesn't this have a more useful signature like IntOps.BigIntOps? *) + val cast_to: Cil.ikind -> Z.t -> Z.t +end module Interval : SOverflow with type int_t = IntOps.BigIntOps.t diff --git a/src/domains/valueDomainQueries.ml b/src/domains/valueDomainQueries.ml index 2f28f729ea..d366e6dda3 100644 --- a/src/domains/valueDomainQueries.ml +++ b/src/domains/valueDomainQueries.ml @@ -34,7 +34,6 @@ struct let to_int x = unlift_opt I.to_int x let to_bool x = unlift_opt I.to_bool x - let to_interval x = unlift_opt I.to_interval x let is_top_of ik = unlift_is (I.is_top_of ik) From ffd6d4c1234bfa352655493370128044536ec8b5 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Wed, 14 Jun 2023 14:27:28 +0200 Subject: [PATCH 0125/1312] Add dummy finalize function --- src/analyses/termination_new.ml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index 503b3f2d51..ce0b36121e 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -48,6 +48,8 @@ struct let startstate _ = D.bot () let exitstate = startstate (* TODO *) + let finalize () = () (* TODO *) + (** Provides some default implementations *) include Analyses.IdentitySpec From 090e704e72f2e9ba4626f9ca53505f7e03671c9e Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Wed, 14 Jun 2023 16:22:52 +0200 Subject: [PATCH 0126/1312] WIP on final loop termination report --- src/analyses/termination_new.ml | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index ce0b36121e..44b0c7fb5e 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -37,22 +37,31 @@ let check_bounded ctx varinfo = | `Lifted v -> not (is_top_of (ikind v) v) | `Bot -> raise (PreProcessing "Loop variable is Bot") +module FunContextV : Analyses.SpecSysVar = +struct + include Printable.Prod (CilType.Fundec) (CilType.Fundec) (* TODO *) + include Analyses.StdV +end + + module Spec : Analyses.MCPSpec = struct + (** Provides some default implementations *) + include Analyses.IdentitySpec + let name () = "termination" module D = MapDomain.MapBot (Basetype.Variables) (BoolDomain.MustBool) module C = D + module V = FunContextV + (* TODO *) let startstate _ = D.bot () let exitstate = startstate (* TODO *) let finalize () = () (* TODO *) - (** Provides some default implementations *) - include Analyses.IdentitySpec - let assign ctx (lval : lval) (rval : exp) = (* Detect assignment to loop counter variable *) match lval, rval with From 82bb72dd579517d4357488185080beaea13e5fa0 Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Thu, 15 Jun 2023 12:32:00 +0200 Subject: [PATCH 0127/1312] Test case term:20 fixed --- tests/regression/80-termination/20-rand-nonterminating.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/regression/80-termination/20-rand-nonterminating.c b/tests/regression/80-termination/20-rand-nonterminating.c index 6639e5bc76..88f6f50bd4 100644 --- a/tests/regression/80-termination/20-rand-nonterminating.c +++ b/tests/regression/80-termination/20-rand-nonterminating.c @@ -11,7 +11,7 @@ int main() if (rand()) { // Loop inside the if part - for (int i = 1; i <= 0; i++) // NOTERM + for (int i = 1; i >= 0; i++) // NOTERM { printf("Loop inside if part: %d\n", i); } @@ -20,10 +20,9 @@ int main() { // Loop inside the else part int j = 1; - while (j < 5) // NOTERM + while (j > 0) // NOTERM { printf("Loop inside else part: %d\n", j); - j++; } } From 37848d8eec23ea6e6d2423a279f80e2a55fce43c Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Thu, 15 Jun 2023 13:41:05 +0200 Subject: [PATCH 0128/1312] Test cases for term with polyhedra --- .../regression/80-termination/09-complex-for-loop-terminating.c | 2 +- tests/regression/80-termination/10-complex-loop-terminating.c | 2 +- tests/regression/80-termination/11-loopless-termination.c | 2 +- .../80-termination/15-complex-loop-combination-terminating.c | 2 +- tests/regression/80-termination/17-goto-terminating.c | 2 +- tests/regression/80-termination/18-goto-nonterminating.c | 2 +- tests/regression/80-termination/19-rand-terminating.c | 2 +- tests/regression/80-termination/20-rand-nonterminating.c | 2 +- .../regression/80-termination/21-no-exit-on-rand-unproofable.c | 2 +- tests/regression/80-termination/22-exit-on-rand-unproofable.c | 2 +- tests/regression/80-termination/23-exit-on-rand-terminating.c | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/regression/80-termination/09-complex-for-loop-terminating.c b/tests/regression/80-termination/09-complex-for-loop-terminating.c index 508b31500c..ed28fa9b43 100644 --- a/tests/regression/80-termination/09-complex-for-loop-terminating.c +++ b/tests/regression/80-termination/09-complex-for-loop-terminating.c @@ -1,4 +1,4 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/80-termination/10-complex-loop-terminating.c b/tests/regression/80-termination/10-complex-loop-terminating.c index 9d5cd4b928..3a19f17bee 100644 --- a/tests/regression/80-termination/10-complex-loop-terminating.c +++ b/tests/regression/80-termination/10-complex-loop-terminating.c @@ -1,4 +1,4 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/80-termination/11-loopless-termination.c b/tests/regression/80-termination/11-loopless-termination.c index b118e65e35..7aeed0145d 100644 --- a/tests/regression/80-termination/11-loopless-termination.c +++ b/tests/regression/80-termination/11-loopless-termination.c @@ -1,4 +1,4 @@ -// TERM +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/80-termination/15-complex-loop-combination-terminating.c b/tests/regression/80-termination/15-complex-loop-combination-terminating.c index 1ea228ae55..e5383aed66 100644 --- a/tests/regression/80-termination/15-complex-loop-combination-terminating.c +++ b/tests/regression/80-termination/15-complex-loop-combination-terminating.c @@ -1,4 +1,4 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/80-termination/17-goto-terminating.c b/tests/regression/80-termination/17-goto-terminating.c index 10aa729837..dcf72552bc 100644 --- a/tests/regression/80-termination/17-goto-terminating.c +++ b/tests/regression/80-termination/17-goto-terminating.c @@ -1,4 +1,4 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/80-termination/18-goto-nonterminating.c b/tests/regression/80-termination/18-goto-nonterminating.c index dbb7a3df59..672128e009 100644 --- a/tests/regression/80-termination/18-goto-nonterminating.c +++ b/tests/regression/80-termination/18-goto-nonterminating.c @@ -1,4 +1,4 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/80-termination/19-rand-terminating.c b/tests/regression/80-termination/19-rand-terminating.c index 1d226f0df2..879ae3748a 100644 --- a/tests/regression/80-termination/19-rand-terminating.c +++ b/tests/regression/80-termination/19-rand-terminating.c @@ -1,4 +1,4 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include #include #include diff --git a/tests/regression/80-termination/20-rand-nonterminating.c b/tests/regression/80-termination/20-rand-nonterminating.c index 88f6f50bd4..27c3f2c388 100644 --- a/tests/regression/80-termination/20-rand-nonterminating.c +++ b/tests/regression/80-termination/20-rand-nonterminating.c @@ -1,4 +1,4 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include #include #include diff --git a/tests/regression/80-termination/21-no-exit-on-rand-unproofable.c b/tests/regression/80-termination/21-no-exit-on-rand-unproofable.c index 4510ac1bb7..0edafe0f65 100644 --- a/tests/regression/80-termination/21-no-exit-on-rand-unproofable.c +++ b/tests/regression/80-termination/21-no-exit-on-rand-unproofable.c @@ -1,4 +1,4 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/80-termination/22-exit-on-rand-unproofable.c b/tests/regression/80-termination/22-exit-on-rand-unproofable.c index 97b18ed5fc..5c270f3b2a 100644 --- a/tests/regression/80-termination/22-exit-on-rand-unproofable.c +++ b/tests/regression/80-termination/22-exit-on-rand-unproofable.c @@ -1,4 +1,4 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/80-termination/23-exit-on-rand-terminating.c b/tests/regression/80-termination/23-exit-on-rand-terminating.c index 5e2be62637..f793275b1f 100644 --- a/tests/regression/80-termination/23-exit-on-rand-terminating.c +++ b/tests/regression/80-termination/23-exit-on-rand-terminating.c @@ -1,4 +1,4 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() From ff5755833c8539dd48bb7fd49afdc8b0fedf5784 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Fri, 16 Jun 2023 11:13:51 +0200 Subject: [PATCH 0129/1312] filling of global constraint happens in combine_env --- output.txt | 2262 ---------------------------------- runningGob.sh | 2 +- src/framework/analyses.ml | 18 +- src/framework/constraints.ml | 171 +-- src/framework/control.ml | 3 +- 5 files changed, 33 insertions(+), 2423 deletions(-) diff --git a/output.txt b/output.txt index 6ccb110e96..e69de29bb2 100644 --- a/output.txt +++ b/output.txt @@ -1,2262 +0,0 @@ -2023-06-09 18:19:45 -'./goblint' '-v' 'tests/regression/55-loop-unrolling/01-simple-cases.c' '--set' 'ana.activated[+]' 'termination' '--enable' 'warn.debug' '--set' 'ana.activated[+]' 'apron' '--enable' 'ana.int.interval' '--set' 'ana.apron.domain' 'polyhedra' '--enable' 'justcil' -Custom include dirs: - 1. /home/johanna/goblint/goblint-analyzer/lib/linux/stub/include (exists=true) - 2. /home/johanna/goblint/goblint-analyzer/lib/sv-comp/stub/include (exists=false) - 3. /home/johanna/goblint/goblint-analyzer/lib/libc/stub/include (exists=true) - 4. /home/johanna/goblint/goblint-analyzer/lib/goblint/stub/include (exists=false) - 5. /home/johanna/goblint/goblint-analyzer/lib/linux/runtime/include (exists=false) - 6. /home/johanna/goblint/goblint-analyzer/lib/sv-comp/runtime/include (exists=false) - 7. /home/johanna/goblint/goblint-analyzer/lib/libc/runtime/include (exists=false) - 8. /home/johanna/goblint/goblint-analyzer/lib/goblint/runtime/include (exists=true) - 9. /home/johanna/goblint/goblint-analyzer/lib/linux/stub/src (exists=false) - 10. /home/johanna/goblint/goblint-analyzer/lib/sv-comp/stub/src (exists=true) - 11. /home/johanna/goblint/goblint-analyzer/lib/libc/stub/src (exists=true) - 12. /home/johanna/goblint/goblint-analyzer/lib/goblint/stub/src (exists=false) -Preprocessing files. -Preprocessor cpp: is_bad=false -'cpp' '-I' '/home/johanna/goblint/goblint-analyzer/lib/linux/stub/include' '-I' '/home/johanna/goblint/goblint-analyzer/lib/libc/stub/include' '-I' '/home/johanna/goblint/goblint-analyzer/lib/goblint/runtime/include' '-I' '/home/johanna/goblint/goblint-analyzer/lib/sv-comp/stub/src' '-I' '/home/johanna/goblint/goblint-analyzer/lib/libc/stub/src' '/home/johanna/goblint/goblint-analyzer/lib/libc/stub/src/pthread.c' '-o' '.goblint/preprocessed/pthread.i' -'cpp' '-I' '/home/johanna/goblint/goblint-analyzer/lib/linux/stub/include' '-I' '/home/johanna/goblint/goblint-analyzer/lib/libc/stub/include' '-I' '/home/johanna/goblint/goblint-analyzer/lib/goblint/runtime/include' '-I' '/home/johanna/goblint/goblint-analyzer/lib/sv-comp/stub/src' '-I' '/home/johanna/goblint/goblint-analyzer/lib/libc/stub/src' '/home/johanna/goblint/goblint-analyzer/lib/libc/stub/src/stdlib.c' '-o' '.goblint/preprocessed/stdlib.i' -'cpp' '-I' '/home/johanna/goblint/goblint-analyzer/lib/linux/stub/include' '-I' '/home/johanna/goblint/goblint-analyzer/lib/libc/stub/include' '-I' '/home/johanna/goblint/goblint-analyzer/lib/goblint/runtime/include' '-I' '/home/johanna/goblint/goblint-analyzer/lib/sv-comp/stub/src' '-I' '/home/johanna/goblint/goblint-analyzer/lib/libc/stub/src' 'tests/regression/55-loop-unrolling/01-simple-cases.c' '-o' '.goblint/preprocessed/01-simple-cases.i' -Parsing files. -Constructors: -Adding constructors to: main -/* Generated by CIL v. 2.0.1-48-g4df989f */ -/* print_CIL_Input is true */ - -#line 31 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned char __u_char; -#line 32 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned short __u_short; -#line 33 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned int __u_int; -#line 34 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __u_long; -#line 37 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef signed char __int8_t; -#line 38 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned char __uint8_t; -#line 39 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef short __int16_t; -#line 40 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned short __uint16_t; -#line 41 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef int __int32_t; -#line 42 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned int __uint32_t; -#line 44 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __int64_t; -#line 45 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __uint64_t; -#line 52 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef __int8_t __int_least8_t; -#line 53 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef __uint8_t __uint_least8_t; -#line 54 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef __int16_t __int_least16_t; -#line 55 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef __uint16_t __uint_least16_t; -#line 56 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef __int32_t __int_least32_t; -#line 57 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef __uint32_t __uint_least32_t; -#line 58 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef __int64_t __int_least64_t; -#line 59 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef __uint64_t __uint_least64_t; -#line 63 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __quad_t; -#line 64 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __u_quad_t; -#line 72 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __intmax_t; -#line 73 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __uintmax_t; -#line 145 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __dev_t; -#line 146 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned int __uid_t; -#line 147 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned int __gid_t; -#line 148 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __ino_t; -#line 149 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __ino64_t; -#line 150 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned int __mode_t; -#line 151 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __nlink_t; -#line 152 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __off_t; -#line 153 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __off64_t; -#line 154 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef int __pid_t; -#line 155 "/usr/include/x86_64-linux-gnu/bits/types.h" -struct __anonstruct___fsid_t_109580352 { - int __val[2] ; -}; -#line 155 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef struct __anonstruct___fsid_t_109580352 __fsid_t; -#line 156 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __clock_t; -#line 157 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __rlim_t; -#line 158 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __rlim64_t; -#line 159 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned int __id_t; -#line 160 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __time_t; -#line 161 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned int __useconds_t; -#line 162 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __suseconds_t; -#line 163 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __suseconds64_t; -#line 165 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef int __daddr_t; -#line 166 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef int __key_t; -#line 169 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef int __clockid_t; -#line 172 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef void *__timer_t; -#line 175 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __blksize_t; -#line 180 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __blkcnt_t; -#line 181 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __blkcnt64_t; -#line 184 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __fsblkcnt_t; -#line 185 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __fsblkcnt64_t; -#line 188 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __fsfilcnt_t; -#line 189 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __fsfilcnt64_t; -#line 192 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __fsword_t; -#line 194 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __ssize_t; -#line 197 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __syscall_slong_t; -#line 199 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __syscall_ulong_t; -#line 203 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef __off64_t __loff_t; -#line 204 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef char *__caddr_t; -#line 207 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __intptr_t; -#line 210 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned int __socklen_t; -#line 215 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef int __sig_atomic_t; -#line 209 "/usr/lib/gcc/x86_64-linux-gnu/11/include/stddef.h" -typedef unsigned long size_t; -#line 10 "/usr/include/x86_64-linux-gnu/bits/types/time_t.h" -typedef __time_t time_t; -#line 11 "/usr/include/x86_64-linux-gnu/bits/types/struct_timespec.h" -struct timespec { - __time_t tv_sec ; - __syscall_slong_t tv_nsec ; -}; -#line 38 "/usr/include/sched.h" -typedef __pid_t pid_t; -#line 23 "/usr/include/x86_64-linux-gnu/bits/types/struct_sched_param.h" -struct sched_param { - int sched_priority ; -}; -#line 32 "/usr/include/x86_64-linux-gnu/bits/cpu-set.h" -typedef unsigned long __cpu_mask; -#line 39 "/usr/include/x86_64-linux-gnu/bits/cpu-set.h" -struct __anonstruct_cpu_set_t_826868708 { - __cpu_mask __bits[1024UL / (8UL * sizeof(__cpu_mask ))] ; -}; -#line 39 "/usr/include/x86_64-linux-gnu/bits/cpu-set.h" -typedef struct __anonstruct_cpu_set_t_826868708 cpu_set_t; -#line 7 "/usr/include/x86_64-linux-gnu/bits/types/clock_t.h" -typedef __clock_t clock_t; -#line 7 "/usr/include/x86_64-linux-gnu/bits/types/struct_tm.h" -struct tm { - int tm_sec ; - int tm_min ; - int tm_hour ; - int tm_mday ; - int tm_mon ; - int tm_year ; - int tm_wday ; - int tm_yday ; - int tm_isdst ; - long tm_gmtoff ; - char const *tm_zone ; -}; -#line 7 "/usr/include/x86_64-linux-gnu/bits/types/clockid_t.h" -typedef __clockid_t clockid_t; -#line 7 "/usr/include/x86_64-linux-gnu/bits/types/timer_t.h" -typedef __timer_t timer_t; -#line 8 "/usr/include/x86_64-linux-gnu/bits/types/struct_itimerspec.h" -struct itimerspec { - struct timespec it_interval ; - struct timespec it_value ; -}; -#line 49 "/usr/include/time.h" -struct sigevent ; -#line 27 "/usr/include/x86_64-linux-gnu/bits/types/__locale_t.h" -struct __locale_data ; -#line 27 "/usr/include/x86_64-linux-gnu/bits/types/__locale_t.h" -struct __locale_struct { - struct __locale_data *__locales[13] ; - unsigned short const *__ctype_b ; - int const *__ctype_tolower ; - int const *__ctype_toupper ; - char const *__names[13] ; -}; -#line 41 "/usr/include/x86_64-linux-gnu/bits/types/__locale_t.h" -typedef struct __locale_struct *__locale_t; -#line 24 "/usr/include/x86_64-linux-gnu/bits/types/locale_t.h" -typedef __locale_t locale_t; -#line 25 "/usr/include/x86_64-linux-gnu/bits/atomic_wide_counter.h" -struct __anonstruct___value32_817613185 { - unsigned int __low ; - unsigned int __high ; -}; -#line 25 "/usr/include/x86_64-linux-gnu/bits/atomic_wide_counter.h" -union __anonunion___atomic_wide_counter_643133811 { - unsigned long long __value64 ; - struct __anonstruct___value32_817613185 __value32 ; -}; -#line 25 "/usr/include/x86_64-linux-gnu/bits/atomic_wide_counter.h" -typedef union __anonunion___atomic_wide_counter_643133811 __atomic_wide_counter; -#line 51 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" -struct __pthread_internal_list { - struct __pthread_internal_list *__prev ; - struct __pthread_internal_list *__next ; -}; -#line 51 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" -typedef struct __pthread_internal_list __pthread_list_t; -#line 57 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" -struct __pthread_internal_slist { - struct __pthread_internal_slist *__next ; -}; -#line 57 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" -typedef struct __pthread_internal_slist __pthread_slist_t; -#line 22 "/usr/include/x86_64-linux-gnu/bits/struct_mutex.h" -struct __pthread_mutex_s { - int __lock ; - unsigned int __count ; - int __owner ; - unsigned int __nusers ; - int __kind ; - short __spins ; - short __elision ; - __pthread_list_t __list ; -}; -#line 23 "/usr/include/x86_64-linux-gnu/bits/struct_rwlock.h" -struct __pthread_rwlock_arch_t { - unsigned int __readers ; - unsigned int __writers ; - unsigned int __wrphase_futex ; - unsigned int __writers_futex ; - unsigned int __pad3 ; - unsigned int __pad4 ; - int __cur_writer ; - int __shared ; - signed char __rwelision ; - unsigned char __pad1[7] ; - unsigned long __pad2 ; - unsigned int __flags ; -}; -#line 94 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" -struct __pthread_cond_s { - __atomic_wide_counter __wseq ; - __atomic_wide_counter __g1_start ; - unsigned int __g_refs[2] ; - unsigned int __g_size[2] ; - unsigned int __g1_orig_size ; - unsigned int __wrefs ; - unsigned int __g_signals[2] ; -}; -#line 105 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" -typedef unsigned int __tss_t; -#line 106 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" -typedef unsigned long __thrd_t; -#line 108 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" -struct __anonstruct___once_flag_826868709 { - int __data ; -}; -#line 108 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" -typedef struct __anonstruct___once_flag_826868709 __once_flag; -#line 27 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -typedef unsigned long pthread_t; -#line 32 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -union __anonunion_pthread_mutexattr_t_488594144 { - char __size[4] ; - int __align ; -}; -#line 32 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -typedef union __anonunion_pthread_mutexattr_t_488594144 pthread_mutexattr_t; -#line 41 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -union __anonunion_pthread_condattr_t_488594145 { - char __size[4] ; - int __align ; -}; -#line 41 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -typedef union __anonunion_pthread_condattr_t_488594145 pthread_condattr_t; -#line 49 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -typedef unsigned int pthread_key_t; -#line 53 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -typedef int pthread_once_t; -#line 56 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -union pthread_attr_t { - char __size[56] ; - long __align ; -}; -#line 62 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -typedef union pthread_attr_t pthread_attr_t; -#line 67 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -union __anonunion_pthread_mutex_t_335460617 { - struct __pthread_mutex_s __data ; - char __size[40] ; - long __align ; -}; -#line 67 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -typedef union __anonunion_pthread_mutex_t_335460617 pthread_mutex_t; -#line 75 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -union __anonunion_pthread_cond_t_951761805 { - struct __pthread_cond_s __data ; - char __size[48] ; - long long __align ; -}; -#line 75 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -typedef union __anonunion_pthread_cond_t_951761805 pthread_cond_t; -#line 86 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -union __anonunion_pthread_rwlock_t_656928968 { - struct __pthread_rwlock_arch_t __data ; - char __size[56] ; - long __align ; -}; -#line 86 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -typedef union __anonunion_pthread_rwlock_t_656928968 pthread_rwlock_t; -#line 93 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -union __anonunion_pthread_rwlockattr_t_145707745 { - char __size[8] ; - long __align ; -}; -#line 93 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -typedef union __anonunion_pthread_rwlockattr_t_145707745 pthread_rwlockattr_t; -#line 103 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -typedef int volatile pthread_spinlock_t; -#line 108 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -union __anonunion_pthread_barrier_t_145707746 { - char __size[32] ; - long __align ; -}; -#line 108 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -typedef union __anonunion_pthread_barrier_t_145707746 pthread_barrier_t; -#line 114 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -union __anonunion_pthread_barrierattr_t_951761806 { - char __size[4] ; - int __align ; -}; -#line 114 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -typedef union __anonunion_pthread_barrierattr_t_951761806 pthread_barrierattr_t; -#line 31 "/usr/include/x86_64-linux-gnu/bits/setjmp.h" -typedef long __jmp_buf[8]; -#line 5 "/usr/include/x86_64-linux-gnu/bits/types/__sigset_t.h" -struct __anonstruct___sigset_t_764561023 { - unsigned long __val[1024UL / (8UL * sizeof(unsigned long ))] ; -}; -#line 5 "/usr/include/x86_64-linux-gnu/bits/types/__sigset_t.h" -typedef struct __anonstruct___sigset_t_764561023 __sigset_t; -#line 26 "/usr/include/x86_64-linux-gnu/bits/types/struct___jmp_buf_tag.h" -struct __jmp_buf_tag { - __jmp_buf __jmpbuf ; - int __mask_was_saved ; - __sigset_t __saved_mask ; -}; -#line 37 "/usr/include/pthread.h" -enum __anonenum_34415463 { - PTHREAD_CREATE_JOINABLE = 0, - PTHREAD_CREATE_DETACHED = 1 -} ; -#line 47 -enum __anonenum_508643754 { - PTHREAD_MUTEX_TIMED_NP = 0, - PTHREAD_MUTEX_RECURSIVE_NP = 1, - PTHREAD_MUTEX_ERRORCHECK_NP = 2, - PTHREAD_MUTEX_ADAPTIVE_NP = 3, - PTHREAD_MUTEX_NORMAL = 0, - PTHREAD_MUTEX_RECURSIVE = 1, - PTHREAD_MUTEX_ERRORCHECK = 2, - PTHREAD_MUTEX_DEFAULT = 0 -} ; -#line 69 -enum __anonenum_931900394 { - PTHREAD_MUTEX_STALLED = 0, - PTHREAD_MUTEX_STALLED_NP = 0, - PTHREAD_MUTEX_ROBUST = 1, - PTHREAD_MUTEX_ROBUST_NP = 1 -} ; -#line 81 -enum __anonenum_205214487 { - PTHREAD_PRIO_NONE = 0, - PTHREAD_PRIO_INHERIT = 1, - PTHREAD_PRIO_PROTECT = 2 -} ; -#line 104 -enum __anonenum_25043950 { - PTHREAD_RWLOCK_PREFER_READER_NP = 0, - PTHREAD_RWLOCK_PREFER_WRITER_NP = 1, - PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP = 2, - PTHREAD_RWLOCK_DEFAULT_NP = 0 -} ; -#line 124 -enum __anonenum_436439511 { - PTHREAD_INHERIT_SCHED = 0, - PTHREAD_EXPLICIT_SCHED = 1 -} ; -#line 134 -enum __anonenum_998661166 { - PTHREAD_SCOPE_SYSTEM = 0, - PTHREAD_SCOPE_PROCESS = 1 -} ; -#line 144 -enum __anonenum_146137331 { - PTHREAD_PROCESS_PRIVATE = 0, - PTHREAD_PROCESS_SHARED = 1 -} ; -#line 159 "/usr/include/pthread.h" -struct _pthread_cleanup_buffer { - void (*__routine)(void * ) ; - void *__arg ; - int __canceltype ; - struct _pthread_cleanup_buffer *__prev ; -}; -#line 168 -enum __anonenum_53396917 { - PTHREAD_CANCEL_ENABLE = 0, - PTHREAD_CANCEL_DISABLE = 1 -} ; -#line 175 -enum __anonenum_904563783 { - PTHREAD_CANCEL_DEFERRED = 0, - PTHREAD_CANCEL_ASYNCHRONOUS = 1 -} ; -#line 538 "/usr/include/pthread.h" -struct __cancel_jmp_buf_tag { - __jmp_buf __cancel_jmp_buf ; - int __mask_was_saved ; -}; -#line 544 "/usr/include/pthread.h" -struct __anonstruct___pthread_unwind_buf_t_530692248 { - struct __cancel_jmp_buf_tag __cancel_jmp_buf[1] ; - void *__pad[4] ; -}; -#line 544 "/usr/include/pthread.h" -typedef struct __anonstruct___pthread_unwind_buf_t_530692248 __attribute__((__aligned__)) __pthread_unwind_buf_t; -#line 557 "/usr/include/pthread.h" -struct __pthread_cleanup_frame { - void (*__cancel_routine)(void * ) ; - void *__cancel_arg ; - int __do_it ; - int __cancel_type ; -}; -#line 143 "/usr/lib/gcc/x86_64-linux-gnu/11/include/stddef.h" -typedef long ptrdiff_t; -#line 321 "/usr/lib/gcc/x86_64-linux-gnu/11/include/stddef.h" -typedef int wchar_t; -#line 415 "/usr/lib/gcc/x86_64-linux-gnu/11/include/stddef.h" -struct __anonstruct_max_align_t_896270833 { - long long __max_align_ll __attribute__((__aligned__(__alignof__(long long )))) ; - long double __max_align_ld __attribute__((__aligned__(__alignof__(long double )))) ; -}; -#line 415 "/usr/lib/gcc/x86_64-linux-gnu/11/include/stddef.h" -typedef struct __anonstruct_max_align_t_896270833 max_align_t; -/* compiler builtin: - void __builtin_va_copy(__builtin_va_list , __builtin_va_list ) ; */ -/* compiler builtin: - void *__builtin_frob_return_address(void * ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_and_and_fetch(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_fetch_and_or(...) ; */ -/* compiler builtin: - int __builtin_popcountll(unsigned long long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_or_fetch(...) ; */ -/* compiler builtin: - float __builtin_atanf(float ) ; */ -/* compiler builtin: - float __attribute__((____vector_size____(16))) __builtin_ia32_addps(float __attribute__((____vector_size____(16))) , - float __attribute__((____vector_size____(16))) ) ; */ -/* compiler builtin: - unsigned long __builtin_strcspn(char const * , char const * ) ; */ -/* compiler builtin: - float __builtin_asinf(float ) ; */ -/* compiler builtin: - float __attribute__((____vector_size____(16))) __builtin_ia32_maxps(float __attribute__((____vector_size____(16))) , - float __attribute__((____vector_size____(16))) ) ; */ -/* compiler builtin: - float __attribute__((____vector_size____(16))) __builtin_ia32_unpckhps(float __attribute__((____vector_size____(16))) , - float __attribute__((____vector_size____(16))) ) ; */ -/* compiler builtin: - double __builtin_acos(double ) ; */ -/* compiler builtin: - int __builtin___sprintf_chk(char * , int , unsigned long , char const * , ...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_nand_fetch_8(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_and(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_xor_fetch_16(...) ; */ -/* compiler builtin: - double __builtin_cosh(double ) ; */ -/* compiler builtin: - float __builtin_tanhf(float ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_nand_16(...) ; */ -/* compiler builtin: - void *__builtin_mempcpy(void * , void const * , unsigned long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_sub_fetch_1(...) ; */ -/* compiler builtin: - long double __builtin_sqrtl(long double ) ; */ -/* compiler builtin: - int __builtin_parity(unsigned int ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_or(...) ; */ -/* compiler builtin: - long double __builtin_coshl(long double ) ; */ -/* compiler builtin: - long double __builtin_cosl(long double ) ; */ -/* compiler builtin: - float __builtin_cosf(float ) ; */ -/* compiler builtin: - void __sync_synchronize(...) ; */ -/* compiler builtin: - long double __builtin_acosl(long double ) ; */ -/* compiler builtin: - void *__builtin___mempcpy_chk(void * , void const * , unsigned long , unsigned long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_or_and_fetch(...) ; */ -/* compiler builtin: - int __builtin_clz(unsigned int ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_nand_fetch_4(...) ; */ -/* compiler builtin: - double __builtin_log10(double ) ; */ -/* compiler builtin: - char *__builtin___strcat_chk(char * , char const * , unsigned long ) ; */ -/* compiler builtin: - float __builtin_modff(float , float * ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_sub_fetch_4(...) ; */ -/* compiler builtin: - _Bool __atomic_compare_exchange_n(...) ; */ -/* compiler builtin: - double __builtin_sin(double ) ; */ -/* compiler builtin: - double __builtin_frexp(double , int * ) ; */ -/* compiler builtin: - float __builtin_acosf(float ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_add_and_fetch(...) ; */ -/* compiler builtin: - long double __builtin_sinhl(long double ) ; */ -/* compiler builtin: - char *__builtin___stpcpy_chk(char * , char const * , unsigned long ) ; */ -/* compiler builtin: - void __atomic_signal_fence(int ) ; */ -/* compiler builtin: - double __builtin_fabs(double ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_nand_1(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_and_fetch_16(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_fetch_and_nand(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_xor_fetch_8(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_xor_fetch_2(...) ; */ -/* compiler builtin: - void __atomic_thread_fence(int ) ; */ -/* compiler builtin: - void __atomic_store_16(...) ; */ -/* compiler builtin: - void __builtin_va_start(__builtin_va_list ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_and_8(...) ; */ -/* compiler builtin: - short __builtin_bswap16(short ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_and_fetch_2(...) ; */ -/* compiler builtin: - _Bool __atomic_test_and_set(void * , int ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_add_8(...) ; */ -/* compiler builtin: - int __builtin_ctz(unsigned int ) ; */ -/* compiler builtin: - char *__builtin_strpbrk(char const * , char const * ) ; */ -/* compiler builtin: - char *__builtin_strcpy(char * , char const * ) ; */ -/* compiler builtin: - double __builtin_sqrt(double ) ; */ -/* compiler builtin: - __builtin_va_list __builtin_next_arg(void) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_nand(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_exchange_16(...) ; */ -/* compiler builtin: - void __atomic_clear(_Bool * , int ) ; */ -/* compiler builtin: - void __atomic_store(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_add_fetch_2(...) ; */ -/* compiler builtin: - float __builtin_log10f(float ) ; */ -/* compiler builtin: - long double __builtin_fabsl(long double ) ; */ -/* compiler builtin: - long double __builtin_floorl(long double ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_xor_1(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_and_fetch(...) ; */ -/* compiler builtin: - float __builtin_floorf(float ) ; */ -/* compiler builtin: - _Bool __atomic_compare_exchange_4(...) ; */ -/* compiler builtin: - void *__builtin_memcpy(void * , void const * , unsigned long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_sub_and_fetch(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_nand_and_fetch(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_load_16(...) ; */ -/* compiler builtin: - float __attribute__((____vector_size____(16))) __builtin_ia32_subps(float __attribute__((____vector_size____(16))) , - float __attribute__((____vector_size____(16))) ) ; */ -/* compiler builtin: - int __builtin_parityll(unsigned long long ) ; */ -/* compiler builtin: - void __builtin_va_end(__builtin_va_list ) ; */ -/* compiler builtin: - void __builtin_bzero(void * , unsigned long ) ; */ -/* compiler builtin: - _Bool __atomic_always_lock_free(unsigned long , void * ) ; */ -/* compiler builtin: - int __builtin_strncmp(char const * , char const * , unsigned long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_nand_fetch_16(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_xor_and_fetch(...) ; */ -/* compiler builtin: - int __builtin___vsprintf_chk(char * , int , unsigned long , char const * , - __builtin_va_list ) ; */ -/* compiler builtin: - float __builtin_sqrtf(float ) ; */ -/* compiler builtin: - double __builtin_nans(char const * ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_xor_8(...) ; */ -/* compiler builtin: - double __builtin_exp(double ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_exchange_8(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_exchange_1(...) ; */ -/* compiler builtin: - int __builtin_strcmp(char const * , char const * ) ; */ -/* compiler builtin: - float __builtin_ldexpf(float , int ) ; */ -/* compiler builtin: - float __builtin_powif(float , int ) ; */ -/* compiler builtin: - long double __builtin_log10l(long double ) ; */ -/* compiler builtin: - void *__builtin___memmove_chk(void * , void const * , unsigned long , unsigned long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_fetch_and_and(...) ; */ -/* compiler builtin: - void *__builtin_return_address(unsigned int ) ; */ -/* compiler builtin: - void __atomic_feraiseexcept(int ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_and_fetch_4(...) ; */ -/* compiler builtin: - float __builtin_fabsf(float ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_or_fetch_8(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_or_fetch_4(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_or_fetch_1(...) ; */ -/* compiler builtin: - unsigned long __builtin_object_size(void * , int ) ; */ -/* compiler builtin: - void *__builtin_alloca(unsigned long ) ; */ -/* compiler builtin: - int __builtin_va_arg_pack_len(void) ; */ -/* compiler builtin: - long double __builtin_tanl(long double ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_and_2(...) ; */ -/* compiler builtin: - void __sync_lock_release(...) ; */ -/* compiler builtin: - long double __builtin_modfl(long double , long double * ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_nand_8(...) ; */ -/* compiler builtin: - char *__builtin_stpcpy(char * , char const * ) ; */ -/* compiler builtin: - long double __builtin_sinl(long double ) ; */ -/* compiler builtin: - double __builtin_asin(double ) ; */ -/* compiler builtin: - float __builtin_sinhf(float ) ; */ -/* compiler builtin: - int __builtin_ctzl(unsigned long ) ; */ -/* compiler builtin: - long double __builtin_tanhl(long double ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_add(...) ; */ -/* compiler builtin: - long __builtin_bswap64(long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_nand_fetch_1(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_nand_2(...) ; */ -/* compiler builtin: - float __attribute__((____vector_size____(16))) __builtin_ia32_mulps(float __attribute__((____vector_size____(16))) , - float __attribute__((____vector_size____(16))) ) ; */ -/* compiler builtin: - double __builtin_tan(double ) ; */ -/* compiler builtin: - char *__builtin_strncpy(char * , char const * , unsigned long ) ; */ -/* compiler builtin: - float __builtin_inff(void) ; */ -/* compiler builtin: - void *__builtin___memset_chk(void * , int , unsigned long , unsigned long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_load_n(...) ; */ -/* compiler builtin: - double __builtin_huge_val(void) ; */ -/* compiler builtin: - int __builtin_clzl(unsigned long ) ; */ -/* compiler builtin: - _Bool __atomic_compare_exchange_16(...) ; */ -/* compiler builtin: - float __builtin_frexpf(float , int * ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_exchange_n(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_or_1(...) ; */ -/* compiler builtin: - long double __builtin_fmodl(long double ) ; */ -/* compiler builtin: - double __builtin_atan(double ) ; */ -/* compiler builtin: - int __builtin___fprintf_chk(void * , int , char const * , ...) ; */ -/* compiler builtin: - float __builtin_ceilf(float ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_or_8(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_add_1(...) ; */ -/* compiler builtin: - void __builtin_return(void const * ) ; */ -/* compiler builtin: - long double __builtin_asinl(long double ) ; */ -/* compiler builtin: - int __builtin_ffsll(unsigned long long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_sub_1(...) ; */ -/* compiler builtin: - int __builtin_va_arg_pack(void) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_or_4(...) ; */ -/* compiler builtin: - char *__builtin___strncpy_chk(char * , char const * , unsigned long , unsigned long ) ; */ -/* compiler builtin: - double __builtin_powi(double , int ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_load_2(...) ; */ -/* compiler builtin: - char *__builtin_strchr(char * , int ) ; */ -/* compiler builtin: - char *__builtin___strncat_chk(char * , char const * , unsigned long , unsigned long ) ; */ -/* compiler builtin: - void __atomic_store_2(...) ; */ -/* compiler builtin: - long double __builtin_huge_vall(void) ; */ -/* compiler builtin: - int __builtin_ffsl(unsigned long ) ; */ -/* compiler builtin: - int __builtin___vprintf_chk(int , char const * , __builtin_va_list ) ; */ -/* compiler builtin: - float __attribute__((____vector_size____(16))) __builtin_ia32_unpcklps(float __attribute__((____vector_size____(16))) , - float __attribute__((____vector_size____(16))) ) ; */ -/* compiler builtin: - char *__builtin_strncat(char * , char const * , unsigned long ) ; */ -/* compiler builtin: - int __builtin_ctzll(unsigned long long ) ; */ -/* compiler builtin: - void __builtin_stdarg_start(__builtin_va_list ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_exchange_4(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_fetch_and_xor(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_sub_4(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_and_4(...) ; */ -/* compiler builtin: - long double __builtin_frexpl(long double , int * ) ; */ -/* compiler builtin: - _Bool __atomic_compare_exchange(...) ; */ -/* compiler builtin: - float __builtin_tanf(float ) ; */ -/* compiler builtin: - long double __builtin_logl(long double ) ; */ -/* compiler builtin: - void __builtin_va_arg(__builtin_va_list , unsigned long , void * ) ; */ -/* compiler builtin: - long __builtin_expect(long , long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_load_1(...) ; */ -/* compiler builtin: - _Bool __atomic_compare_exchange_1(...) ; */ -/* compiler builtin: - int __builtin___printf_chk(int , char const * , ...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_xor_2(...) ; */ -/* compiler builtin: - int __builtin___vfprintf_chk(void * , int , char const * , __builtin_va_list ) ; */ -/* compiler builtin: - void __builtin_prefetch(void const * , ...) ; */ -/* compiler builtin: - long double __builtin_nansl(char const * ) ; */ -/* compiler builtin: - double __builtin_fmod(double ) ; */ -/* compiler builtin: - void __atomic_load(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_or_fetch_16(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_add_fetch_16(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_val_compare_and_swap(...) ; */ -/* compiler builtin: - void __atomic_store_4(...) ; */ -/* compiler builtin: - double __builtin_tanh(double ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_and_1(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_nand_4(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_add_16(...) ; */ -/* compiler builtin: - void __builtin_unreachable(void) ; */ -/* compiler builtin: - _Bool __atomic_compare_exchange_2(...) ; */ -/* compiler builtin: - long double __builtin_ldexpl(long double , int ) ; */ -/* compiler builtin: - void *__builtin_apply(void (*)() , void * , unsigned long ) ; */ -/* compiler builtin: - float __builtin_sinf(float ) ; */ -/* compiler builtin: - double __builtin_ceil(double ) ; */ -/* compiler builtin: - void __atomic_exchange(...) ; */ -/* compiler builtin: - long double __builtin_powil(long double , int ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_sub_fetch_8(...) ; */ -/* compiler builtin: - long double __builtin_expl(long double ) ; */ -/* compiler builtin: - int __builtin_constant_p(int ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_sub_16(...) ; */ -/* compiler builtin: - double __builtin_log(double ) ; */ -/* compiler builtin: - float __builtin_expf(float ) ; */ -/* compiler builtin: - int __builtin_types_compatible_p(unsigned long , unsigned long ) ; */ -/* compiler builtin: - long double __builtin_atan2l(long double , long double ) ; */ -/* compiler builtin: - void *__builtin_apply_args(void) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_exchange_2(...) ; */ -/* compiler builtin: - float __builtin_logf(float ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_sub_fetch_2(...) ; */ -/* compiler builtin: - unsigned long __builtin_strlen(char const * ) ; */ -/* compiler builtin: - int __builtin_ffs(unsigned int ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_xor_16(...) ; */ -/* compiler builtin: - double __builtin_inf(void) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_or_16(...) ; */ -/* compiler builtin: - void *__builtin___memcpy_chk(void * , void const * , unsigned long , unsigned long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_load_4(...) ; */ -/* compiler builtin: - void __atomic_store_n(...) ; */ -/* compiler builtin: - void __builtin_trap(void) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_add_4(...) ; */ -/* compiler builtin: - int __builtin_parityl(unsigned long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_nand_fetch_2(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_lock_test_and_set(...) ; */ -/* compiler builtin: - unsigned long __builtin_strspn(char const * , char const * ) ; */ -/* compiler builtin: - void __builtin_varargs_start(__builtin_va_list ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_sub_fetch(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_and_16(...) ; */ -/* compiler builtin: - _Bool __atomic_compare_exchange_8(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_nand_fetch(...) ; */ -/* compiler builtin: - double __builtin_nan(char const * ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_load_8(...) ; */ -/* compiler builtin: - int __builtin___snprintf_chk(char * , unsigned long , int , unsigned long , - char const * , ...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_xor_fetch_4(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_sub_2(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_add_fetch(...) ; */ -/* compiler builtin: - long double __builtin_atanl(long double ) ; */ -/* compiler builtin: - int __builtin_clzll(unsigned long long ) ; */ -/* compiler builtin: - float __builtin_huge_valf(void) ; */ -/* compiler builtin: - float __builtin_coshf(float ) ; */ -/* compiler builtin: - float __builtin_nansf(char const * ) ; */ -/* compiler builtin: - void __atomic_store_8(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_xor_fetch(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_fetch_and_add(...) ; */ -/* compiler builtin: - int __builtin___vsnprintf_chk(char * , unsigned long , int , unsigned long , - char const * , __builtin_va_list ) ; */ -/* compiler builtin: - float __builtin_nanf(char const * ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_or_fetch_2(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_add_fetch_1(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_xor(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_add_fetch_8(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_xor_4(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_sub_8(...) ; */ -/* compiler builtin: - _Bool __sync_bool_compare_and_swap(...) ; */ -/* compiler builtin: - double __builtin_atan2(double , double ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __builtin_tgmath(...) ; */ -/* compiler builtin: - int __builtin_popcountl(unsigned long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_and_fetch_1(...) ; */ -/* compiler builtin: - long double __builtin_ceill(long double ) ; */ -/* compiler builtin: - void __atomic_store_1(...) ; */ -/* compiler builtin: - char *__builtin___strcpy_chk(char * , char const * , unsigned long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_xor_fetch_1(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_or_2(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_sub_fetch_16(...) ; */ -/* compiler builtin: - double __builtin_floor(double ) ; */ -/* compiler builtin: - double __builtin_cos(double ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_fetch_and_sub(...) ; */ -/* compiler builtin: - void *__builtin_memset(void * , int , int ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_add_2(...) ; */ -/* compiler builtin: - long double __builtin_nanl(char const * ) ; */ -/* compiler builtin: - float __builtin_atan2f(float , float ) ; */ -/* compiler builtin: - _Bool __atomic_is_lock_free(unsigned long , void * ) ; */ -/* compiler builtin: - int __builtin_popcount(unsigned int ) ; */ -/* compiler builtin: - double __builtin_sinh(double ) ; */ -/* compiler builtin: - void __builtin_bcopy(void const * , void * , unsigned long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_and_fetch_8(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_sub(...) ; */ -/* compiler builtin: - void *__builtin_extract_return_addr(void * ) ; */ -/* compiler builtin: - int __builtin_bswap32(int ) ; */ -/* compiler builtin: - double __builtin_ldexp(double , int ) ; */ -/* compiler builtin: - long double __builtin_infl(void) ; */ -/* compiler builtin: - float __builtin_fmodf(float ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_add_fetch_4(...) ; */ -/* compiler builtin: - void *__builtin_frame_address(unsigned int ) ; */ -#line 1 "lib/goblint/runtime/include/goblint.h" -extern void __goblint_check(int exp ) ; -#line 2 -extern void __goblint_assume(int exp ) ; -#line 3 -extern void __goblint_assert(int exp ) ; -#line 5 -extern void __goblint_assume_join() ; -#line 7 -extern void __goblint_split_begin(int exp ) ; -#line 8 -extern void __goblint_split_end(int exp ) ; -#line 4 "tests/regression/55-loop-unrolling/01-simple-cases.c" -int global ; -#line 8 -void example1(void) ; -#line 9 -void example2(void) ; -#line 10 -void example3(void) ; -#line 11 -void example4(void) ; -#line 12 -void example5(void) ; -#line 13 -void example6(void) ; -#line 14 -void example7(void) ; -#line 15 -void example8(void) ; -#line 16 -void example9(void) ; -#line 17 -void example10(void) ; -#line 6 "tests/regression/55-loop-unrolling/01-simple-cases.c" -int main(void) -{ - - - { -#line 8 - example1(); -#line 9 - example2(); -#line 10 - example3(); -#line 11 - example4(); -#line 12 - example5(); -#line 13 - example6(); -#line 14 - example7(); -#line 15 - example8(); -#line 16 - example9(); -#line 17 - example10(); -#line 18 - return (0); -} -} -#line 22 "tests/regression/55-loop-unrolling/01-simple-cases.c" -void example1(void) -{ - int a[5] ; - int i ; - int term27_5-file_01-simple-cases ; - - { -#line 25 - i = 0; - { -#line 27 - term27_5-file_01-simple-cases = 0; - { -#line 27 - while (1) { - while_continue: /* CIL Label */ ; -#line 27 - if (! (i < 5)) { -#line 27 - goto while_break; - } -#line 27 - term27_5-file_01-simple-cases ++; -#line 28 - a[i] = i; -#line 29 - i ++; - } - while_break: /* CIL Label */ ; - } -#line 27 - __goblint_bounded(term27_5-file_01-simple-cases); - } -#line 32 - __goblint_check(a[0] == 0); -#line 33 - __goblint_check(a[3] == 3); -#line 34 - return; -} -} -#line 37 "tests/regression/55-loop-unrolling/01-simple-cases.c" -void example2(void) -{ - int a[5] ; - int i ; - int term42_5-file_01-simple-cases ; - - { -#line 40 - i = 0; - { -#line 42 - term42_5-file_01-simple-cases = 0; - { -#line 42 - while (1) { - while_continue: /* CIL Label */ ; -#line 43 - a[i] = i; -#line 44 - i ++; -#line 42 - term42_5-file_01-simple-cases ++; -#line 42 - if (! (i <= 5)) { -#line 42 - goto while_break; - } - } - while_break: /* CIL Label */ ; - } -#line 42 - __goblint_bounded(term42_5-file_01-simple-cases); - } -#line 47 - __goblint_check(a[0] == 0); -#line 48 - __goblint_check(a[3] == 3); -#line 49 - return; -} -} -#line 52 "tests/regression/55-loop-unrolling/01-simple-cases.c" -void example3(void) -{ - int a[10] ; - int i ; - int term57_5-file_01-simple-cases ; - - { -#line 55 - i = 0; - { -#line 57 - term57_5-file_01-simple-cases = 0; - { -#line 57 - while (1) { - while_continue: /* CIL Label */ ; -#line 57 - if (! (i < 5)) { -#line 57 - goto while_break; - } -#line 57 - term57_5-file_01-simple-cases ++; -#line 58 - a[i] = i; -#line 59 - i ++; - } - while_break: /* CIL Label */ ; - } -#line 57 - __goblint_bounded(term57_5-file_01-simple-cases); - } -#line 62 - __goblint_check(a[0] == 0); -#line 63 - __goblint_check(a[3] == 0); -#line 64 - __goblint_check(a[7] == 0); -#line 65 - return; -} -} -#line 68 "tests/regression/55-loop-unrolling/01-simple-cases.c" -void example4(void) -{ - int a[10] ; - int i ; - int first_iteration ; - int term74_5-file_01-simple-cases ; - - { -#line 71 - i = 0; -#line 72 - first_iteration = 1; - { -#line 74 - term74_5-file_01-simple-cases = 0; - { -#line 74 - while (1) { - while_continue: /* CIL Label */ ; -#line 74 - if (! (i < 10)) { -#line 74 - goto while_break; - } -#line 74 - term74_5-file_01-simple-cases ++; -#line 75 - if (first_iteration == 1) { -#line 75 - __goblint_check(i == 0); - } else -#line 76 - if (i > 5) { -#line 76 - __goblint_check(i == 6); - } -#line 77 - first_iteration = 0; -#line 78 - a[i] = 0; -#line 79 - i ++; - } - while_break: /* CIL Label */ ; - } -#line 74 - __goblint_bounded(term74_5-file_01-simple-cases); - } -#line 82 - __goblint_check(a[0] == 0); -#line 83 - __goblint_check(first_iteration == 0); -#line 84 - return; -} -} -#line 89 "tests/regression/55-loop-unrolling/01-simple-cases.c" -void example5(void) -{ - int a[4] ; - int i ; - int top ; - int term95_5-file_01-simple-cases ; - - { -#line 92 - i = 0; -#line 93 - top = 0; - { -#line 95 - term95_5-file_01-simple-cases = 0; - { -#line 95 - while (1) { - while_continue: /* CIL Label */ ; -#line 95 - if (! (i < 4)) { -#line 95 - goto while_break; - } -#line 95 - term95_5-file_01-simple-cases ++; -#line 96 - a[i] = 0; -#line 97 - top += i; -#line 98 - if (i == 2) { -#line 99 - __goblint_check(top == 3); - } else { -#line 102 - __goblint_check(top == 3); - } -#line 104 - i ++; - } - while_break: /* CIL Label */ ; - } -#line 95 - __goblint_bounded(term95_5-file_01-simple-cases); - } -#line 107 - __goblint_check(a[0] == 0); -#line 108 - __goblint_check(a[3] == 0); -#line 109 - __goblint_check(top == 6); -#line 110 - return; -} -} -#line 113 "tests/regression/55-loop-unrolling/01-simple-cases.c" -void example6(void) -{ - int a[5] ; - int i ; - int top ; - int term119_5-file_01-simple-cases ; - - { -#line 116 - i = 0; -#line 117 - top = 0; - { -#line 119 - term119_5-file_01-simple-cases = 0; - { -#line 119 - while (1) { - while_continue: /* CIL Label */ ; -#line 119 - if (! (i < 3)) { -#line 119 - goto while_break; - } -#line 119 - term119_5-file_01-simple-cases ++; -#line 120 - a[i] = 0; -#line 121 - __goblint_check(a[0] == 0); -#line 122 - i ++; - } - while_break: /* CIL Label */ ; - } -#line 119 - __goblint_bounded(term119_5-file_01-simple-cases); - } -#line 125 - __goblint_check(a[0] == 0); -#line 126 - __goblint_check(a[3] == 0); -#line 127 - __goblint_check(top == 6); -#line 128 - return; -} -} -#line 131 "tests/regression/55-loop-unrolling/01-simple-cases.c" -int update(int i ) -{ - - - { -#line 132 - if (i > 5) { -#line 133 - return (0); - } else { -#line 136 - return (1); - } -} -} -#line 139 "tests/regression/55-loop-unrolling/01-simple-cases.c" -void example7(void) -{ - int a[10] ; - int i ; - int tmp ; - int term143_2-file_01-simple-cases ; - - { -#line 142 - i = 0; - { -#line 143 - term143_2-file_01-simple-cases = 0; - { -#line 143 - while (1) { - while_continue: /* CIL Label */ ; -#line 143 - tmp = update(i); -#line 143 - term143_2-file_01-simple-cases ++; -#line 143 - if (! tmp) { -#line 143 - goto while_break; - } -#line 144 - a[i] = i; -#line 145 - i ++; - } - while_break: /* CIL Label */ ; - } -#line 143 - __goblint_bounded(term143_2-file_01-simple-cases); - } -#line 147 - __goblint_check(a[0] == 0); -#line 148 - __goblint_check(a[6] == 0); -#line 149 - return; -} -} -#line 152 "tests/regression/55-loop-unrolling/01-simple-cases.c" -void example8(void) -{ - int a[5] ; - int b[5] ; - int i ; - int j ; - int term160_9-file_01-simple-cases ; - int term157_2-file_01-simple-cases ; - - { -#line 155 - b[0] = 0; -#line 155 - b[1] = 0; -#line 155 - b[2] = 0; -#line 155 - b[3] = 0; -#line 155 - b[4] = 0; -#line 156 - i = 0; - { -#line 157 - term157_2-file_01-simple-cases = 0; - { -#line 157 - while (1) { - while_continue: /* CIL Label */ ; -#line 157 - if (! (i < 5)) { -#line 157 - goto while_break; - } -#line 157 - term157_2-file_01-simple-cases ++; -#line 158 - a[i] = i; -#line 159 - j = 0; - { -#line 160 - term160_9-file_01-simple-cases = 0; - { -#line 160 - while (1) { - while_continue___0: /* CIL Label */ ; -#line 160 - if (! (j < 5)) { -#line 160 - goto while_break___0; - } -#line 160 - term160_9-file_01-simple-cases ++; -#line 161 - b[j] += a[i]; -#line 162 - j ++; - } - while_break___0: /* CIL Label */ ; - } -#line 160 - __goblint_bounded(term160_9-file_01-simple-cases); - } -#line 164 - i ++; - } - while_break: /* CIL Label */ ; - } -#line 157 - __goblint_bounded(term157_2-file_01-simple-cases); - } -#line 166 - return; -} -} -#line 170 "tests/regression/55-loop-unrolling/01-simple-cases.c" -void example9(void) -{ - int a[5] ; - int i ; - int term174_2-file_01-simple-cases ; - - { -#line 173 - i = 0; - { -#line 174 - term174_2-file_01-simple-cases = 0; - { -#line 174 - while (1) { - while_continue: /* CIL Label */ ; -#line 174 - if (! 1) { -#line 174 - goto while_break; - } -#line 174 - term174_2-file_01-simple-cases ++; -#line 175 - a[i] = i; -#line 176 - i ++; -#line 177 - if (i == 5) { -#line 177 - goto while_break; - } - } - while_break: /* CIL Label */ ; - } -#line 174 - __goblint_bounded(term174_2-file_01-simple-cases); - } -#line 179 - return; -} -} -#line 183 "tests/regression/55-loop-unrolling/01-simple-cases.c" -void example10(void) -{ - int a[5] ; - int i ; - int term187_2-file_01-simple-cases ; - - { -#line 186 - i = 0; - { -#line 187 - term187_2-file_01-simple-cases = 0; - { -#line 187 - while (1) { - while_continue: /* CIL Label */ ; -#line 187 - if (! (i < 5)) { -#line 187 - goto while_break; - } -#line 187 - term187_2-file_01-simple-cases ++; -#line 188 - if (i == 3) { -#line 189 - i ++; -#line 190 - goto while_continue; - } -#line 192 - a[i] = i; -#line 193 - i ++; - } - while_break: /* CIL Label */ ; - } -#line 187 - __goblint_bounded(term187_2-file_01-simple-cases); - } -#line 195 - return; -} -} -#line 117 "/usr/include/x86_64-linux-gnu/bits/cpu-set.h" -extern int ( __attribute__((__leaf__)) __sched_cpucount)(size_t __setsize , cpu_set_t const *__setp ) __attribute__((__nothrow__)) ; -#line 119 -extern cpu_set_t *( __attribute__((__leaf__)) __sched_cpualloc)(size_t __count ) __attribute__((__nothrow__)) ; -#line 120 -extern void ( __attribute__((__leaf__)) __sched_cpufree)(cpu_set_t *__set ) __attribute__((__nothrow__)) ; -#line 54 "/usr/include/sched.h" -extern int ( __attribute__((__leaf__)) sched_setparam)(__pid_t __pid , struct sched_param const *__param ) __attribute__((__nothrow__)) ; -#line 58 -extern int ( __attribute__((__leaf__)) sched_getparam)(__pid_t __pid , struct sched_param *__param ) __attribute__((__nothrow__)) ; -#line 61 -extern int ( __attribute__((__leaf__)) sched_setscheduler)(__pid_t __pid , int __policy , - struct sched_param const *__param ) __attribute__((__nothrow__)) ; -#line 65 -extern int ( __attribute__((__leaf__)) sched_getscheduler)(__pid_t __pid ) __attribute__((__nothrow__)) ; -#line 68 -extern int ( __attribute__((__leaf__)) sched_yield)(void) __attribute__((__nothrow__)) ; -#line 71 -extern int ( __attribute__((__leaf__)) sched_get_priority_max)(int __algorithm ) __attribute__((__nothrow__)) ; -#line 74 -extern int ( __attribute__((__leaf__)) sched_get_priority_min)(int __algorithm ) __attribute__((__nothrow__)) ; -#line 78 -extern int ( __attribute__((__leaf__)) sched_rr_get_interval)(__pid_t __pid , struct timespec *__t ) __attribute__((__nothrow__)) ; -#line 72 "/usr/include/time.h" -extern clock_t ( __attribute__((__leaf__)) clock)(void) __attribute__((__nothrow__)) ; -#line 76 -extern time_t ( __attribute__((__leaf__)) time)(time_t *__timer ) __attribute__((__nothrow__)) ; -#line 79 -extern double ( __attribute__((__leaf__)) difftime)(time_t __time1 , time_t __time0 ) __attribute__((__nothrow__, -__const__)) ; -#line 83 -extern time_t ( __attribute__((__leaf__)) mktime)(struct tm *__tp ) __attribute__((__nothrow__)) ; -#line 100 -extern size_t ( __attribute__((__leaf__)) strftime)(char * __restrict __s , size_t __maxsize , - char const * __restrict __format , - struct tm const * __restrict __tp ) __attribute__((__nothrow__)) ; -#line 116 -extern size_t ( __attribute__((__leaf__)) strftime_l)(char * __restrict __s , size_t __maxsize , - char const * __restrict __format , - struct tm const * __restrict __tp , - locale_t __loc ) __attribute__((__nothrow__)) ; -#line 132 -extern struct tm *( __attribute__((__leaf__)) gmtime)(time_t const *__timer ) __attribute__((__nothrow__)) ; -#line 136 -extern struct tm *( __attribute__((__leaf__)) localtime)(time_t const *__timer ) __attribute__((__nothrow__)) ; -#line 154 -extern struct tm *( __attribute__((__leaf__)) gmtime_r)(time_t const * __restrict __timer , - struct tm * __restrict __tp ) __attribute__((__nothrow__)) ; -#line 159 -extern struct tm *( __attribute__((__leaf__)) localtime_r)(time_t const * __restrict __timer , - struct tm * __restrict __tp ) __attribute__((__nothrow__)) ; -#line 179 -extern char *( __attribute__((__leaf__)) asctime)(struct tm const *__tp ) __attribute__((__nothrow__)) ; -#line 183 -extern char *( __attribute__((__leaf__)) ctime)(time_t const *__timer ) __attribute__((__nothrow__)) ; -#line 197 -extern char *( __attribute__((__leaf__)) asctime_r)(struct tm const * __restrict __tp , - char * __restrict __buf ) __attribute__((__nothrow__)) ; -#line 202 -extern char *( __attribute__((__leaf__)) ctime_r)(time_t const * __restrict __timer , - char * __restrict __buf ) __attribute__((__nothrow__)) ; -#line 217 -extern char *__tzname[2] ; -#line 218 -extern int __daylight ; -#line 219 -extern long __timezone ; -#line 224 -extern char *tzname[2] ; -#line 228 -extern void ( __attribute__((__leaf__)) tzset)(void) __attribute__((__nothrow__)) ; -#line 232 -extern int daylight ; -#line 233 -extern long timezone ; -#line 249 -extern time_t ( __attribute__((__leaf__)) timegm)(struct tm *__tp ) __attribute__((__nothrow__)) ; -#line 251 -extern time_t ( __attribute__((__leaf__)) timelocal)(struct tm *__tp ) __attribute__((__nothrow__)) ; -#line 262 -extern int ( __attribute__((__leaf__)) dysize)(int __year ) __attribute__((__nothrow__, -__const__)) ; -#line 272 -extern int nanosleep(struct timespec const *__requested_time , struct timespec *__remaining ) ; -#line 276 -extern int ( __attribute__((__leaf__)) clock_getres)(clockid_t __clock_id , struct timespec *__res ) __attribute__((__nothrow__)) ; -#line 279 -extern int ( __attribute__((__leaf__)) clock_gettime)(clockid_t __clock_id , struct timespec *__tp ) __attribute__((__nothrow__)) ; -#line 282 -extern int ( __attribute__((__leaf__)) clock_settime)(clockid_t __clock_id , struct timespec const *__tp ) __attribute__((__nothrow__)) ; -#line 311 -extern int clock_nanosleep(clockid_t __clock_id , int __flags , struct timespec const *__req , - struct timespec *__rem ) ; -#line 326 -extern int ( __attribute__((__leaf__)) clock_getcpuclockid)(pid_t __pid , clockid_t *__clock_id ) __attribute__((__nothrow__)) ; -#line 331 -extern int ( __attribute__((__leaf__)) timer_create)(clockid_t __clock_id , struct sigevent * __restrict __evp , - timer_t * __restrict __timerid ) __attribute__((__nothrow__)) ; -#line 336 -extern int ( __attribute__((__leaf__)) timer_delete)(timer_t __timerid ) __attribute__((__nothrow__)) ; -#line 340 -extern int ( __attribute__((__leaf__)) timer_settime)(timer_t __timerid , int __flags , - struct itimerspec const * __restrict __value , - struct itimerspec * __restrict __ovalue ) __attribute__((__nothrow__)) ; -#line 345 -extern int ( __attribute__((__leaf__)) timer_gettime)(timer_t __timerid , struct itimerspec *__value ) __attribute__((__nothrow__)) ; -#line 364 -extern int ( __attribute__((__leaf__)) timer_getoverrun)(timer_t __timerid ) __attribute__((__nothrow__)) ; -#line 371 -extern int ( __attribute__((__nonnull__(1), __leaf__)) timespec_get)(struct timespec *__ts , - int __base ) __attribute__((__nothrow__)) ; -#line 202 "/usr/include/pthread.h" -extern int ( __attribute__((__nonnull__(1,3))) pthread_create)(pthread_t * __restrict __newthread , - pthread_attr_t const * __restrict __attr , - void *(*__start_routine)(void * ) , - void * __restrict __arg ) __attribute__((__nothrow__)) ; -#line 211 -extern void pthread_exit(void *__retval ) __attribute__((__noreturn__)) ; -#line 219 -extern int pthread_join(pthread_t __th , void **__thread_return ) ; -#line 269 -extern int ( __attribute__((__leaf__)) pthread_detach)(pthread_t __th ) __attribute__((__nothrow__)) ; -#line 273 -extern pthread_t ( __attribute__((__leaf__)) pthread_self)(void) __attribute__((__nothrow__, -__const__)) ; -#line 276 -extern int ( __attribute__((__leaf__)) pthread_equal)(pthread_t __thread1 , pthread_t __thread2 ) __attribute__((__nothrow__, -__const__)) ; -#line 285 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_attr_init)(pthread_attr_t *__attr ) __attribute__((__nothrow__)) ; -#line 288 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_attr_destroy)(pthread_attr_t *__attr ) __attribute__((__nothrow__)) ; -#line 292 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_attr_getdetachstate)(pthread_attr_t const *__attr , - int *__detachstate ) __attribute__((__nothrow__)) ; -#line 297 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_attr_setdetachstate)(pthread_attr_t *__attr , - int __detachstate ) __attribute__((__nothrow__)) ; -#line 303 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_attr_getguardsize)(pthread_attr_t const *__attr , - size_t *__guardsize ) __attribute__((__nothrow__)) ; -#line 308 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_attr_setguardsize)(pthread_attr_t *__attr , - size_t __guardsize ) __attribute__((__nothrow__)) ; -#line 314 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_attr_getschedparam)(pthread_attr_t const * __restrict __attr , - struct sched_param * __restrict __param ) __attribute__((__nothrow__)) ; -#line 319 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_attr_setschedparam)(pthread_attr_t * __restrict __attr , - struct sched_param const * __restrict __param ) __attribute__((__nothrow__)) ; -#line 324 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_attr_getschedpolicy)(pthread_attr_t const * __restrict __attr , - int * __restrict __policy ) __attribute__((__nothrow__)) ; -#line 329 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_attr_setschedpolicy)(pthread_attr_t *__attr , - int __policy ) __attribute__((__nothrow__)) ; -#line 333 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_attr_getinheritsched)(pthread_attr_t const * __restrict __attr , - int * __restrict __inherit ) __attribute__((__nothrow__)) ; -#line 338 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_attr_setinheritsched)(pthread_attr_t *__attr , - int __inherit ) __attribute__((__nothrow__)) ; -#line 344 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_attr_getscope)(pthread_attr_t const * __restrict __attr , - int * __restrict __scope ) __attribute__((__nothrow__)) ; -#line 349 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_attr_setscope)(pthread_attr_t *__attr , - int __scope ) __attribute__((__nothrow__)) ; -#line 353 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_attr_getstackaddr)(pthread_attr_t const * __restrict __attr , - void ** __restrict __stackaddr ) __attribute__((__nothrow__, -__deprecated__)) ; -#line 361 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_attr_setstackaddr)(pthread_attr_t *__attr , - void *__stackaddr ) __attribute__((__nothrow__, -__deprecated__)) ; -#line 366 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_attr_getstacksize)(pthread_attr_t const * __restrict __attr , - size_t * __restrict __stacksize ) __attribute__((__nothrow__)) ; -#line 373 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_attr_setstacksize)(pthread_attr_t *__attr , - size_t __stacksize ) __attribute__((__nothrow__)) ; -#line 379 -extern int ( __attribute__((__nonnull__(1,2,3), __leaf__)) pthread_attr_getstack)(pthread_attr_t const * __restrict __attr , - void ** __restrict __stackaddr , - size_t * __restrict __stacksize ) __attribute__((__nothrow__)) ; -#line 387 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_attr_setstack)(pthread_attr_t *__attr , - void *__stackaddr , - size_t __stacksize ) __attribute__((__nothrow__)) ; -#line 441 -extern int ( __attribute__((__nonnull__(3), __leaf__)) pthread_setschedparam)(pthread_t __target_thread , - int __policy , - struct sched_param const *__param ) __attribute__((__nothrow__)) ; -#line 446 -extern int ( __attribute__((__nonnull__(2,3), __leaf__)) pthread_getschedparam)(pthread_t __target_thread , - int * __restrict __policy , - struct sched_param * __restrict __param ) __attribute__((__nothrow__)) ; -#line 452 -extern int ( __attribute__((__leaf__)) pthread_setschedprio)(pthread_t __target_thread , - int __prio ) __attribute__((__nothrow__)) ; -#line 509 -int ( __attribute__((__nonnull__(1,2))) pthread_once)(pthread_once_t *once_control , - void (*init_routine)(void) ) __attribute__((__goblint_stub__)) ; -#line 521 -extern int pthread_setcancelstate(int __state , int *__oldstate ) ; -#line 525 -extern int pthread_setcanceltype(int __type , int *__oldtype ) ; -#line 528 -extern int pthread_cancel(pthread_t __th ) ; -#line 533 -extern void pthread_testcancel(void) ; -#line 697 -extern void __pthread_register_cancel(__pthread_unwind_buf_t *__buf ) ; -#line 709 -extern void __pthread_unregister_cancel(__pthread_unwind_buf_t *__buf ) ; -#line 750 -extern void __pthread_unwind_next(__pthread_unwind_buf_t *__buf ) __attribute__((__weak__, -__noreturn__)) ; -#line 766 -extern int __sigsetjmp_cancel(struct __cancel_jmp_buf_tag *__env , int __savemask ) __asm__("__sigsetjmp") __attribute__((__returns_twice__, -__nothrow__)) ; -#line 781 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutex_init)(pthread_mutex_t *__mutex , - pthread_mutexattr_t const *__mutexattr ) __attribute__((__nothrow__)) ; -#line 786 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutex_destroy)(pthread_mutex_t *__mutex ) __attribute__((__nothrow__)) ; -#line 790 -extern int ( __attribute__((__nonnull__(1))) pthread_mutex_trylock)(pthread_mutex_t *__mutex ) __attribute__((__nothrow__)) ; -#line 794 -extern int ( __attribute__((__nonnull__(1))) pthread_mutex_lock)(pthread_mutex_t *__mutex ) __attribute__((__nothrow__)) ; -#line 800 -extern int ( __attribute__((__nonnull__(1,2))) pthread_mutex_timedlock)(pthread_mutex_t * __restrict __mutex , - struct timespec const * __restrict __abstime ) __attribute__((__nothrow__)) ; -#line 835 -extern int ( __attribute__((__nonnull__(1))) pthread_mutex_unlock)(pthread_mutex_t *__mutex ) __attribute__((__nothrow__)) ; -#line 840 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_mutex_getprioceiling)(pthread_mutex_t const * __restrict __mutex , - int * __restrict __prioceiling ) __attribute__((__nothrow__)) ; -#line 847 -extern int ( __attribute__((__nonnull__(1,3), __leaf__)) pthread_mutex_setprioceiling)(pthread_mutex_t * __restrict __mutex , - int __prioceiling , - int * __restrict __old_ceiling ) __attribute__((__nothrow__)) ; -#line 855 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutex_consistent)(pthread_mutex_t *__mutex ) __attribute__((__nothrow__)) ; -#line 874 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutexattr_init)(pthread_mutexattr_t *__attr ) __attribute__((__nothrow__)) ; -#line 878 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutexattr_destroy)(pthread_mutexattr_t *__attr ) __attribute__((__nothrow__)) ; -#line 882 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_mutexattr_getpshared)(pthread_mutexattr_t const * __restrict __attr , - int * __restrict __pshared ) __attribute__((__nothrow__)) ; -#line 888 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutexattr_setpshared)(pthread_mutexattr_t *__attr , - int __pshared ) __attribute__((__nothrow__)) ; -#line 894 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_mutexattr_gettype)(pthread_mutexattr_t const * __restrict __attr , - int * __restrict __kind ) __attribute__((__nothrow__)) ; -#line 901 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutexattr_settype)(pthread_mutexattr_t *__attr , - int __kind ) __attribute__((__nothrow__)) ; -#line 906 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_mutexattr_getprotocol)(pthread_mutexattr_t const * __restrict __attr , - int * __restrict __protocol ) __attribute__((__nothrow__)) ; -#line 913 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutexattr_setprotocol)(pthread_mutexattr_t *__attr , - int __protocol ) __attribute__((__nothrow__)) ; -#line 918 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_mutexattr_getprioceiling)(pthread_mutexattr_t const * __restrict __attr , - int * __restrict __prioceiling ) __attribute__((__nothrow__)) ; -#line 924 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutexattr_setprioceiling)(pthread_mutexattr_t *__attr , - int __prioceiling ) __attribute__((__nothrow__)) ; -#line 930 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_mutexattr_getrobust)(pthread_mutexattr_t const *__attr , - int *__robustness ) __attribute__((__nothrow__)) ; -#line 946 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutexattr_setrobust)(pthread_mutexattr_t *__attr , - int __robustness ) __attribute__((__nothrow__)) ; -#line 967 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_rwlock_init)(pthread_rwlock_t * __restrict __rwlock , - pthread_rwlockattr_t const * __restrict __attr ) __attribute__((__nothrow__)) ; -#line 972 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_rwlock_destroy)(pthread_rwlock_t *__rwlock ) __attribute__((__nothrow__)) ; -#line 976 -extern int ( __attribute__((__nonnull__(1))) pthread_rwlock_rdlock)(pthread_rwlock_t *__rwlock ) __attribute__((__nothrow__)) ; -#line 980 -extern int ( __attribute__((__nonnull__(1))) pthread_rwlock_tryrdlock)(pthread_rwlock_t *__rwlock ) __attribute__((__nothrow__)) ; -#line 986 -extern int ( __attribute__((__nonnull__(1,2))) pthread_rwlock_timedrdlock)(pthread_rwlock_t * __restrict __rwlock , - struct timespec const * __restrict __abstime ) __attribute__((__nothrow__)) ; -#line 1023 -extern int ( __attribute__((__nonnull__(1))) pthread_rwlock_wrlock)(pthread_rwlock_t *__rwlock ) __attribute__((__nothrow__)) ; -#line 1027 -extern int ( __attribute__((__nonnull__(1))) pthread_rwlock_trywrlock)(pthread_rwlock_t *__rwlock ) __attribute__((__nothrow__)) ; -#line 1033 -extern int ( __attribute__((__nonnull__(1,2))) pthread_rwlock_timedwrlock)(pthread_rwlock_t * __restrict __rwlock , - struct timespec const * __restrict __abstime ) __attribute__((__nothrow__)) ; -#line 1071 -extern int ( __attribute__((__nonnull__(1))) pthread_rwlock_unlock)(pthread_rwlock_t *__rwlock ) __attribute__((__nothrow__)) ; -#line 1078 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_rwlockattr_init)(pthread_rwlockattr_t *__attr ) __attribute__((__nothrow__)) ; -#line 1082 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_rwlockattr_destroy)(pthread_rwlockattr_t *__attr ) __attribute__((__nothrow__)) ; -#line 1086 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_rwlockattr_getpshared)(pthread_rwlockattr_t const * __restrict __attr , - int * __restrict __pshared ) __attribute__((__nothrow__)) ; -#line 1092 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_rwlockattr_setpshared)(pthread_rwlockattr_t *__attr , - int __pshared ) __attribute__((__nothrow__)) ; -#line 1097 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_rwlockattr_getkind_np)(pthread_rwlockattr_t const * __restrict __attr , - int * __restrict __pref ) __attribute__((__nothrow__)) ; -#line 1103 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_rwlockattr_setkind_np)(pthread_rwlockattr_t *__attr , - int __pref ) __attribute__((__nothrow__)) ; -#line 1112 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_cond_init)(pthread_cond_t * __restrict __cond , - pthread_condattr_t const * __restrict __cond_attr ) __attribute__((__nothrow__)) ; -#line 1117 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_cond_destroy)(pthread_cond_t *__cond ) __attribute__((__nothrow__)) ; -#line 1121 -extern int ( __attribute__((__nonnull__(1))) pthread_cond_signal)(pthread_cond_t *__cond ) __attribute__((__nothrow__)) ; -#line 1125 -extern int ( __attribute__((__nonnull__(1))) pthread_cond_broadcast)(pthread_cond_t *__cond ) __attribute__((__nothrow__)) ; -#line 1133 -extern int ( __attribute__((__nonnull__(1,2))) pthread_cond_wait)(pthread_cond_t * __restrict __cond , - pthread_mutex_t * __restrict __mutex ) ; -#line 1145 -extern int ( __attribute__((__nonnull__(1,2,3))) pthread_cond_timedwait)(pthread_cond_t * __restrict __cond , - pthread_mutex_t * __restrict __mutex , - struct timespec const * __restrict __abstime ) ; -#line 1194 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_condattr_init)(pthread_condattr_t *__attr ) __attribute__((__nothrow__)) ; -#line 1198 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_condattr_destroy)(pthread_condattr_t *__attr ) __attribute__((__nothrow__)) ; -#line 1202 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_condattr_getpshared)(pthread_condattr_t const * __restrict __attr , - int * __restrict __pshared ) __attribute__((__nothrow__)) ; -#line 1208 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_condattr_setpshared)(pthread_condattr_t *__attr , - int __pshared ) __attribute__((__nothrow__)) ; -#line 1213 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_condattr_getclock)(pthread_condattr_t const * __restrict __attr , - __clockid_t * __restrict __clock_id ) __attribute__((__nothrow__)) ; -#line 1219 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_condattr_setclock)(pthread_condattr_t *__attr , - __clockid_t __clock_id ) __attribute__((__nothrow__)) ; -#line 1230 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_spin_init)(pthread_spinlock_t *__lock , - int __pshared ) __attribute__((__nothrow__)) ; -#line 1234 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_spin_destroy)(pthread_spinlock_t *__lock ) __attribute__((__nothrow__)) ; -#line 1238 -extern int ( __attribute__((__nonnull__(1))) pthread_spin_lock)(pthread_spinlock_t *__lock ) __attribute__((__nothrow__)) ; -#line 1242 -extern int ( __attribute__((__nonnull__(1))) pthread_spin_trylock)(pthread_spinlock_t *__lock ) __attribute__((__nothrow__)) ; -#line 1246 -extern int ( __attribute__((__nonnull__(1))) pthread_spin_unlock)(pthread_spinlock_t *__lock ) __attribute__((__nothrow__)) ; -#line 1254 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_barrier_init)(pthread_barrier_t * __restrict __barrier , - pthread_barrierattr_t const * __restrict __attr , - unsigned int __count ) __attribute__((__nothrow__)) ; -#line 1260 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_barrier_destroy)(pthread_barrier_t *__barrier ) __attribute__((__nothrow__)) ; -#line 1264 -extern int ( __attribute__((__nonnull__(1))) pthread_barrier_wait)(pthread_barrier_t *__barrier ) __attribute__((__nothrow__)) ; -#line 1269 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_barrierattr_init)(pthread_barrierattr_t *__attr ) __attribute__((__nothrow__)) ; -#line 1273 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_barrierattr_destroy)(pthread_barrierattr_t *__attr ) __attribute__((__nothrow__)) ; -#line 1277 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_barrierattr_getpshared)(pthread_barrierattr_t const * __restrict __attr , - int * __restrict __pshared ) __attribute__((__nothrow__)) ; -#line 1283 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_barrierattr_setpshared)(pthread_barrierattr_t *__attr , - int __pshared ) __attribute__((__nothrow__)) ; -#line 1297 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_key_create)(pthread_key_t *__key , - void (*__destr_function)(void * ) ) __attribute__((__nothrow__)) ; -#line 1302 -extern int ( __attribute__((__leaf__)) pthread_key_delete)(pthread_key_t __key ) __attribute__((__nothrow__)) ; -#line 1305 -extern void *( __attribute__((__leaf__)) pthread_getspecific)(pthread_key_t __key ) __attribute__((__nothrow__)) ; -#line 1308 -extern int ( __attribute__((__leaf__)) pthread_setspecific)(pthread_key_t __key , - void const *__pointer ) __attribute__((__nothrow__, -__access__(__none__,2))) ; -#line 1315 -extern int ( __attribute__((__nonnull__(2), __leaf__)) pthread_getcpuclockid)(pthread_t __thread_id , - __clockid_t *__clock_id ) __attribute__((__nothrow__)) ; -#line 1332 -extern int ( __attribute__((__leaf__)) pthread_atfork)(void (*__prepare)(void) , void (*__parent)(void) , - void (*__child)(void) ) __attribute__((__nothrow__)) ; -#line 5 "lib/libc/stub/src/pthread.c" -int ( __attribute__((__nonnull__(1,2))) pthread_once)(pthread_once_t *once_control , - void (*init_routine)(void) ) __attribute__((__goblint_stub__)) ; -#line 5 "lib/libc/stub/src/pthread.c" -int ( __attribute__((__nonnull__(1,2))) pthread_once)(pthread_once_t *once_control , - void (*init_routine)(void) ) -{ - int top ; - - { -#line 8 - (*init_routine)(); -#line 9 - return (top); -} -} -#line 6 "lib/libc/stub/src/stdlib.c" -void qsort(void *ptr , size_t count , size_t size , int (*comp)(void const * , void const * ) ) __attribute__((__goblint_stub__)) ; -#line 7 -void qsort(void *ptr , size_t count , size_t size , int (*comp)(void const * , void const * ) ) __attribute__((__goblint_stub__)) ; -#line 7 "lib/libc/stub/src/stdlib.c" -void qsort(void *ptr , size_t count , size_t size , int (*comp)(void const * , void const * ) ) -{ - size_t i ; - size_t j ; - size_t i___0 ; - size_t j___0 ; - int r ; - size_t k ; - char *a ; - char *b ; - char c ; - int term10_5-file_stdlib ; - int term9_3-file_stdlib ; - int term21_9-file_stdlib ; - int term17_5-file_stdlib ; - int term16_3-file_stdlib ; - - { -#line 9 - i = (size_t )0; - { -#line 9 - term9_3-file_stdlib = 0; - { -#line 9 - while (1) { - while_continue: /* CIL Label */ ; -#line 9 - if (! (i < count)) { -#line 9 - goto while_break; - } -#line 9 - term9_3-file_stdlib ++; -#line 10 - j = (size_t )0; - { -#line 10 - term10_5-file_stdlib = 0; - { -#line 10 - while (1) { - while_continue___0: /* CIL Label */ ; -#line 10 - if (! (j < count)) { -#line 10 - goto while_break___0; - } -#line 10 - term10_5-file_stdlib ++; -#line 11 - (*comp)((void const *)(ptr + i * size), (void const *)(ptr + j * size)); -#line 10 - j ++; - } - while_break___0: /* CIL Label */ ; - } -#line 10 - __goblint_bounded(term10_5-file_stdlib); - } -#line 9 - i ++; - } - while_break: /* CIL Label */ ; - } -#line 9 - __goblint_bounded(term9_3-file_stdlib); - } -#line 16 - i___0 = (size_t )0; - { -#line 16 - term16_3-file_stdlib = 0; - { -#line 16 - while (1) { - while_continue___1: /* CIL Label */ ; -#line 16 - if (! (i___0 < count)) { -#line 16 - goto while_break___1; - } -#line 16 - term16_3-file_stdlib ++; -#line 17 - j___0 = (size_t )0; - { -#line 17 - term17_5-file_stdlib = 0; - { -#line 17 - while (1) { - while_continue___2: /* CIL Label */ ; -#line 17 - if (! (j___0 < count)) { -#line 17 - goto while_break___2; - } -#line 17 - term17_5-file_stdlib ++; -#line 19 - if (r) { -#line 21 - k = (size_t )0; - { -#line 21 - term21_9-file_stdlib = 0; - { -#line 21 - while (1) { - while_continue___3: /* CIL Label */ ; -#line 21 - if (! (k < size)) { -#line 21 - goto while_break___3; - } -#line 21 - term21_9-file_stdlib ++; -#line 22 - a = (char *)((ptr + i___0 * size) + k); -#line 23 - b = (char *)((ptr + j___0 * size) + k); -#line 24 - c = *a; -#line 25 - *a = *b; -#line 26 - *b = c; -#line 21 - k ++; - } - while_break___3: /* CIL Label */ ; - } -#line 21 - __goblint_bounded(term21_9-file_stdlib); - } - } -#line 17 - j___0 ++; - } - while_break___2: /* CIL Label */ ; - } -#line 17 - __goblint_bounded(term17_5-file_stdlib); - } -#line 16 - i___0 ++; - } - while_break___1: /* CIL Label */ ; - } -#line 16 - __goblint_bounded(term16_3-file_stdlib); - } -#line 33 - return; -} -} -#line 37 -void *bsearch(void const *key , void const *ptr , size_t count , size_t size , - int (*comp)(void const * , void const * ) ) __attribute__((__goblint_stub__)) ; -#line 38 -void *bsearch(void const *key , void const *ptr , size_t count , size_t size , - int (*comp)(void const * , void const * ) ) __attribute__((__goblint_stub__)) ; -#line 38 "lib/libc/stub/src/stdlib.c" -void *bsearch(void const *key , void const *ptr , size_t count , size_t size , - int (*comp)(void const * , void const * ) ) -{ - size_t i ; - void const *a ; - int tmp ; - int term40_3-file_stdlib ; - - { -#line 40 - i = (size_t )0; - { -#line 40 - term40_3-file_stdlib = 0; - { -#line 40 - while (1) { - while_continue: /* CIL Label */ ; -#line 40 - if (! (i < count)) { -#line 40 - goto while_break; - } -#line 40 - term40_3-file_stdlib ++; -#line 41 - a = ptr + i * size; -#line 42 - tmp = (*comp)(key, a); -#line 42 - if (tmp == 0) { -#line 43 - return ((void *)a); - } -#line 40 - i ++; - } - while_break: /* CIL Label */ ; - } -#line 40 - __goblint_bounded(term40_3-file_stdlib); - } -#line 47 - return ((void *)0); -} -} - -vars = 0 evals = 0 narrow_reuses = 0 - -Timings: diff --git a/runningGob.sh b/runningGob.sh index 848d69c341..15007b3e0b 100755 --- a/runningGob.sh +++ b/runningGob.sh @@ -1,6 +1,6 @@ #!/bin/bash make -#make install +make install # set options and file for apron execution options_apron="--set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --enable warn.debug" #note: preprocessing first needs to be added to apron diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 993aaf6421..0a7ccd04b2 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -139,7 +139,13 @@ struct let name () = "Tuple" let to_yojson x = `String (show x) let relift (a,b) = (a,b) (*Todo: is this correct?*) - let printXml f (a,b) = Base1.printXml f a; Base2.printXml f b (*Todo: what do we have to put here?*) + let printXml f (a,b) = + BatPrintf.fprintf f "\n + Tuple:\n + \n + fundec\n%a\n\n + context\n%a\n\n + \n" Base1.printXml a Base2.printXml b (*Todo: what do we have to put here?*) let compare (a1,b1) (a2,b2) = 3 (*Todo: what do we have to put here?*) (*let a = Base1.compare a1 a2 in let b = Base2.compare b1 b2 in @@ -150,22 +156,26 @@ struct end -module GVarGG (G: Lattice.S) (C: Printable.S) = +module GVarGG (G: Lattice.S) (C: Printable.S) (Base: Printable.S) = struct module CSet = struct include SetDomain.Make ( struct - include (T (CilType.Fundec) (C) (C)) (* Set of Tuples*) + include (Base) (* Set of Tuples*) end ) let name () = "contexts" + let printXml f a = + BatPrintf.fprintf f "\n\n"; + iter (Base.printXml f) a; + BatPrintf.fprintf f "\n\n" end module CMap = struct include MapDomain.MapBot (C_ (C)) (CSet) - let printXml f c = BatPrintf.fprintf f "%a" printXml c (* TODO *) + let printXml f c = BatPrintf.fprintf f "%a" printXml c (*TODO*) end include Lattice.Lift2 (G) (CMap) (Printable.DefaultNames) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 1614354cfa..31843794ec 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1458,144 +1458,6 @@ struct end -module DeadBranchLifter2 (S: Spec): Spec = -struct - include S - - let name () = "DeadBranch2 (" ^ S.name () ^ ")" - - (* Two global invariants: - 1. S.V -> S.G -- used for S - 2. node -> (exp -> flat bool) -- used for warnings *) - - module V = - struct - include Printable.Either (S.V) (Node) - let name () = "DeadBranch2" - let s x = `Left x - let node x = `Right x - let is_write_only = function - | `Left x -> S.V.is_write_only x - | `Right _ -> true - end - - module EM = - struct - include MapDomain.MapBot (Basetype.CilExp) (Basetype.Bools) - let name () = "branches2" - end - - module G = - struct - include Lattice.Lift2 (S.G) (EM) (Printable.DefaultNames) - let name () = "deadbranch2" - - let s = function - | `Bot -> S.G.bot () - | `Lifted1 x -> x - | _ -> failwith "DeadBranchLifter2.s" - let node = function - | `Bot -> EM.bot () - | `Lifted2 x -> x - | _ -> failwith "DeadBranchLifter2.node" - let create_s s = `Lifted1 s - let create_node node = `Lifted2 node - - let printXml f = function - | `Lifted1 x -> S.G.printXml f x - | `Lifted2 x -> BatPrintf.fprintf f "%a" EM.printXml x - | x -> BatPrintf.fprintf f "%a" printXml x - end - - let conv (ctx: (_, G.t, _, V.t) ctx): (_, S.G.t, _, S.V.t) ctx = - { ctx with - global = (fun v -> G.s (ctx.global (V.s v))); - sideg = (fun v g -> ctx.sideg (V.s v) (G.create_s g)); - } - - let query ctx (type a) (q: a Queries.t): a Queries.result = - match q with - | WarnGlobal g -> - let g: V.t = Obj.obj g in - begin match g with - | `Left g -> - S.query (conv ctx) (WarnGlobal (Obj.repr g)) - | `Right g -> - let em = G.node (ctx.global (V.node g)) in - EM.iter (fun exp tv -> - match tv with - | `Lifted tv -> - let loc = Node.location g in (* TODO: looking up location now doesn't work nicely with incremental *) - let cilinserted = if loc.synthetic then "(possibly inserted by CIL) " else "" in - M.warn ~loc:(Node g) ~tags:[CWE (if tv then 571 else 570)] ~category:Deadcode "condition '%a' %sis always %B" d_exp exp cilinserted tv - | `Bot when not (CilType.Exp.equal exp one) -> (* all branches dead *) - M.error ~loc:(Node g) ~category:Analyzer ~tags:[Category Unsound] "both branches over condition '%a' are dead" d_exp exp - | `Bot (* all branches dead, fine at our inserted Neg(1)-s because no Pos(1) *) - | `Top -> (* may be both true and false *) - () - ) em; - end - | InvariantGlobal g -> - let g: V.t = Obj.obj g in - begin match g with - | `Left g -> - S.query (conv ctx) (InvariantGlobal (Obj.repr g)) - | `Right g -> - Queries.Result.top q - end - | IterSysVars (vq, vf) -> - (* vars for S *) - let vf' x = vf (Obj.repr (V.s (Obj.obj x))) in - S.query (conv ctx) (IterSysVars (vq, vf')); - - (* node vars for dead branches *) - begin match vq with - | Node {node; _} -> - vf (Obj.repr (V.node node)) - | _ -> - () - end - | _ -> - S.query (conv ctx) q - - - let branch ctx = S.branch (conv ctx) - - let branch ctx exp tv = - if !AnalysisState.postsolving then ( - try - let r = branch ctx exp tv in - (* branch is live *) - ctx.sideg (V.node ctx.prev_node) (G.create_node (EM.singleton exp (`Lifted tv))); (* record expression with reached tv *) - r - with Deadcode -> - (* branch is dead *) - ctx.sideg (V.node ctx.prev_node) (G.create_node (EM.singleton exp `Bot)); (* record expression without reached tv *) - raise Deadcode - ) - else ( - ctx.sideg (V.node ctx.prev_node) (G.create_node (EM.bot ())); (* create global variable during solving, to allow postsolving leq hack to pass verify *) - branch ctx exp tv - ) - - let assign ctx = S.assign (conv ctx) - let vdecl ctx = S.vdecl (conv ctx) - let enter ctx = S.enter (conv ctx) - let paths_as_set ctx = S.paths_as_set (conv ctx) - let body ctx = S.body (conv ctx) - let return ctx = S.return (conv ctx) - let combine_env ctx = S.combine_env (conv ctx) - let combine_assign ctx = S.combine_assign (conv ctx) - let special ctx = S.special (conv ctx) - let threadenter ctx = S.threadenter (conv ctx) - let threadspawn ctx lv f args fctx = S.threadspawn (conv ctx) lv f args (conv fctx) - let sync ctx = S.sync (conv ctx) - let skip ctx = S.skip (conv ctx) - let asm ctx = S.asm (conv ctx) - let event ctx e octx = S.event (conv ctx) e (conv octx) -end - - module LongjmpLifter (S: Spec): Spec = struct include S @@ -2082,12 +1944,10 @@ struct let s = spec end - module G = GVarGG (S.G) (S.C) + module G = GVarGG (S.G) (S.C) (T (CilType.Fundec) (C) (C)) let name () = "RecursionTerm (" ^ S.name () ^ ")" - let finalize = S.finalize (*TODO*) - (**let side_context sideg f c = if !AnalysisState.postsolving then sideg (f) (G.create_contexts (G.CSet.singleton c))*) @@ -2098,6 +1958,8 @@ struct global = (fun v -> G.s (ctx.global (V.spec v))); sideg = (fun v g -> ctx.sideg (V.spec v) (G.create_s g)); } + + (*TODO: We may need to add new queries here*) let query ctx (type a) (q: a Queries.t): a Queries.result = match q with | WarnGlobal g -> @@ -2129,23 +1991,24 @@ struct if !AnalysisState.postsolving then sideg (V.contexts f) (G.create_contexts (G.CMap.singleton (c) (t))) - let enter ctx lval fu exprList = (*TODO*) - if !AnalysisState.postsolving then - let c_r: unit -> S.C.t = ctx.context in (*Caller context*) (*TODO is this the caller or callee context???*) - let fd_r : fundec = fu in (*Caller fundec*) (*TODO: Falsch??*) - let c_e : unit -> S.C.t = ctx.context in (*Callee context*) (*TODO: Falsch??*) - let fd_e : fundec = fu in (*Callee fundec*) - let tup: (fundec * S.C.t) = (fd_r, (c_r ())) in (* TODO: is fundec the caller or callee fundec???*) - let t = G.CSet.singleton (tup) in (*TODO do we fill the set correctly???*) - side_context ctx.sideg fd_e (c_e ()) t; - S.enter (conv ctx) lval fu exprList - else - S.enter (conv ctx) lval fu exprList + let enter ctx = S.enter (conv ctx) let paths_as_set ctx = S.paths_as_set (conv ctx) let body ctx = S.body (conv ctx) let return ctx = S.return (conv ctx) - let combine_env ctx = S.combine_env (conv ctx) + let combine_env ctx r fe f args fc es f_ask = (*Todo*) + if !AnalysisState.postsolving then + let c_r: unit -> S.C.t = ctx.context in (*Caller context*) (*TODO is this the caller or callee context???*) + let nodeF = ctx.node in + let fd_r : fundec = Node.find_fundec nodeF in (*Caller fundec*) + let c_e: S.C.t = Option.get (fc) in (*Callee context*) + let fd_e : fundec = f in (*Callee fundec*) + let tup: (fundec * S.C.t) = (fd_r, (c_r ())) in + let t = G.CSet.singleton (tup) in (*TODO do we fill the set correctly???*) + side_context ctx.sideg fd_e (c_e) t; + S.combine_env (conv ctx) r fe f args fc es f_ask + else + S.combine_env (conv ctx) r fe f args fc es f_ask let combine_assign ctx = S.combine_assign (conv ctx) let special ctx = S.special (conv ctx) let threadenter ctx = S.threadenter (conv ctx) diff --git a/src/framework/control.ml b/src/framework/control.ml index 0a36d6c989..6261329e18 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -37,8 +37,7 @@ let spec_module: (module Spec) Lazy.t = lazy ( |> lift (get_bool "ana.widen.tokens") (module WideningTokens.Lifter) |> lift true (module LongjmpLifter) |> lift true (module RecursionTermLifter)(*TODO: should we really always evaluate it???*) - |> lift (get_bool "ana.dead-code.branches") (module DeadBranchLifter2) - ) in + ) in GobConfig.building_spec := false; ControlSpecC.control_spec_c := (module S1.C); (module S1) From 478cb410e4c84154025ef81c660050ccb08b858d Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Fri, 16 Jun 2023 15:11:39 +0200 Subject: [PATCH 0130/1312] updated hash and compare of tuple --- src/framework/analyses.ml | 39 ++++-- src/framework/constraints.ml | 245 +---------------------------------- 2 files changed, 34 insertions(+), 250 deletions(-) diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 0a7ccd04b2..90be60b779 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -124,15 +124,18 @@ module C_ (C: Printable.S)= struct include C include Printable.Std (* To make it Groupable *) - let printXml f c = BatPrintf.fprintf f "%a" printXml c (* wrap in for HTML printing *) + let printXml f c = BatPrintf.fprintf f (*Todo: Make this print pretty*) + " + callee_context:\n%a\n\n + " printXml c (* wrap in for HTML printing *) end (* Tuple of fundec and S.C*) -module T (Base1: Printable.S) (Base2: Printable.S) (C: Printable.S) = (*Todo: is this Printable.S or S.C*) +module T (Base1: Printable.S) (Base2: Printable.S) = (*Todo: is this Printable.S or S.C*) struct include Printable.Std - type t = (CilType.Fundec.t * C.t) + type t = (CilType.Fundec.t * Base2.t) let equal (a1, b1) (a2, b2) = if (a1 = a2) && (b1 = b2) then true else false let show (a,b) = (Base1.show a) ^ (Base2.show b) @@ -143,16 +146,30 @@ struct BatPrintf.fprintf f "\n Tuple:\n \n - fundec\n%a\n\n - context\n%a\n\n - \n" Base1.printXml a Base2.printXml b (*Todo: what do we have to put here?*) - let compare (a1,b1) (a2,b2) = 3 (*Todo: what do we have to put here?*) - (*let a = Base1.compare a1 a2 in - let b = Base2.compare b1 b2 in - *) + caller_fundec\n%a\n\n + caller_context\n%a\n\n + \n" Base1.printXml a Base2.printXml b + + (*Result of compare: + start with inital value of 0 + - a1 > a2: +1 + - a1 < a2: -1 + - b1 > b2: +3 + - b1 < b2: -3 + *) + let compare (a1,b1) (a2,b2) = (*Todo: is this ok?*) + let res = ref 0 in + let comp_a = Base1.compare a1 a2 in + let comp_b = Base2.compare b1 b2 in + if (comp_a > 0) then res := !(res) + 1 + else if (comp_a < 0) then res := !(res) - 1; + if (comp_b > 0) then res := !(res) + 3 + else if (comp_b < 0) then res := !(res) - 3; + !res + let pretty () x = text (show x) - let hash (a,b) = 2 (*Todo: what do we have to put here?*) + let hash (a,b) = Hashtbl.hash (Base1.hash a * Base2.hash b) (*Todo: is this ok?*) end diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 31843794ec..143d417cc6 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1694,237 +1694,6 @@ struct let event ctx e octx = S.event (conv ctx) e (conv octx) end -(* -module RecursionTermLifter (S: Spec): Spec = -struct - include S - - let name () = "RecursionTerm (" ^ S.name () ^ ")" - - (* global invariant: - - fundec -> (S.C -> Set (fundec * S.C)) -- used to detect loops in the call graph *) - - module V = - struct - include Printable.Option (S.V) (struct let name = "RecursionTerm" end) - let name () = "RecursionTerm" - let is_write_only t = true - let s x = `Left x - end - - module C_ = - struct - include S.C - include Printable.Std (* To make it Groupable *) - let printXml f c = BatPrintf.fprintf f "%a" printXml c (* wrap in for HTML printing *) - - end - - (*module Tuple = struct - type t = (fundec, S.C) [@@deriving eq, ord, hash] - let equal t1 t2 = false - let compare t1 t3 = 0 - let show t = "t" - let pretty () (x: t) = match x with _ -> . - - let printXml f (d,m) = BatPrintf.fprintf f "%a" printXml c (* wrap in for HTML printing *) - - let name u = "recursion" - let to_yojson (x: t) = match x with _ -> . - - let tag t = 1 - let arbitrary () = failwith "Printable.Empty.arbitrary" - - let relift t = t - end -*) - module Tupel (S:Spec) = - struct - include Printable.Std - type t = fundec * S.C.t [@@deriving eq, ord, hash] - - let equal_fundec = false - let hash_fundec = false - - let name () = "recursion" - - let pretty () (x: t) = match x with _ -> . - - let relift (f, c) = - (f, c) - - let equal t1 t2 = false - let compare t1 t3 = 0 - let show t = "t" - - let printXml f c = BatPrintf.fprintf f "%a" c (* wrap in for HTML printing *) - - let name u = "recursion" - let to_yojson (x: t) = match x with _ -> . - - let tag t = 1 - let arbitrary () = failwith "Printable.Empty.arbitrary" - end - - module T = - struct - include SetDomain.Make (Tupel (S)) - end - - module EM = - struct - include MapDomain.MapBot (C_) (T) - let name () = "recursions" - end - - module G = - struct - include Lattice.Lift2 (S.G) (EM) (Printable.DefaultNames) (*Todo: do we need lift2?*) - let name () = "recursionTerm" - let node = function - | `Bot -> EM.bot () - | `Lifted2 x -> x - | _ -> failwith "DeadBranchLifter.node" - let create_s s = `Lifted1 s - let create_node node = `Lifted2 node - - let printXml f = function - | `Lifted1 x -> S.G.printXml f x - | `Lifted2 x -> BatPrintf.fprintf f "%a" EM.printXml x - | x -> BatPrintf.fprintf f "%a" printXml x - end - - - - let conv (ctx: (_, G.t, _, V.t) ctx): (_, S.G.t, _, S.V.t) ctx = - { ctx with - global = (fun v -> G.s (ctx.global (V.s v))); - sideg = (fun v g -> ctx.sideg (V.s v) (G.create_s g)); - } - - let query ctx (type a) (q: a Queries.t): a Queries.result = - match q with - | WarnGlobal g -> - let g: V.t = Obj.obj g in - begin match g with - | `Left g -> - S.query (conv ctx) (WarnGlobal (Obj.repr g)) - | `Right g -> - let em = G.node (ctx.global (V.node g)) in - EM.iter (fun exp tv -> - match tv with - | `Lifted tv -> - let loc = Node.location g in (* TODO: looking up location now doesn't work nicely with incremental *) - let cilinserted = if loc.synthetic then "(possibly inserted by CIL) " else "" in - M.warn ~loc:(Node g) ~tags:[CWE (if tv then 571 else 570)] ~category:Deadcode "condition '%a' %sis always %B" d_exp exp cilinserted tv - | `Bot when not (CilType.Exp.equal exp one) -> (* all branches dead *) - M.error ~loc:(Node g) ~category:Analyzer ~tags:[Category Unsound] "both branches over condition '%a' are dead" d_exp exp - | `Bot (* all branches dead, fine at our inserted Neg(1)-s because no Pos(1) *) - | `Top -> (* may be both true and false *) - () - ) em; - end - | InvariantGlobal g -> - let g: V.t = Obj.obj g in - begin match g with - | `Left g -> - S.query (conv ctx) (InvariantGlobal (Obj.repr g)) - | `Right g -> - Queries.Result.top q - end - | IterSysVars (vq, vf) -> - (* vars for S *) - let vf' x = vf (Obj.repr (V.s (Obj.obj x))) in - S.query (conv ctx) (IterSysVars (vq, vf')); - - (* node vars for dead branches *) - begin match vq with - | Node {node; _} -> - vf (Obj.repr (V.node node)) - | _ -> - () - end - | _ -> - S.query (conv ctx) q - - - let branch ctx = S.branch (conv ctx) - - let branch ctx exp tv = - if !AnalysisState.postsolving then ( - try - let r = branch ctx exp tv in - (* branch is live *) - ctx.sideg (V.node ctx.prev_node) (G.create_node (EM.singleton exp (`Lifted tv))); (* record expression with reached tv *) - r - with Deadcode -> - (* branch is dead *) - ctx.sideg (V.node ctx.prev_node) (G.create_node (EM.singleton exp `Bot)); (* record expression without reached tv *) - raise Deadcode - ) - else ( - ctx.sideg (V.node ctx.prev_node) (G.create_node (EM.bot ())); (* create global variable during solving, to allow postsolving leq hack to pass verify *) - branch ctx exp tv - ) - - let assign ctx = S.assign (conv ctx) - let vdecl ctx = S.vdecl (conv ctx) - let enter ctx = S.enter (conv ctx) - let paths_as_set ctx = S.paths_as_set (conv ctx) - let body ctx = S.body (conv ctx) - let return ctx = S.return (conv ctx) - let combine_env ctx = S.combine_env (conv ctx) - let combine_assign ctx = S.combine_assign (conv ctx) - let special ctx = S.special (conv ctx) - let threadenter ctx = S.threadenter (conv ctx) - let threadspawn ctx lv f args fctx = S.threadspawn (conv ctx) lv f args (conv fctx) - let sync ctx = S.sync (conv ctx) - let skip ctx = S.skip (conv ctx) - let asm ctx = S.asm (conv ctx) - let event ctx e octx = S.event (conv ctx) e (conv octx) -end -*) -(*GMapG (S.G) (S.C)*) -(*struct - include Lattice.Prod (S.G) (M) - let printXml f (d,m) = BatPrintf.fprintf f "\n%a\n%a\n" S.G.printXml d M.printXml m - end*) - - -(*module type FundecType = - sig - type t = fundec - - val getFundec: t -> fundec - (* Define any other values or types exposed by the module *) - end - - module Fundec (F:fundec) : FundecType = - struct - let getFundec = F - let fname = F.fname - end*) - (* - - module CVal = - struct - include C - include Printable.Std (* To make it Groupable *) - let printXml f c = BatPrintf.fprintf f "%a" printXml c (* wrap in for HTML printing *) - end - module M = MapDomain.MapBot (CVal) (CVal)*) (*(*TODO: do I need to change V???*) - struct - include Printable.Option (S.V) (struct let name = "RecursionTerm" end) - let name () = "RecursionTerm" - let is_write_only t = true - let s x = `Left x - end*) -(*include Lattice.Lift2 (S.G) (MapDomain.MapBot (C_) (TSet)) (Printable.DefaultNames) (*TODO: does MapBot fit?*) - let s = function - | `Bot -> S.G.bot () - | `Lifted1 x -> x - | _ -> failwith "RecursionTerm.s" - end*) (** Add cycle detection in the function call graph to a analysis *) module RecursionTermLifter (S: Spec) @@ -1944,14 +1713,10 @@ struct let s = spec end - module G = GVarGG (S.G) (S.C) (T (CilType.Fundec) (C) (C)) + module G = GVarGG (S.G) (S.C) (T (CilType.Fundec) (C)) let name () = "RecursionTerm (" ^ S.name () ^ ")" - (**let side_context sideg f c = - if !AnalysisState.postsolving then - sideg (f) (G.create_contexts (G.CSet.singleton c))*) - (*TODO Change the body??*) let conv (ctx: (_, G.t, _, V.t) ctx): (_, S.G.t, _, S.V.t) ctx = { ctx with @@ -1968,6 +1733,8 @@ struct | `Left g -> S.query (conv ctx) (WarnGlobal (Obj.repr g)) | `Right g -> + (*TODO: Implement cycle detection algorithm here*) + Queries.Result.top q end | InvariantGlobal g -> @@ -1992,18 +1759,18 @@ struct sideg (V.contexts f) (G.create_contexts (G.CMap.singleton (c) (t))) let enter ctx = S.enter (conv ctx) - let paths_as_set ctx = S.paths_as_set (conv ctx) let body ctx = S.body (conv ctx) let return ctx = S.return (conv ctx) let combine_env ctx r fe f args fc es f_ask = (*Todo*) if !AnalysisState.postsolving then - let c_r: unit -> S.C.t = ctx.context in (*Caller context*) (*TODO is this the caller or callee context???*) + let c_r: S.C.t = ctx.context () in (*Caller context*) (*TODO is this the caller or callee context???*) let nodeF = ctx.node in let fd_r : fundec = Node.find_fundec nodeF in (*Caller fundec*) let c_e: S.C.t = Option.get (fc) in (*Callee context*) let fd_e : fundec = f in (*Callee fundec*) - let tup: (fundec * S.C.t) = (fd_r, (c_r ())) in + + let tup: (fundec * S.C.t) = (fd_r, c_r) in let t = G.CSet.singleton (tup) in (*TODO do we fill the set correctly???*) side_context ctx.sideg fd_e (c_e) t; S.combine_env (conv ctx) r fe f args fc es f_ask From 6a1445ac56ddf7235ab74768067cd08a47e163a2 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Fri, 16 Jun 2023 19:06:43 +0300 Subject: [PATCH 0131/1312] Convert some more lib-funs to new specifications --- src/analyses/libraryFunctions.ml | 70 ++++++++++++++++---------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index f2fd7e0d41..e3fce8718a 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -40,13 +40,18 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("fgets", unknown [drop "str" [r; w]; drop "count" []; drop "stream" [r_deep; w_deep]]); ("fopen", unknown [drop "pathname" [r]; drop "mode" [r]]); ("fdopen", unknown [drop "fd" []; drop "mode" [r]]); + ("printf", unknown (drop "format" [r] :: VarArgs (drop' []))); (* TODO: why not r for VarArgs?*) ("fprintf", unknown (drop "stream" [r_deep; w_deep] :: drop "format" [r] :: VarArgs (drop' []))); (* TODO: why not r for VarArgs?*) - ("fputc", unknown [drop "ch" []; drop "stream" [r_deep; w_deep];]); - ("fputs", unknown [drop "str" [r]; drop "stream" [r_deep; w_deep];]); + ("sprintf", unknown (drop "buffer" [r_deep; w_deep] :: drop "format" [r] :: VarArgs (drop' []))); (* TODO: why not r for VarArgs?*) + ("snprintf", unknown (drop "buffer" [r_deep; w_deep] :: drop "bufsz" [] :: drop "format" [r] :: VarArgs (drop' []))); (* TODO: why not r for VarArgs?*) + ("fputc", unknown [drop "ch" []; drop "stream" [r_deep; w_deep]]); + ("putc", unknown [drop "ch" []; drop "stream" [r_deep; w_deep]]); + ("fputs", unknown [drop "str" [r]; drop "stream" [r_deep; w_deep]]); ("fread", unknown [drop "buffer" [w_deep]; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); ("fseek", unknown [drop "stream" [r_deep; w_deep]; drop "offset" []; drop "origin" []]); ("ftell", unknown [drop "stream" [r_deep]]); ("fwrite", unknown [drop "buffer" [r_deep]; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); + ("rewind", unknown [drop "stream" [r_deep; w_deep]]); ("setvbuf", unknown [drop "stream" [r_deep; w_deep]; drop "buffer" [r_deep; w_deep]; drop "mode" []; drop "size" []]); (* TODO: if this is used to set an input buffer, the buffer (second argument) would need to remain TOP, *) (* as any future write (or flush) of the stream could result in a write to the buffer *) @@ -64,7 +69,9 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("abort", special [] Abort); ("exit", special [drop "exit_code" []] Abort); ("ungetc", unknown [drop "c" []; drop "stream" [r; w]]); - ("fscanf", unknown ((drop "stream" [r; w]) :: (drop "format" [r]) :: (VarArgs (drop' [w])))); + ("scanf", unknown ((drop "format" [r]) :: (VarArgs (drop' [w])))); + ("fscanf", unknown ((drop "stream" [r; w]) :: (drop "format" [r]) :: (VarArgs (drop' [w])))); (* TODO: why stream not r_deep; w_deep? *) + ("sscanf", unknown ((drop "buffer" [r]) :: (drop "format" [r]) :: (VarArgs (drop' [w])))); ("__freading", unknown [drop "stream" [r]]); ("mbsinit", unknown [drop "ps" [r]]); ("mbrtowc", unknown [drop "pwc" [w]; drop "s" [r]; drop "n" []; drop "ps" [r; w]]); @@ -72,17 +79,27 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("iswalnum", unknown [drop "wc" []]); ("iswprint", unknown [drop "wc" []]); ("rename" , unknown [drop "oldpath" [r]; drop "newpath" [r];]); + ("perror", unknown [drop "s" [r]]); + ("getchar", unknown []); + ("putchar", unknown [drop "ch" []]); ("puts", unknown [drop "s" [r]]); ("rand", unknown ~attrs:[ThreadUnsafe] []); ("strspn", unknown [drop "s" [r]; drop "accept" [r]]); ("strcspn", unknown [drop "s" [r]; drop "accept" [r]]); + ("strftime", unknown [drop "str" [w]; drop "count" []; drop "format" [r]; drop "tp" [r]]); ("strtod", unknown [drop "nptr" [r]; drop "endptr" [w]]); ("strtol", unknown [drop "nptr" [r]; drop "endptr" [w]; drop "base" []]); ("__strtol_internal", unknown [drop "nptr" [r]; drop "endptr" [w]; drop "base" []; drop "group" []]); ("strtoll", unknown [drop "nptr" [r]; drop "endptr" [w]; drop "base" []]); ("strtoul", unknown [drop "nptr" [r]; drop "endptr" [w]; drop "base" []]); ("strtoull", unknown [drop "nptr" [r]; drop "endptr" [w]; drop "base" []]); + ("tolower", unknown [drop "ch" []]); + ("toupper", unknown [drop "ch" []]); + ("time", unknown [drop "arg" [w]]); ("tmpnam", unknown ~attrs:[ThreadUnsafe] [drop "filename" [r]]); + ("vprintf", unknown [drop "format" [r]; drop "vlist" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) + ("vfprintf", unknown [drop "stream" [r_deep; w_deep]; drop "format" [r]; drop "vlist" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) + ("vsprintf", unknown [drop "buffer" [w]; drop "format" [r]; drop "vlist" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) ("mktime", unknown [drop "tm" [r;w]]); ("ctime", unknown ~attrs:[ThreadUnsafe] [drop "rm" [r]]); ("clearerr", unknown [drop "stream" [w]]); @@ -220,7 +237,22 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("timer_getoverrun", unknown [drop "timerid" []]); ("lstat", unknown [drop "pathname" [r]; drop "statbuf" [w]]); ("getpwnam", unknown [drop "name" [r]]); + ("chdir", unknown [drop "path" [r]]); + ("closedir", unknown [drop "dirp" [w]]); + ("mkdir", unknown [drop "pathname" [r]; drop "mode" []]); + ("opendir", unknown [drop "name" [r]]); + ("rmdir", unknown [drop "path" [r]]); + ("open", unknown (drop "pathname" [r] :: drop "flags" [] :: VarArgs (drop "mode" []))); + ("read", unknown [drop "fd" []; drop "buf" [w]; drop "count" []]); + ("write", unknown [drop "fd" []; drop "buf" [r]; drop "count" []]); + ("recv", unknown [drop "sockfd" []; drop "buf" [w]; drop "len" []; drop "flags" []]); + ("send", unknown [drop "sockfd" []; drop "buf" [r]; drop "len" []; drop "flags" []]); + ("strdup", unknown [drop "s" [r]]); ("strndup", unknown [drop "s" [r]; drop "n" []]); + ("syscall", unknown (drop "number" [] :: VarArgs (drop' [r; w]))); + ("sysconf", unknown [drop "name" []]); + ("syslog", unknown (drop "priority" [] :: drop "format" [r] :: VarArgs (drop' [r]))); (* TODO: is the VarArgs correct here? *) + ("vsyslog", unknown [drop "priority" []; drop "format" [r]; drop "ap" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) ("freeaddrinfo", unknown [drop "res" [f_deep]]); ("getgid", unknown []); ("pselect", unknown [drop "nfds" []; drop "readdfs" [r]; drop "writedfs" [r]; drop "exceptfds" [r]; drop "timeout" [r]; drop "sigmask" [r]]); @@ -929,29 +961,11 @@ open Invalidate let invalidate_actions = [ "atoi", readsAll; (*safe*) "connect", readsAll; (*safe*) - "printf", readsAll;(*safe*) "__printf_chk", readsAll;(*safe*) "printk", readsAll;(*safe*) - "perror", readsAll;(*safe*) "__mutex_init", readsAll;(*safe*) - "read", writes [2];(*keep [2]*) - "recv", writes [2];(*keep [2]*) - "scanf", writesAllButFirst 1 readsAll;(*drop 1*) - "send", readsAll;(*safe*) - "snprintf", writes [1];(*keep [1]*) "__builtin___snprintf_chk", writes [1];(*keep [1]*) - "sprintf", writes [1];(*keep [1]*) - "sscanf", writesAllButFirst 2 readsAll;(*drop 2*) - "strftime", writes [1];(*keep [1]*) - "strdup", readsAll;(*safe*) - "toupper", readsAll;(*safe*) - "tolower", readsAll;(*safe*) - "time", writesAll;(*unsafe*) - "vfprintf", writes [1];(*keep [1]*) "__vfprintf_chk", writes [1];(*keep [1]*) - "vprintf", readsAll;(*safe*) - "vsprintf", writes [1];(*keep [1]*) - "write", readsAll;(*safe*) "__builtin_va_arg", readsAll;(*safe*) "__builtin_va_end", readsAll;(*safe*) "__builtin_va_start", readsAll;(*safe*) @@ -965,13 +979,10 @@ let invalidate_actions = [ "__strdup", readsAll;(*safe*) "strtoul__extinline", readsAll;(*safe*) "geteuid", readsAll;(*safe*) - "opendir", readsAll; (*safe*) "readdir_r", writesAll;(*unsafe*) "atoi__extinline", readsAll;(*safe*) "getpid", readsAll;(*safe*) "_IO_getc", writesAll;(*unsafe*) - "closedir", writesAll;(*unsafe*) - "chdir", readsAll;(*safe*) "pipe", writesAll;(*unsafe*) "close", writesAll;(*unsafe*) "setsid", readsAll;(*safe*) @@ -995,15 +1006,12 @@ let invalidate_actions = [ "__builtin___memmove_chk", writes [2;3];(*keep [2;3]*) "waitpid", readsAll;(*safe*) "statfs", writes [1;3;4];(*keep [1;3;4]*) - "mkdir", readsAll;(*safe*) "mount", readsAll;(*safe*) - "open", readsAll;(*safe*) "__open_alias", readsAll;(*safe*) "__open_2", readsAll;(*safe*) "ioctl", writesAll;(*unsafe*) "fstat__extinline", writesAll;(*unsafe*) "umount", readsAll;(*safe*) - "rmdir", readsAll;(*safe*) "strrchr", readsAll;(*safe*) "scandir", writes [1;3;4];(*keep [1;3;4]*) "unlink", readsAll;(*safe*) @@ -1015,15 +1023,8 @@ let invalidate_actions = [ "bindtextdomain", readsAll;(*safe*) "textdomain", readsAll;(*safe*) "dcgettext", readsAll;(*safe*) - "syscall", writesAllButFirst 1 readsAll;(*drop 1*) - "sysconf", readsAll; - "rewind", writesAll; - "putc", readsAll;(*safe*) "putw", readsAll;(*safe*) - "putchar", readsAll;(*safe*) - "getchar", readsAll;(*safe*) "__getdelim", writes [3];(*keep [3]*) - "vsyslog", readsAll;(*safe*) "gethostbyname_r", readsAll;(*safe*) "__h_errno_location", readsAll;(*safe*) "__fxstat", readsAll;(*safe*) @@ -1050,7 +1051,6 @@ let invalidate_actions = [ "vsnprintf", writesAllButFirst 3 readsAll; (*drop 3*) "__builtin___vsnprintf", writesAllButFirst 3 readsAll; (*drop 3*) "__builtin___vsnprintf_chk", writesAllButFirst 3 readsAll; (*drop 3*) - "syslog", readsAll; (*safe*) "strcasecmp", readsAll; (*safe*) "strchr", readsAll; (*safe*) "__error", readsAll; (*safe*) From 7ac939a00cfcb4b9d96c16bd1bc0e3c8740efc72 Mon Sep 17 00:00:00 2001 From: Isidor Zweckstetter Date: Fri, 16 Jun 2023 22:14:12 +0200 Subject: [PATCH 0132/1312] added implementation for DFS for cycles in Callgraph; Not testet --- src/framework/analyses.ml | 11 +++++++-- src/framework/constraints.ml | 45 ++++++++++++++++++++++++------------ 2 files changed, 39 insertions(+), 17 deletions(-) diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 0a7ccd04b2..a5e250c4ed 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -129,11 +129,13 @@ struct end (* Tuple of fundec and S.C*) -module T (Base1: Printable.S) (Base2: Printable.S) (C: Printable.S) = (*Todo: is this Printable.S or S.C*) +module T (Base1: Printable.S) (Base2: Printable.S) = (*Todo: is this Printable.S or S.C*) struct include Printable.Std - type t = (CilType.Fundec.t * C.t) + type t = (CilType.Fundec.t * Base2.t) + let fundec (a,_) = a + let context (_,b) = b let equal (a1, b1) (a2, b2) = if (a1 = a2) && (b1 = b2) then true else false let show (a,b) = (Base1.show a) ^ (Base2.show b) let name () = "Tuple" @@ -202,6 +204,11 @@ struct | _ -> failwith "RecursionTerm.s" let create_s s = `Lifted1 s (*TODO: does this work? copied from DeadBranch*) + + let base2 instance = + match instance with + | `Lifted2 n -> Some n + | _ -> None end diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 31843794ec..b256d8ecbd 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1941,10 +1941,9 @@ struct module V = struct include GVarF(S.V) - let s = spec end - module G = GVarGG (S.G) (S.C) (T (CilType.Fundec) (C) (C)) + module G = GVarGG (S.G) (S.C) (T (CilType.Fundec) (C)) let name () = "RecursionTerm (" ^ S.name () ^ ")" @@ -1965,19 +1964,35 @@ struct | WarnGlobal g -> let g: V.t = Obj.obj g in begin match g with - | `Left g -> - S.query (conv ctx) (WarnGlobal (Obj.repr g)) - | `Right g -> - Queries.Result.top q - end - | InvariantGlobal g -> - let g: V.t = Obj.obj g in - begin match g with - | `Left g -> - S.query (conv ctx) (InvariantGlobal (Obj.repr g)) - | `Right g -> - Queries.Result.top q - end + | `Left g' -> + S.query (conv ctx) (WarnGlobal (Obj.repr g')) + | `Right g' -> + let module LH = Hashtbl.Make (T (CilType.Fundec) (C)) in + let module LS = Set.Make (T (CilType.Fundec) (C)) in + (* TODO: find all cycles/SCCs *) + let global_visited_calls = LH.create 100 in + + (* DFS *) + let rec iter_call (path_visited_calls: LS.t) (call: T (CilType.Fundec) (C).t) = + if LS.mem call path_visited_calls then + let msgs = + [ + (Pretty.dprintf "lock before:", Some (M.Location.CilLocation locUnknown)); + (Pretty.dprintf "lock after: with", Some (M.Location.CilLocation locUnknown)); + ] in + M.msg_group Warning ~category:Deadlock "Locking order cycle" msgs; + S.query (conv ctx) q + else if not (LH.mem global_visited_calls call) then begin + LH.replace global_visited_calls call (); + let callers = G.CMap.find (ctx.context()) (Option.get (G.base2 (ctx.global (g)))) in (*TODO: how do we get our Map out of g*) + let new_path_visited_calls = LS.add call path_visited_calls in + G.CSet.iter (fun to_call -> + iter_call new_path_visited_calls to_call + ) callers + end + in + S.query (conv ctx) q + end | _ -> S.query (conv ctx) q let branch ctx = S.branch (conv ctx) From df59fd78c1bd6577969e517deb9db4dd8b956b6a Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Sat, 17 Jun 2023 09:57:13 +0200 Subject: [PATCH 0133/1312] just comments --- src/framework/constraints.ml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 143d417cc6..35b553135a 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1734,7 +1734,7 @@ struct S.query (conv ctx) (WarnGlobal (Obj.repr g)) | `Right g -> (*TODO: Implement cycle detection algorithm here*) - + Queries.Result.top q end | InvariantGlobal g -> @@ -1762,16 +1762,16 @@ struct let paths_as_set ctx = S.paths_as_set (conv ctx) let body ctx = S.body (conv ctx) let return ctx = S.return (conv ctx) - let combine_env ctx r fe f args fc es f_ask = (*Todo*) + let combine_env ctx r fe f args fc es f_ask = if !AnalysisState.postsolving then - let c_r: S.C.t = ctx.context () in (*Caller context*) (*TODO is this the caller or callee context???*) + let c_r: S.C.t = ctx.context () in (*Caller context*) let nodeF = ctx.node in let fd_r : fundec = Node.find_fundec nodeF in (*Caller fundec*) let c_e: S.C.t = Option.get (fc) in (*Callee context*) let fd_e : fundec = f in (*Callee fundec*) let tup: (fundec * S.C.t) = (fd_r, c_r) in - let t = G.CSet.singleton (tup) in (*TODO do we fill the set correctly???*) + let t = G.CSet.singleton (tup) in side_context ctx.sideg fd_e (c_e) t; S.combine_env (conv ctx) r fe f args fc es f_ask else From 7f4866caca9b982d5e3036bf685c36c0723bab8a Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Sat, 17 Jun 2023 10:34:59 +0200 Subject: [PATCH 0134/1312] comments, transformed loop_counters to a map to store stmts --- src/analyses/termination_new.ml | 6 +- src/framework/constraints.ml | 269 --------------------------- src/util/terminationPreprocessing.ml | 54 +----- 3 files changed, 10 insertions(+), 319 deletions(-) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index 44b0c7fb5e..4926c23daa 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -6,7 +6,9 @@ open TerminationPreprocessing exception PreProcessing of string -let loop_counters : varinfo list ref = ref [] + +(* contains all loop counter variables (varinfo) and maps them to their corresponding loop statement*) +let loop_counters: stmt VarToStmt.t ref = ref VarToStmt.empty (** Contains the locations of the upjumping gotos *) let upjumping_gotos : location list ref = ref [] @@ -14,7 +16,7 @@ let upjumping_gotos : location list ref = ref [] let loop_exit : varinfo ref = ref (makeVarinfo false "-error" Cil.intType) let is_loop_counter_var (x : varinfo) = - List.mem x !loop_counters + VarToStmt.mem x !loop_counters let is_loop_exit_indicator (x : varinfo) = x = !loop_exit diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 0177f3ea85..07750122a5 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1693,275 +1693,6 @@ struct let event ctx e octx = S.event (conv ctx) e (conv octx) end -(** -module RecursionTermLifter (S: Spec): Spec = -struct - include S - - let name () = "RecursionTerm (" ^ S.name () ^ ")" - - (* global invariant: - - fundec -> (S.C -> Set (fundec * S.C)) -- used to detect loops in the call graph *) - - module V = - struct - include Printable.Option (S.V) (struct let name = "RecursionTerm" end) - let name () = "RecursionTerm" - let is_write_only t = true - let s x = `Left x - end - - module C = - struct - include S.C - include Printable.Std (* To make it Groupable *) - let printXml f c = BatPrintf.fprintf f "%a" printXml c (* wrap in for HTML printing *) - - end - - (*module Tuple = struct - type t = (fundec, S.C) [@@deriving eq, ord, hash] - let equal t1 t2 = false - let compare t1 t3 = 0 - let show t = "t" - let pretty () (x: t) = match x with _ -> . - - let printXml f (d,m) = BatPrintf.fprintf f "%a" printXml c (* wrap in for HTML printing *) - - let name u = "recursion" - let to_yojson (x: t) = match x with _ -> . - - let tag t = 1 - let arbitrary () = failwith "Printable.Empty.arbitrary" - - let relift t = t - end -*) - module Tupel (S:Spec) = - struct - include Printable.Std - type t = fundec * S.C.t [@@deriving eq, ord, hash] - - let equal_fundec = false - let hash_fundec = false - - let name () = "recursion" - - let pretty () (x: t) = match x with _ -> . - - let relift (f, c) = - (f, c) - - let equal t1 t2 = false - let compare t1 t3 = 0 - let show t = "t" - - let printXml f c = BatPrintf.fprintf f "%a" c (* wrap in for HTML printing *) - - let name u = "recursion" - let to_yojson (x: t) = match x with _ -> . - - let tag t = 1 - let arbitrary () = failwith "Printable.Empty.arbitrary" - end - - module T = - struct - include SetDomain.Make (Tupel (S)) - end - - module EM = - struct - include MapDomain.MapBot (C) (T) - let name () = "recursions" - end - - module G = - struct - include Lattice.Lift2 (S.G) (EM) (Printable.DefaultNames) - let name () = "recursionTerm" - let node = function - | `Bot -> EM.bot () - | `Lifted2 x -> x - | _ -> failwith "DeadBranchLifter.node" - let create_s s = `Lifted1 s - let create_node node = `Lifted2 node - - let printXml f = function - | `Lifted1 x -> S.G.printXml f x - | `Lifted2 x -> BatPrintf.fprintf f "%a" EM.printXml x - | x -> BatPrintf.fprintf f "%a" printXml x - end - - let conv (ctx: (_, G.t, _, V.t) ctx): (_, S.G.t, _, S.V.t) ctx = - { ctx with - global = (fun v -> G.s (ctx.global (V.s v))); - sideg = (fun v g -> ctx.sideg (V.s v) (G.create_s g)); - } - - let query ctx (type a) (q: a Queries.t): a Queries.result = - match q with - | WarnGlobal g -> - let g: V.t = Obj.obj g in - begin match g with - | `Left g -> - S.query (conv ctx) (WarnGlobal (Obj.repr g)) - | `Right g -> - let em = G.node (ctx.global (V.node g)) in - EM.iter (fun exp tv -> - match tv with - | `Lifted tv -> - let loc = Node.location g in (* TODO: looking up location now doesn't work nicely with incremental *) - let cilinserted = if loc.synthetic then "(possibly inserted by CIL) " else "" in - M.warn ~loc:(Node g) ~tags:[CWE (if tv then 571 else 570)] ~category:Deadcode "condition '%a' %sis always %B" d_exp exp cilinserted tv - | `Bot when not (CilType.Exp.equal exp one) -> (* all branches dead *) - M.error ~loc:(Node g) ~category:Analyzer ~tags:[Category Unsound] "both branches over condition '%a' are dead" d_exp exp - | `Bot (* all branches dead, fine at our inserted Neg(1)-s because no Pos(1) *) - | `Top -> (* may be both true and false *) - () - ) em; - end - | InvariantGlobal g -> - let g: V.t = Obj.obj g in - begin match g with - | `Left g -> - S.query (conv ctx) (InvariantGlobal (Obj.repr g)) - | `Right g -> - Queries.Result.top q - end - | IterSysVars (vq, vf) -> - (* vars for S *) - let vf' x = vf (Obj.repr (V.s (Obj.obj x))) in - S.query (conv ctx) (IterSysVars (vq, vf')); - - (* node vars for dead branches *) - begin match vq with - | Node {node; _} -> - vf (Obj.repr (V.node node)) - | _ -> - () - end - | _ -> - S.query (conv ctx) q - - - let branch ctx = S.branch (conv ctx) - - let branch ctx exp tv = - if !AnalysisState.postsolving then ( - try - let r = branch ctx exp tv in - (* branch is live *) - ctx.sideg (V.node ctx.prev_node) (G.create_node (EM.singleton exp (`Lifted tv))); (* record expression with reached tv *) - r - with Deadcode -> - (* branch is dead *) - ctx.sideg (V.node ctx.prev_node) (G.create_node (EM.singleton exp `Bot)); (* record expression without reached tv *) - raise Deadcode - ) - else ( - ctx.sideg (V.node ctx.prev_node) (G.create_node (EM.bot ())); (* create global variable during solving, to allow postsolving leq hack to pass verify *) - branch ctx exp tv - ) - - let assign ctx = S.assign (conv ctx) - let vdecl ctx = S.vdecl (conv ctx) - let enter ctx = S.enter (conv ctx) - let paths_as_set ctx = S.paths_as_set (conv ctx) - let body ctx = S.body (conv ctx) - let return ctx = S.return (conv ctx) - let combine_env ctx = S.combine_env (conv ctx) - let combine_assign ctx = S.combine_assign (conv ctx) - let special ctx = S.special (conv ctx) - let threadenter ctx = S.threadenter (conv ctx) - let threadspawn ctx lv f args fctx = S.threadspawn (conv ctx) lv f args (conv fctx) - let sync ctx = S.sync (conv ctx) - let skip ctx = S.skip (conv ctx) - let asm ctx = S.asm (conv ctx) - let event ctx e octx = S.event (conv ctx) e (conv octx) -end - - -(** Add cycle detection in the function call graph to a analysis *) -module RecursionTermLifter (S: Spec) - : Spec with module D = S.D - and module G = S.G - and module C = S.C - and module G = S.G -= - -struct - module C = S.C - module P = S.P - module D = S.D - - (*global invariant - - fundec -> Map (S.C) (Set (fundec * S.C)) - So: g -> {c' -> f, c} - in case f, c --> g, c' *) - - (* - - - module CVal = - struct - include C - include Printable.Std (* To make it Groupable *) - let printXml f c = BatPrintf.fprintf f "%a" printXml c (* wrap in for HTML printing *) - end - module M = MapDomain.MapBot (CVal) (CVal) -*) - module V = (*TODO: do I need to change V???*) - struct - include Printable.Option (S.V) (struct let name = "RecursionTerm" end) - let name () = "RecursionTerm" - let is_write_only t = true - let s x = `Left x - end - module G = S.G - (*GMapG (S.G) (S.C)*) - (*struct - include Lattice.Prod (S.G) (M) - let printXml f (d,m) = BatPrintf.fprintf f "\n%a\n%a\n" S.G.printXml d M.printXml m - end*) - let name () = "RecursionTerm (" ^ S.name () ^ ")" - - type marshal = S.marshal - let init = S.init - let finalize = S.finalize (*TODO*) - - let startstate v = S.startstate v - let exitstate v = S.exitstate v - let morphstate = S.morphstate - - let context = S.context - - (**let side_context sideg f c = - if !AnalysisState.postsolving then - sideg (f) (G.create_contexts (G.CSet.singleton c))*) - - let query ctx = S.query (ctx) - let branch ctx = S.branch (ctx) - let assign ctx = S.assign (ctx) - let vdecl ctx = S.vdecl (ctx) - let enter ctx = - if !AnalysisState.postsolving then - printf "hallo hallo"; - S.enter (ctx) (*TODO*) - let paths_as_set ctx = S.paths_as_set (ctx) - let body ctx = S.body (ctx) - let return ctx = S.return (ctx) - let combine_env ctx = S.combine_env (ctx) - let combine_assign ctx = S.combine_assign (ctx) - let special ctx = S.special (ctx) - let threadenter ctx = S.threadenter (ctx) - let threadspawn ctx lv f args fctx = S.threadspawn (ctx) lv f args (fctx) - let sync ctx = S.sync (ctx) - let skip ctx = S.skip (ctx) - let asm ctx = S.asm (ctx) - let event ctx e octx = S.event (ctx) e (octx) -end -*) module CompareGlobSys (SpecSys: SpecSys) = struct diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index 684733c05f..7e1e477262 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -1,13 +1,8 @@ -(* - code in src/analysis/termination.ml contains loopCounterVisitor which might be interesting - - check if overflow happend with new variable - - how do we deal with nested loops? - - make sure only the analyzed files are appended with the code - - return variables that are newly created - *) - open GoblintCil include Printf +module VarToStmt = Map.Make(CilType.Varinfo);; (* maps varinfos (= loop counter variable) to the statement of the corresponding loop*) + let extract_file_name s = (*There still may be a need to filter more chars*) let ls = String.split_on_char '/' s in (*Assuming '/' as path seperator*) let ls = List.rev ls in @@ -35,7 +30,7 @@ class loopCounterVisitor lc lg le (fd : fundec) = object(self) | Loop (b, loc, eloc, _, _) -> let name = "term"^show_location_id loc in let typ = Cil.intType in - let v = (Cil.makeLocalVar fd name typ) in + let v = (Cil.makeLocalVar fd name typ) in (* Not tested for incremental mode*) let init_stmt = mkStmtOneInstr @@ Set (var v, zero, loc, eloc) in let inc_stmt = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in let exit_stmt = mkStmtOneInstr @@ Set ((var !le), (Lval (var v)), loc, eloc) in @@ -43,7 +38,8 @@ class loopCounterVisitor lc lg le (fd : fundec) = object(self) | cont :: cond :: ss -> b.bstmts <- cont :: inc_stmt :: cond :: ss; (*cont :: cond :: inc_stmt :: ss = it is also possible, but for loops with cond at the end, inc is also at the end*) | _ -> ()); - lc := List.append !lc ([v] : varinfo list); + lc := VarToStmt.add (v: varinfo) (s: stmt) !lc; + (*lc := List.append !lc ([v] : varinfo list);*) let nb = mkBlock [init_stmt; mkStmt s.skind; exit_stmt] in s.skind <- Block nb; s @@ -56,42 +52,4 @@ class loopCounterVisitor lc lg le (fd : fundec) = object(self) s | _ -> s in ChangeDoChildrenPost (s, action); - end - -(* just a test -class loopCounterVisitor (fd : fundec) = object(self) -inherit nopCilVisitor -method! vstmt s = - match s.skind with - | Loop (b, loc, eloc, _, _) -> - let name = "term"^show_location_id loc in - let typ = intType in - let v = Goblintutil.create_var (makeLocalVar fd name ~init:(SingleInit zero) typ) in - let init_stmt = mkStmtOneInstr @@ Set (var v, zero, loc, eloc) in - let inc_stmt = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in - b.bstmts <- inc_stmt :: b.bstmts; - let nb = mkBlock [init_stmt; mkStmt s.skind] in (* init_stmt; *) - ChangeDoChildrenPost (s, (fun _ -> s.skind <- Block(nb); s)) - | _ -> DoChildren -end - -let add_var_loopTerm fd f = - let thisVisitor = new loopCounterVisitor in - visitCilFileSameGlobals (thisVisitor fd ) f*) -(* -let action (fd : fundec) s = - let a s = match s.skind with - | Loop (b, loc, eloc, _, _) -> - let name = "term"^show_location_id loc in - let typ = intType in - let v = Goblintutil.create_var (makeLocalVar fd name ~init:(SingleInit zero) typ) in - let init_stmt = mkStmtOneInstr @@ Set (var v, zero, loc, eloc) in - let inc_stmt = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in - b.bstmts <- inc_stmt :: b.bstmts; - let nb = mkBlock [init_stmt; mkStmt s.skind] in (* *) - s.skind <- Block nb; - s - | _ -> s -in ChangeDoChildrenPost (s, a) -*) - + end \ No newline at end of file From 99e0a068696f9696b030c67dbcbdae4b20ce0872 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Sat, 17 Jun 2023 11:16:02 +0200 Subject: [PATCH 0135/1312] small changes --- runningGob.sh | 1 + src/framework/analyses.ml | 3 +-- src/framework/constraints.ml | 3 +++ tests/regression/55-loop-unrolling/01-simple-cases.c | 3 +++ 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/runningGob.sh b/runningGob.sh index 15007b3e0b..783ad90fec 100755 --- a/runningGob.sh +++ b/runningGob.sh @@ -1,6 +1,7 @@ #!/bin/bash make make install +clear # set options and file for apron execution options_apron="--set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --enable warn.debug" #note: preprocessing first needs to be added to apron diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 890b5c12e8..b70f2c09cd 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -146,8 +146,7 @@ struct let relift (a,b) = (a,b) (*Todo: is this correct?*) let printXml f (a,b) = BatPrintf.fprintf f "\n - Tuple:\n - \n + Tuple:\n caller_fundec\n%a\n\n caller_context\n%a\n\n \n" Base1.printXml a Base2.printXml b diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index c6060f8725..8e4eac1bca 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1740,6 +1740,7 @@ struct (* DFS *) let rec iter_call (path_visited_calls: LS.t) (call: T (CilType.Fundec) (C).t) = if LS.mem call path_visited_calls then + (*Cycle found*) let msgs = [ (Pretty.dprintf "lock before:", Some (M.Location.CilLocation locUnknown)); @@ -1748,6 +1749,7 @@ struct M.msg_group Warning ~category:Deadlock "Locking order cycle" msgs; S.query (conv ctx) q else if not (LH.mem global_visited_calls call) then begin + printf "test\n"; LH.replace global_visited_calls call (); let callers = G.CMap.find (ctx.context()) (Option.get (G.base2 (ctx.global (g)))) in (*TODO: how do we get our Map out of g*) let new_path_visited_calls = LS.add call path_visited_calls in @@ -1785,6 +1787,7 @@ struct let tup: (fundec * S.C.t) = (fd_r, c_r) in let t = G.CSet.singleton (tup) in + side_context ctx.sideg fd_e (c_e) t; S.combine_env (conv ctx) r fe f args fc es f_ask else diff --git a/tests/regression/55-loop-unrolling/01-simple-cases.c b/tests/regression/55-loop-unrolling/01-simple-cases.c index 0073717187..6790add384 100644 --- a/tests/regression/55-loop-unrolling/01-simple-cases.c +++ b/tests/regression/55-loop-unrolling/01-simple-cases.c @@ -31,6 +31,8 @@ void example1(void) __goblint_check(a[0] == 0); // UNKNOWN __goblint_check(a[3] == 3); // UNKNOWN + + example2(); } // Do-while loop simple example @@ -46,6 +48,7 @@ void example2(void) __goblint_check(a[0] == 0); // UNKNOWN __goblint_check(a[3] == 3); // UNKNOWN + example1(); } // Initialization not completed, yet the array representation is not precise From c66a3fb57a499b31e04ee65dd6224cc4455ae038 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Sat, 17 Jun 2023 11:20:57 +0200 Subject: [PATCH 0136/1312] comments --- src/util/terminationPreprocessing.ml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index 7e1e477262..409aa2c2c4 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -30,7 +30,7 @@ class loopCounterVisitor lc lg le (fd : fundec) = object(self) | Loop (b, loc, eloc, _, _) -> let name = "term"^show_location_id loc in let typ = Cil.intType in - let v = (Cil.makeLocalVar fd name typ) in (* Not tested for incremental mode*) + let v = (Cil.makeLocalVar fd name typ) in (*Not tested for incremental mode*) let init_stmt = mkStmtOneInstr @@ Set (var v, zero, loc, eloc) in let inc_stmt = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in let exit_stmt = mkStmtOneInstr @@ Set ((var !le), (Lval (var v)), loc, eloc) in @@ -39,7 +39,6 @@ class loopCounterVisitor lc lg le (fd : fundec) = object(self) b.bstmts <- cont :: inc_stmt :: cond :: ss; (*cont :: cond :: inc_stmt :: ss = it is also possible, but for loops with cond at the end, inc is also at the end*) | _ -> ()); lc := VarToStmt.add (v: varinfo) (s: stmt) !lc; - (*lc := List.append !lc ([v] : varinfo list);*) let nb = mkBlock [init_stmt; mkStmt s.skind; exit_stmt] in s.skind <- Block nb; s From 6e37746024b526fb1835c689681dc50756c9f32f Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Sat, 17 Jun 2023 12:24:55 +0200 Subject: [PATCH 0137/1312] Change loop analysis domain keys to statements Before, the keys of the domain were simply the (loop counter) variables. Now, the keys are the CIL statements which are the analyzed loop. --- output.txt | 62 ++++++++++++++++----------------- src/analyses/termination_new.ml | 27 +++++++------- src/domains/queries.ml | 8 ++--- 3 files changed, 50 insertions(+), 47 deletions(-) diff --git a/output.txt b/output.txt index 07c71d61b9..d751966836 100644 --- a/output.txt +++ b/output.txt @@ -131,7 +131,7 @@ typedef long __intptr_t; typedef unsigned int __socklen_t; #line 215 "/usr/include/x86_64-linux-gnu/bits/types.h" typedef int __sig_atomic_t; -#line 209 "/usr/lib/gcc/x86_64-linux-gnu/11/include/stddef.h" +#line 209 "/usr/lib/gcc/x86_64-linux-gnu/10/include/stddef.h" typedef unsigned long size_t; #line 10 "/usr/include/x86_64-linux-gnu/bits/types/time_t.h" typedef __time_t time_t; @@ -201,12 +201,12 @@ struct __anonstruct___value32_817613185 { unsigned int __high ; }; #line 25 "/usr/include/x86_64-linux-gnu/bits/atomic_wide_counter.h" -union __anonunion___atomic_wide_counter_643133811 { +union __anonunion___atomic_wide_counter_1044835921 { unsigned long long __value64 ; struct __anonstruct___value32_817613185 __value32 ; }; #line 25 "/usr/include/x86_64-linux-gnu/bits/atomic_wide_counter.h" -typedef union __anonunion___atomic_wide_counter_643133811 __atomic_wide_counter; +typedef union __anonunion___atomic_wide_counter_1044835921 __atomic_wide_counter; #line 51 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" struct __pthread_internal_list { struct __pthread_internal_list *__prev ; @@ -261,11 +261,11 @@ typedef unsigned int __tss_t; #line 106 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" typedef unsigned long __thrd_t; #line 108 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" -struct __anonstruct___once_flag_826868709 { +struct __anonstruct___once_flag_1044835922 { int __data ; }; #line 108 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" -typedef struct __anonstruct___once_flag_826868709 __once_flag; +typedef struct __anonstruct___once_flag_1044835922 __once_flag; #line 27 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" typedef unsigned long pthread_t; #line 32 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" @@ -441,16 +441,16 @@ struct __pthread_cleanup_frame { int __do_it ; int __cancel_type ; }; -#line 143 "/usr/lib/gcc/x86_64-linux-gnu/11/include/stddef.h" +#line 143 "/usr/lib/gcc/x86_64-linux-gnu/10/include/stddef.h" typedef long ptrdiff_t; -#line 321 "/usr/lib/gcc/x86_64-linux-gnu/11/include/stddef.h" +#line 321 "/usr/lib/gcc/x86_64-linux-gnu/10/include/stddef.h" typedef int wchar_t; -#line 415 "/usr/lib/gcc/x86_64-linux-gnu/11/include/stddef.h" +#line 415 "/usr/lib/gcc/x86_64-linux-gnu/10/include/stddef.h" struct __anonstruct_max_align_t_896270833 { long long __max_align_ll __attribute__((__aligned__(__alignof__(long long )))) ; long double __max_align_ld __attribute__((__aligned__(__alignof__(long double )))) ; }; -#line 415 "/usr/lib/gcc/x86_64-linux-gnu/11/include/stddef.h" +#line 415 "/usr/lib/gcc/x86_64-linux-gnu/10/include/stddef.h" typedef struct __anonstruct_max_align_t_896270833 max_align_t; /* compiler builtin: void __builtin_va_copy(__builtin_va_list , __builtin_va_list ) ; */ @@ -1683,40 +1683,42 @@ extern void ( __attribute__((__leaf__)) tzset)(void) __attribute__((__nothrow__ extern int daylight ; #line 233 extern long timezone ; -#line 249 +#line 246 extern time_t ( __attribute__((__leaf__)) timegm)(struct tm *__tp ) __attribute__((__nothrow__)) ; -#line 251 +#line 263 extern time_t ( __attribute__((__leaf__)) timelocal)(struct tm *__tp ) __attribute__((__nothrow__)) ; -#line 262 +#line 271 extern int ( __attribute__((__leaf__)) dysize)(int __year ) __attribute__((__nothrow__, __const__)) ; -#line 272 +#line 281 extern int nanosleep(struct timespec const *__requested_time , struct timespec *__remaining ) ; -#line 276 +#line 285 extern int ( __attribute__((__leaf__)) clock_getres)(clockid_t __clock_id , struct timespec *__res ) __attribute__((__nothrow__)) ; -#line 279 -extern int ( __attribute__((__leaf__)) clock_gettime)(clockid_t __clock_id , struct timespec *__tp ) __attribute__((__nothrow__)) ; -#line 282 -extern int ( __attribute__((__leaf__)) clock_settime)(clockid_t __clock_id , struct timespec const *__tp ) __attribute__((__nothrow__)) ; -#line 311 +#line 288 +extern int ( __attribute__((__nonnull__(2), __leaf__)) clock_gettime)(clockid_t __clock_id , + struct timespec *__tp ) __attribute__((__nothrow__)) ; +#line 292 +extern int ( __attribute__((__nonnull__(2), __leaf__)) clock_settime)(clockid_t __clock_id , + struct timespec const *__tp ) __attribute__((__nothrow__)) ; +#line 323 extern int clock_nanosleep(clockid_t __clock_id , int __flags , struct timespec const *__req , struct timespec *__rem ) ; -#line 326 +#line 338 extern int ( __attribute__((__leaf__)) clock_getcpuclockid)(pid_t __pid , clockid_t *__clock_id ) __attribute__((__nothrow__)) ; -#line 331 +#line 343 extern int ( __attribute__((__leaf__)) timer_create)(clockid_t __clock_id , struct sigevent * __restrict __evp , timer_t * __restrict __timerid ) __attribute__((__nothrow__)) ; -#line 336 +#line 348 extern int ( __attribute__((__leaf__)) timer_delete)(timer_t __timerid ) __attribute__((__nothrow__)) ; -#line 340 +#line 352 extern int ( __attribute__((__leaf__)) timer_settime)(timer_t __timerid , int __flags , struct itimerspec const * __restrict __value , struct itimerspec * __restrict __ovalue ) __attribute__((__nothrow__)) ; -#line 345 +#line 357 extern int ( __attribute__((__leaf__)) timer_gettime)(timer_t __timerid , struct itimerspec *__value ) __attribute__((__nothrow__)) ; -#line 364 +#line 376 extern int ( __attribute__((__leaf__)) timer_getoverrun)(timer_t __timerid ) __attribute__((__nothrow__)) ; -#line 371 +#line 383 extern int ( __attribute__((__nonnull__(1), __leaf__)) timespec_get)(struct timespec *__ts , int __base ) __attribute__((__nothrow__)) ; #line 202 "/usr/include/pthread.h" @@ -1827,9 +1829,8 @@ extern void __pthread_unregister_cancel(__pthread_unwind_buf_t *__buf ) ; #line 750 extern void __pthread_unwind_next(__pthread_unwind_buf_t *__buf ) __attribute__((__weak__, __noreturn__)) ; -#line 766 -extern int __sigsetjmp_cancel(struct __cancel_jmp_buf_tag *__env , int __savemask ) __asm__("__sigsetjmp") __attribute__((__returns_twice__, -__nothrow__)) ; +#line 773 +extern int __sigsetjmp(struct __jmp_buf_tag *__env , int __savemask ) __attribute__((__nothrow__)) ; #line 781 extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutex_init)(pthread_mutex_t *__mutex , pthread_mutexattr_t const *__mutexattr ) __attribute__((__nothrow__)) ; @@ -1994,8 +1995,7 @@ extern int ( __attribute__((__leaf__)) pthread_key_delete)(pthread_key_t __key ) extern void *( __attribute__((__leaf__)) pthread_getspecific)(pthread_key_t __key ) __attribute__((__nothrow__)) ; #line 1308 extern int ( __attribute__((__leaf__)) pthread_setspecific)(pthread_key_t __key , - void const *__pointer ) __attribute__((__nothrow__, -__access__(__none__,2))) ; + void const *__pointer ) __attribute__((__nothrow__)) ; #line 1315 extern int ( __attribute__((__nonnull__(2), __leaf__)) pthread_getcpuclockid)(pthread_t __thread_id , __clockid_t *__clock_id ) __attribute__((__nothrow__)) ; diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index 4926c23daa..e722b9a2b2 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -6,13 +6,13 @@ open TerminationPreprocessing exception PreProcessing of string - -(* contains all loop counter variables (varinfo) and maps them to their corresponding loop statement*) -let loop_counters: stmt VarToStmt.t ref = ref VarToStmt.empty +(** Contains all loop counter variables (varinfo) and maps them to their corresponding loop statement. *) +let loop_counters : stmt VarToStmt.t ref = ref VarToStmt.empty (** Contains the locations of the upjumping gotos *) let upjumping_gotos : location list ref = ref [] +(** Indicates the place in the code, right after a loop is exited. *) let loop_exit : varinfo ref = ref (makeVarinfo false "-error" Cil.intType) let is_loop_counter_var (x : varinfo) = @@ -25,10 +25,10 @@ let is_loop_exit_indicator (x : varinfo) = * upjumping goto was already reached. Returns true if no upjumping goto was * reached until now *) let currrently_no_upjumping_gotos (loc : location) = - List.for_all (function (l) -> (l >= loc)) upjumping_gotos.contents + List.for_all (function l -> l >= loc) upjumping_gotos.contents let no_upjumping_gotos () = - (List.length upjumping_gotos.contents) <= 0 + List.length upjumping_gotos.contents = 0 (** Checks whether a variable can be bounded *) let check_bounded ctx varinfo = @@ -45,6 +45,7 @@ struct include Analyses.StdV end +module Statements = Lattice.Flat (CilType.Stmt) (Printable.DefaultNames) (* TODO: Use Basetype.CilStmt instead? *) module Spec : Analyses.MCPSpec = struct @@ -54,7 +55,7 @@ struct let name () = "termination" - module D = MapDomain.MapBot (Basetype.Variables) (BoolDomain.MustBool) + module D = MapDomain.MapBot (Statements) (BoolDomain.MustBool) module C = D module V = FunContextV (* TODO *) @@ -69,12 +70,14 @@ struct match lval, rval with (Var x, NoOffset), _ when is_loop_counter_var x -> (* Assume that the following loop does not terminate *) - D.add x false ctx.local + let loop_statement = VarToStmt.find x !loop_counters in + D.add (`Lifted loop_statement) false ctx.local | (Var y, NoOffset), Lval (Var x, NoOffset) when is_loop_exit_indicator y -> (* Loop exit: Check whether loop counter variable is bounded *) (* TODO: Move *) let is_bounded = check_bounded ctx x in - D.add x is_bounded ctx.local + let loop_statement = VarToStmt.find x !loop_counters in + D.add (`Lifted loop_statement) is_bounded ctx.local | _ -> ctx.local let special ctx (lval : lval option) (f : varinfo) (arglist : exp list) = @@ -82,16 +85,16 @@ struct ctx.local (** Provides information to Goblint *) - (* TODO: Consider gotos and recursion *) + (* TODO: Consider gotos *) let query ctx (type a) (q: a Queries.t): a Queries.result = let open Queries in match q with - | Queries.MustTermLoop v -> - (match D.find_opt v ctx.local with + | Queries.MustTermLoop loop_statement -> + (match D.find_opt (`Lifted loop_statement) ctx.local with Some b -> b | None -> Result.top q) | Queries.MustTermProg -> - D.for_all (fun loop term_info -> term_info) ctx.local + D.for_all (fun _ term_info -> term_info) ctx.local | _ -> Result.top q end diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 022e8e1dee..c5d7d729b6 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -117,7 +117,7 @@ type _ t = | MayAccessed: AccessDomain.EventSet.t t | MayBeTainted: LS.t t | MayBeModifiedSinceSetjmp: JmpBufDomain.BufferEntry.t -> VS.t t - | MustTermLoop: varinfo -> MustBool.t t (** TODO: not sure if it is the MayBool*) + | MustTermLoop: stmt -> MustBool.t t | MustTermProg: MustBool.t t type 'a result = 'a @@ -347,7 +347,7 @@ struct compare (Any q1) (Any q2) | Any (IsHeapVar v1), Any (IsHeapVar v2) -> CilType.Varinfo.compare v1 v2 | Any (IsMultiple v1), Any (IsMultiple v2) -> CilType.Varinfo.compare v1 v2 - | Any (MustTermLoop v1), Any (MustTermLoop v2) -> CilType.Varinfo.compare v1 v2 + | Any (MustTermLoop s1), Any (MustTermLoop s2) -> CilType.Stmt.compare s1 s2 | Any (EvalThread e1), Any (EvalThread e2) -> CilType.Exp.compare e1 e2 | Any (EvalJumpBuf e1), Any (EvalJumpBuf e2) -> CilType.Exp.compare e1 e2 | Any (WarnGlobal vi1), Any (WarnGlobal vi2) -> Stdlib.compare (Hashtbl.hash vi1) (Hashtbl.hash vi2) @@ -386,7 +386,7 @@ struct | Any (IterVars i) -> 0 | Any (PathQuery (i, q)) -> 31 * i + hash (Any q) | Any (IsHeapVar v) -> CilType.Varinfo.hash v - | Any (MustTermLoop v) -> CilType.Varinfo.hash v + | Any (MustTermLoop s) -> CilType.Stmt.hash s | Any (IsMultiple v) -> CilType.Varinfo.hash v | Any (EvalThread e) -> CilType.Exp.hash e | Any (EvalJumpBuf e) -> CilType.Exp.hash e @@ -454,7 +454,7 @@ struct | Any MayBeTainted -> Pretty.dprintf "MayBeTainted" | Any DYojson -> Pretty.dprintf "DYojson" | Any MayBeModifiedSinceSetjmp buf -> Pretty.dprintf "MayBeModifiedSinceSetjmp %a" JmpBufDomain.BufferEntry.pretty buf - | Any (MustTermLoop v) -> Pretty.dprintf "MustTermLoop %a" CilType.Varinfo.pretty v + | Any (MustTermLoop s) -> Pretty.dprintf "MustTermLoop %a" CilType.Stmt.pretty s | Any MustTermProg -> Pretty.dprintf "MustTermProg" end From 5efd011ae3dbd97fb88727c89fead835d33502d5 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Sun, 18 Jun 2023 11:11:31 +0200 Subject: [PATCH 0138/1312] cycle detection should work --- src/framework/analyses.ml | 8 +-- src/framework/constraints.ml | 98 +++++++++++++++++++++++------------- 2 files changed, 69 insertions(+), 37 deletions(-) diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index b70f2c09cd..c6994fff23 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -135,11 +135,11 @@ end module T (Base1: Printable.S) (Base2: Printable.S) = (*Todo: is this Printable.S or S.C*) struct include Printable.Std - type t = (CilType.Fundec.t * Base2.t) + type t = (Base1.t * Base2.t) let fundec (a,_) = a let context (_,b) = b - let equal (a1, b1) (a2, b2) = if (a1 = a2) && (b1 = b2) then true else false + let equal (a1, b1) (a2, b2) = if (Base1.equal a1 a2 && Base2.equal b1 b2) then true else false let show (a,b) = (Base1.show a) ^ (Base2.show b) let name () = "Tuple" let to_yojson x = `String (show x) @@ -159,6 +159,7 @@ struct - b1 < b2: -3 *) let compare (a1,b1) (a2,b2) = (*Todo: is this ok?*) + if equal (a1, b1) (a2, b2) then 0 else( let res = ref 0 in let comp_a = Base1.compare a1 a2 in let comp_b = Base2.compare b1 b2 in @@ -166,7 +167,7 @@ struct else if (comp_a < 0) then res := !(res) - 1; if (comp_b > 0) then res := !(res) + 3 else if (comp_b < 0) then res := !(res) - 3; - !res + !res) let pretty () x = text (show x) @@ -194,6 +195,7 @@ struct struct include MapDomain.MapBot (C_ (C)) (CSet) let printXml f c = BatPrintf.fprintf f "%a" printXml c (*TODO*) + end include Lattice.Lift2 (G) (CMap) (Printable.DefaultNames) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 8e4eac1bca..7d46744c90 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1712,7 +1712,7 @@ struct include GVarF(S.V) end - module G = GVarGG (S.G) (S.C) (T (CilType.Fundec) (C)) + module G = GVarGG (S.G) (S.C) (T (CilType.Fundec) (S.C)) let name () = "RecursionTerm (" ^ S.name () ^ ")" @@ -1723,43 +1723,73 @@ struct sideg = (fun v g -> ctx.sideg (V.spec v) (G.create_s g)); } + let cycleDetection ctx v v' = + let module LH = Hashtbl.Make (T (CilType.Fundec) (S.C)) in + let module LS = Set.Make (T (CilType.Fundec) (S.C)) in + (* TODO: find all cycles/SCCs *) + let global_visited_calls = LH.create 100 in + + (* DFS *) + let rec iter_call (path_visited_calls: LS.t) (call:T (CilType.Fundec) (S.C).t) = + let ((fundec_e:fundec), (context_e: C.t)) = call in (*unpack tuple for later use*) + + if LS.mem call path_visited_calls then ( + (*Cycle found*) + let msgs = + [ + (Pretty.dprintf "The program might not terminate! (Fundec %a is contained in a call graph cycle)\n" CilType.Fundec.pretty fundec_e, Some (M.Location.CilLocation locUnknown)); + ] in + M.msg_group Warning "Recursion cycle" msgs) + else if not (LH.mem global_visited_calls call) then begin + try + LH.replace global_visited_calls call (); + let new_path_visited_calls = LS.add call path_visited_calls in + + let fundec_e_typeV: V.t = V.relift (`Right fundec_e) in + let gmap_opt = G.base2 (ctx.global (fundec_e_typeV)) in + let gmap = Option.get (gmap_opt) in (*might be empty*) + let callers: G.CSet.t = G.CMap.find (context_e) gmap in (*TODO: how do we get our Map out of g*) (*Todo: the context should be the domain of the map*) + G.CSet.iter (fun to_call -> + iter_call new_path_visited_calls to_call + ) callers; + with Invalid_argument _ -> () (* path ended: no cycle*) + end + in + let gmap_opt = G.base2 (ctx.global (v)) in + let gmap = Option.get (gmap_opt) in + (*let c = Option.get(G.CMap.PMap.keys gmap) in *)(*Todo: the context should be the domain of the map*) + G.CMap.iter(fun key value -> + let call = (v', key) in + iter_call LS.empty call + ) gmap (* try all fundec + context pairs that are in the map *) + (*TODO: We may need to add new queries here*) let query ctx (type a) (q: a Queries.t): a Queries.result = match q with - | WarnGlobal g -> - let g: V.t = Obj.obj g in - begin match g with - | `Left g' -> - S.query (conv ctx) (WarnGlobal (Obj.repr g')) - | `Right g' -> - let module LH = Hashtbl.Make (T (CilType.Fundec) (C)) in - let module LS = Set.Make (T (CilType.Fundec) (C)) in - (* TODO: find all cycles/SCCs *) - let global_visited_calls = LH.create 100 in - - (* DFS *) - let rec iter_call (path_visited_calls: LS.t) (call: T (CilType.Fundec) (C).t) = - if LS.mem call path_visited_calls then - (*Cycle found*) + | WarnGlobal v -> + let v: V.t = Obj.obj v in + begin match v with + | `Left v' -> + S.query (conv ctx) (WarnGlobal (Obj.repr v')) + | `Right v' -> + (*Check if the loops terminated*) + match ctx.ask (MustTermProg) with + | false -> (*does not terminate*) let msgs = - [ - (Pretty.dprintf "lock before:", Some (M.Location.CilLocation locUnknown)); - (Pretty.dprintf "lock after: with", Some (M.Location.CilLocation locUnknown)); - ] in - M.msg_group Warning ~category:Deadlock "Locking order cycle" msgs; - S.query (conv ctx) q - else if not (LH.mem global_visited_calls call) then begin - printf "test\n"; - LH.replace global_visited_calls call (); - let callers = G.CMap.find (ctx.context()) (Option.get (G.base2 (ctx.global (g)))) in (*TODO: how do we get our Map out of g*) - let new_path_visited_calls = LS.add call path_visited_calls in - G.CSet.iter (fun to_call -> - iter_call new_path_visited_calls to_call - ) callers - end - in - S.query (conv ctx) q - end + [ + (Pretty.dprintf "The program might not terminate! (Loops)\n", Some (M.Location.CilLocation locUnknown)); + ] in + M.msg_group Warning "Non terminating loops" msgs + | true -> cycleDetection ctx v v' + end + | InvariantGlobal v -> + let v: V.t = Obj.obj v in + begin match v with + | `Left v -> + S.query (conv ctx) (InvariantGlobal (Obj.repr v)) + | `Right v -> + Queries.Result.top q + end | _ -> S.query (conv ctx) q let branch ctx = S.branch (conv ctx) From 8563cc7e438d1811c9ca151287ebc53d940cfcbb Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Sun, 18 Jun 2023 12:39:01 +0200 Subject: [PATCH 0139/1312] Consider upjumping gotos in whole program query and a bit of change towards using a global invariant --- src/analyses/termination_new.ml | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index e722b9a2b2..d610a89fe9 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -21,12 +21,6 @@ let is_loop_counter_var (x : varinfo) = let is_loop_exit_indicator (x : varinfo) = x = !loop_exit -(** Checks whether at the current location (=loc) of the analysis an - * upjumping goto was already reached. Returns true if no upjumping goto was - * reached until now *) -let currrently_no_upjumping_gotos (loc : location) = - List.for_all (function l -> l >= loc) upjumping_gotos.contents - let no_upjumping_gotos () = List.length upjumping_gotos.contents = 0 @@ -39,14 +33,18 @@ let check_bounded ctx varinfo = | `Lifted v -> not (is_top_of (ikind v) v) | `Bot -> raise (PreProcessing "Loop variable is Bot") -module FunContextV : Analyses.SpecSysVar = +module UnitV : SpecSysVar = struct - include Printable.Prod (CilType.Fundec) (CilType.Fundec) (* TODO *) - include Analyses.StdV + include Printable.Unit + include StdV end +(** We want to record termination information of loops and use the loop + * statements for that. We use this lifting because we need to have a + * lattice. *) module Statements = Lattice.Flat (CilType.Stmt) (Printable.DefaultNames) (* TODO: Use Basetype.CilStmt instead? *) +(** The termination analysis considering loops and gotos *) module Spec : Analyses.MCPSpec = struct @@ -55,15 +53,13 @@ struct let name () = "termination" - module D = MapDomain.MapBot (Statements) (BoolDomain.MustBool) + module D = MapDomain.MapBot (Statements) (BoolDomain.MustBool) (* TODO *) module C = D - module V = FunContextV - (* TODO *) + module V = UnitV + module G = MapDomain.MapBot (Statements) (BoolDomain.MustBool) let startstate _ = D.bot () - let exitstate = startstate (* TODO *) - - let finalize () = () (* TODO *) + let exitstate = startstate let assign ctx (lval : lval) (rval : exp) = (* Detect assignment to loop counter variable *) @@ -85,7 +81,6 @@ struct ctx.local (** Provides information to Goblint *) - (* TODO: Consider gotos *) let query ctx (type a) (q: a Queries.t): a Queries.result = let open Queries in match q with @@ -95,6 +90,7 @@ struct | None -> Result.top q) | Queries.MustTermProg -> D.for_all (fun _ term_info -> term_info) ctx.local + && no_upjumping_gotos () | _ -> Result.top q end From d786f98faad490c4957c8fd8d09e9870abdba14b Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Sun, 18 Jun 2023 18:11:11 +0200 Subject: [PATCH 0140/1312] WIP on using find_loop_heads and global invariant --- src/analyses/termination_new.ml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index d610a89fe9..5e56c74a05 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -6,6 +6,17 @@ open TerminationPreprocessing exception PreProcessing of string +(* + * TODO: Make this work +module FileCfg = +struct + let file = !Cilfacade.current_file + module Cfg = (val !MyCFG.current_cfg) +end + +let loop_heads = WitnessUtil.find_loop_heads FileCfg + *) + (** Contains all loop counter variables (varinfo) and maps them to their corresponding loop statement. *) let loop_counters : stmt VarToStmt.t ref = ref VarToStmt.empty @@ -67,6 +78,10 @@ struct (Var x, NoOffset), _ when is_loop_counter_var x -> (* Assume that the following loop does not terminate *) let loop_statement = VarToStmt.find x !loop_counters in + (* + * TODO: Make the below line work + let () = ctx.sideg (() : V.t) (G.add (`Lifted loop_statement) false ctx.local) in + *) D.add (`Lifted loop_statement) false ctx.local | (Var y, NoOffset), Lval (Var x, NoOffset) when is_loop_exit_indicator y -> (* Loop exit: Check whether loop counter variable is bounded *) From bf44af74d3d864d7dfeb477545aa2f7f4b7b1ffd Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Sun, 18 Jun 2023 21:07:06 +0200 Subject: [PATCH 0141/1312] resolved merge conflict, cycle detection is working, BUT loop results are not correct yet --- src/analyses/termination_new.ml | 10 +++++--- src/framework/analyses.ml | 35 +++++++++++++++------------- src/framework/constraints.ml | 27 +++++++++++---------- src/util/terminationPreprocessing.ml | 9 ++++++- 4 files changed, 49 insertions(+), 32 deletions(-) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index e722b9a2b2..fabd341a23 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -90,11 +90,15 @@ struct let open Queries in match q with | Queries.MustTermLoop loop_statement -> - (match D.find_opt (`Lifted loop_statement) ctx.local with + if no_upjumping_gotos () + then ((match D.find_opt (`Lifted loop_statement) ctx.local with Some b -> b - | None -> Result.top q) + | None -> Result.top q)) + else(Result.top q) | Queries.MustTermProg -> - D.for_all (fun _ term_info -> term_info) ctx.local + if no_upjumping_gotos () + then (D.for_all (fun _ term_info -> term_info) ctx.local) + else (Result.top q) | _ -> Result.top q end diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index c6994fff23..763ffd0e56 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -119,17 +119,6 @@ struct | x -> BatPrintf.fprintf f "%a" printXml x end -(* Make the given module Goupable*) -module C_ (C: Printable.S)= -struct - include C - include Printable.Std (* To make it Groupable *) - let printXml f c = BatPrintf.fprintf f (*Todo: Make this print pretty*) - " - callee_context:\n%a\n\n - " printXml c (* wrap in for HTML printing *) - -end (* Tuple of fundec and S.C*) module T (Base1: Printable.S) (Base2: Printable.S) = (*Todo: is this Printable.S or S.C*) @@ -146,7 +135,7 @@ struct let relift (a,b) = (a,b) (*Todo: is this correct?*) let printXml f (a,b) = BatPrintf.fprintf f "\n - Tuple:\n + Tuple:\n\n caller_fundec\n%a\n\n caller_context\n%a\n\n \n" Base1.printXml a Base2.printXml b @@ -176,6 +165,7 @@ struct end module GVarGG (G: Lattice.S) (C: Printable.S) (Base: Printable.S) = + struct module CSet = struct @@ -186,16 +176,29 @@ struct ) let name () = "contexts" let printXml f a = - BatPrintf.fprintf f "\n\n"; + BatPrintf.fprintf f "\n"; iter (Base.printXml f) a; BatPrintf.fprintf f "\n\n" end + (* Make the given module Goupable*) + module C_Printable (C: Printable.S)= + struct + include C + include Printable.Std (* To make it Groupable *) + let printXml f c = BatPrintf.fprintf f (*Todo: Make this print pretty*) + "\n + callee_context\n%a\n\n + " printXml c (* wrap in for HTML printing *) + end + module CMap = struct - include MapDomain.MapBot (C_ (C)) (CSet) - let printXml f c = BatPrintf.fprintf f "%a" printXml c (*TODO*) - + include MapDomain.MapBot (C_Printable (C)) (CSet) + let printXml f c = BatPrintf.fprintf f " + ContextTupleMap\n + %a\n\n + " printXml c (*TODO*) end include Lattice.Lift2 (G) (CMap) (Printable.DefaultNames) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 7d46744c90..5be58cd27b 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1753,6 +1753,7 @@ struct iter_call new_path_visited_calls to_call ) callers; with Invalid_argument _ -> () (* path ended: no cycle*) + end in let gmap_opt = G.base2 (ctx.global (v)) in @@ -1760,9 +1761,19 @@ struct (*let c = Option.get(G.CMap.PMap.keys gmap) in *)(*Todo: the context should be the domain of the map*) G.CMap.iter(fun key value -> let call = (v', key) in - iter_call LS.empty call + iter_call LS.empty call ) gmap (* try all fundec + context pairs that are in the map *) + let checkTerminating ctx v v' = + (*Check if the loops terminated*) + if ctx.ask Queries.MustTermProg + then (cycleDetection ctx v v') + else(let msgs = + [ + (Pretty.dprintf "The program might not terminate! (Loops)\n", Some (M.Location.CilLocation locUnknown)); + ] in + M.msg_group Warning "Possibly non terminating loops" msgs) + (*TODO: We may need to add new queries here*) let query ctx (type a) (q: a Queries.t): a Queries.result = match q with @@ -1771,17 +1782,8 @@ struct begin match v with | `Left v' -> S.query (conv ctx) (WarnGlobal (Obj.repr v')) - | `Right v' -> - (*Check if the loops terminated*) - match ctx.ask (MustTermProg) with - | false -> (*does not terminate*) - let msgs = - [ - (Pretty.dprintf "The program might not terminate! (Loops)\n", Some (M.Location.CilLocation locUnknown)); - ] in - M.msg_group Warning "Non terminating loops" msgs - | true -> cycleDetection ctx v v' - end + | `Right v' -> checkTerminating ctx v v' + end | InvariantGlobal v -> let v: V.t = Obj.obj v in begin match v with @@ -1790,6 +1792,7 @@ struct | `Right v -> Queries.Result.top q end + | MustTermProgWithRec -> false (*TODO*) | _ -> S.query (conv ctx) q let branch ctx = S.branch (conv ctx) diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index a64e48446b..409aa2c2c4 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -18,6 +18,13 @@ let show_location_id l = class loopCounterVisitor lc lg le (fd : fundec) = object(self) inherit nopCilVisitor + method! vfunc (f:fundec) = + if !le.vname <> "term_exit-" then begin + let exit_name = "term_exit-" in + let typ = Cil.intType in + le := Cil.makeGlobalVar exit_name typ; + end; + DoChildren; (* function definition *) method! vstmt s = let action s = match s.skind with | Loop (b, loc, eloc, _, _) -> @@ -26,7 +33,7 @@ class loopCounterVisitor lc lg le (fd : fundec) = object(self) let v = (Cil.makeLocalVar fd name typ) in (*Not tested for incremental mode*) let init_stmt = mkStmtOneInstr @@ Set (var v, zero, loc, eloc) in let inc_stmt = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in - let exit_stmt = mkStmtOneInstr @@ Call (None, f_bounded, [(Lval(var v))], loc, eloc) in + let exit_stmt = mkStmtOneInstr @@ Set ((var !le), (Lval (var v)), loc, eloc) in (match b.bstmts with | cont :: cond :: ss -> b.bstmts <- cont :: inc_stmt :: cond :: ss; (*cont :: cond :: inc_stmt :: ss = it is also possible, but for loops with cond at the end, inc is also at the end*) From 03dbf0bdf92761f2e1680d90b8156a727856a02a Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Sun, 18 Jun 2023 21:09:33 +0200 Subject: [PATCH 0142/1312] added queries --- src/domains/queries.ml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/domains/queries.ml b/src/domains/queries.ml index c5d7d729b6..6603175f36 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -119,6 +119,7 @@ type _ t = | MayBeModifiedSinceSetjmp: JmpBufDomain.BufferEntry.t -> VS.t t | MustTermLoop: stmt -> MustBool.t t | MustTermProg: MustBool.t t + | MustTermProgWithRec: MustBool.t t type 'a result = 'a @@ -185,6 +186,7 @@ struct | MayBeModifiedSinceSetjmp _ -> (module VS) | MustTermLoop _ -> (module MustBool) | MustTermProg -> (module MustBool) + | MustTermProgWithRec -> (module MustBool) (** Get bottom result for query. *) let bot (type a) (q: a t): a result = @@ -250,6 +252,7 @@ struct | MayBeModifiedSinceSetjmp _ -> VS.top () | MustTermLoop _ -> MustBool.top () | MustTermProg -> MustBool.top () + | MustTermProgWithRec -> MustBool.top () end (* The type any_query can't be directly defined in Any as t, @@ -312,6 +315,7 @@ struct | Any ThreadsJoinedCleanly -> 52 | Any (MustTermLoop _) -> 53 | Any MustTermProg -> 54 + | Any MustTermProgWithRec -> 55 let rec compare a b = let r = Stdlib.compare (order a) (order b) in @@ -456,6 +460,7 @@ struct | Any MayBeModifiedSinceSetjmp buf -> Pretty.dprintf "MayBeModifiedSinceSetjmp %a" JmpBufDomain.BufferEntry.pretty buf | Any (MustTermLoop s) -> Pretty.dprintf "MustTermLoop %a" CilType.Stmt.pretty s | Any MustTermProg -> Pretty.dprintf "MustTermProg" + | Any MustTermProgWithRec -> Pretty.dprintf "MustTermProgWithRec" end let to_value_domain_ask (ask: ask) = From 5457fc36b887c5bd8196e3b2a544150b4b9916d4 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Mon, 19 Jun 2023 13:04:28 +0200 Subject: [PATCH 0143/1312] Use global invariant instead of local state Still needs a bit of work. Currently, loops are only ever analyzed if their loop exit indicator is encountered. --- src/analyses/termination_new.ml | 44 ++++++++++++++++----------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index 5e56c74a05..f062030820 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -6,16 +6,14 @@ open TerminationPreprocessing exception PreProcessing of string -(* - * TODO: Make this work -module FileCfg = -struct - let file = !Cilfacade.current_file - module Cfg = (val !MyCFG.current_cfg) -end - -let loop_heads = WitnessUtil.find_loop_heads FileCfg - *) +let loop_heads = + let module FileCfg = + struct + let file = !Cilfacade.current_file + module Cfg = (val !MyCFG.current_cfg) + end in + let module WitnessInvariant = WitnessUtil.Invariant (FileCfg) in + WitnessInvariant.loop_heads (* TODO: Use this *) (** Contains all loop counter variables (varinfo) and maps them to their corresponding loop statement. *) let loop_counters : stmt VarToStmt.t ref = ref VarToStmt.empty @@ -44,7 +42,7 @@ let check_bounded ctx varinfo = | `Lifted v -> not (is_top_of (ikind v) v) | `Bot -> raise (PreProcessing "Loop variable is Bot") -module UnitV : SpecSysVar = +module UnitV = struct include Printable.Unit include StdV @@ -64,7 +62,7 @@ struct let name () = "termination" - module D = MapDomain.MapBot (Statements) (BoolDomain.MustBool) (* TODO *) + module D = Lattice.Unit module C = D module V = UnitV module G = MapDomain.MapBot (Statements) (BoolDomain.MustBool) @@ -75,20 +73,21 @@ struct let assign ctx (lval : lval) (rval : exp) = (* Detect assignment to loop counter variable *) match lval, rval with + (* (Var x, NoOffset), _ when is_loop_counter_var x -> (* Assume that the following loop does not terminate *) let loop_statement = VarToStmt.find x !loop_counters in - (* - * TODO: Make the below line work - let () = ctx.sideg (() : V.t) (G.add (`Lifted loop_statement) false ctx.local) in - *) + let () = ctx.sideg () (G.add (`Lifted loop_statement) false ctx.local) in + let () = print_endline ("Added FALSE for " ^ x.vname) in D.add (`Lifted loop_statement) false ctx.local - | (Var y, NoOffset), Lval (Var x, NoOffset) when is_loop_exit_indicator y -> + *) + (Var y, NoOffset), Lval (Var x, NoOffset) when is_loop_exit_indicator y -> (* Loop exit: Check whether loop counter variable is bounded *) (* TODO: Move *) let is_bounded = check_bounded ctx x in let loop_statement = VarToStmt.find x !loop_counters in - D.add (`Lifted loop_statement) is_bounded ctx.local + let () = ctx.sideg () (G.add (`Lifted loop_statement) is_bounded (ctx.global ())) in + ctx.local | _ -> ctx.local let special ctx (lval : lval option) (f : varinfo) (arglist : exp list) = @@ -97,16 +96,15 @@ struct (** Provides information to Goblint *) let query ctx (type a) (q: a Queries.t): a Queries.result = - let open Queries in match q with | Queries.MustTermLoop loop_statement -> - (match D.find_opt (`Lifted loop_statement) ctx.local with + (match G.find_opt (`Lifted loop_statement) (ctx.global ()) with Some b -> b - | None -> Result.top q) + | None -> false) | Queries.MustTermProg -> - D.for_all (fun _ term_info -> term_info) ctx.local + G.for_all (fun _ term_info -> term_info) (ctx.global ()) && no_upjumping_gotos () - | _ -> Result.top q + | _ -> Queries.Result.top q end From 13e8b7a2379ae12ce8b755287d1403f9fa71fff7 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Sat, 17 Jun 2023 10:34:59 +0200 Subject: [PATCH 0144/1312] comments, transformed loop_counters to a map to store stmts --- src/analyses/termination_new.ml | 6 +- src/framework/constraints.ml | 269 --------------------------- src/util/terminationPreprocessing.ml | 54 +----- 3 files changed, 10 insertions(+), 319 deletions(-) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index 44b0c7fb5e..4926c23daa 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -6,7 +6,9 @@ open TerminationPreprocessing exception PreProcessing of string -let loop_counters : varinfo list ref = ref [] + +(* contains all loop counter variables (varinfo) and maps them to their corresponding loop statement*) +let loop_counters: stmt VarToStmt.t ref = ref VarToStmt.empty (** Contains the locations of the upjumping gotos *) let upjumping_gotos : location list ref = ref [] @@ -14,7 +16,7 @@ let upjumping_gotos : location list ref = ref [] let loop_exit : varinfo ref = ref (makeVarinfo false "-error" Cil.intType) let is_loop_counter_var (x : varinfo) = - List.mem x !loop_counters + VarToStmt.mem x !loop_counters let is_loop_exit_indicator (x : varinfo) = x = !loop_exit diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 0177f3ea85..07750122a5 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1693,275 +1693,6 @@ struct let event ctx e octx = S.event (conv ctx) e (conv octx) end -(** -module RecursionTermLifter (S: Spec): Spec = -struct - include S - - let name () = "RecursionTerm (" ^ S.name () ^ ")" - - (* global invariant: - - fundec -> (S.C -> Set (fundec * S.C)) -- used to detect loops in the call graph *) - - module V = - struct - include Printable.Option (S.V) (struct let name = "RecursionTerm" end) - let name () = "RecursionTerm" - let is_write_only t = true - let s x = `Left x - end - - module C = - struct - include S.C - include Printable.Std (* To make it Groupable *) - let printXml f c = BatPrintf.fprintf f "%a" printXml c (* wrap in for HTML printing *) - - end - - (*module Tuple = struct - type t = (fundec, S.C) [@@deriving eq, ord, hash] - let equal t1 t2 = false - let compare t1 t3 = 0 - let show t = "t" - let pretty () (x: t) = match x with _ -> . - - let printXml f (d,m) = BatPrintf.fprintf f "%a" printXml c (* wrap in for HTML printing *) - - let name u = "recursion" - let to_yojson (x: t) = match x with _ -> . - - let tag t = 1 - let arbitrary () = failwith "Printable.Empty.arbitrary" - - let relift t = t - end -*) - module Tupel (S:Spec) = - struct - include Printable.Std - type t = fundec * S.C.t [@@deriving eq, ord, hash] - - let equal_fundec = false - let hash_fundec = false - - let name () = "recursion" - - let pretty () (x: t) = match x with _ -> . - - let relift (f, c) = - (f, c) - - let equal t1 t2 = false - let compare t1 t3 = 0 - let show t = "t" - - let printXml f c = BatPrintf.fprintf f "%a" c (* wrap in for HTML printing *) - - let name u = "recursion" - let to_yojson (x: t) = match x with _ -> . - - let tag t = 1 - let arbitrary () = failwith "Printable.Empty.arbitrary" - end - - module T = - struct - include SetDomain.Make (Tupel (S)) - end - - module EM = - struct - include MapDomain.MapBot (C) (T) - let name () = "recursions" - end - - module G = - struct - include Lattice.Lift2 (S.G) (EM) (Printable.DefaultNames) - let name () = "recursionTerm" - let node = function - | `Bot -> EM.bot () - | `Lifted2 x -> x - | _ -> failwith "DeadBranchLifter.node" - let create_s s = `Lifted1 s - let create_node node = `Lifted2 node - - let printXml f = function - | `Lifted1 x -> S.G.printXml f x - | `Lifted2 x -> BatPrintf.fprintf f "%a" EM.printXml x - | x -> BatPrintf.fprintf f "%a" printXml x - end - - let conv (ctx: (_, G.t, _, V.t) ctx): (_, S.G.t, _, S.V.t) ctx = - { ctx with - global = (fun v -> G.s (ctx.global (V.s v))); - sideg = (fun v g -> ctx.sideg (V.s v) (G.create_s g)); - } - - let query ctx (type a) (q: a Queries.t): a Queries.result = - match q with - | WarnGlobal g -> - let g: V.t = Obj.obj g in - begin match g with - | `Left g -> - S.query (conv ctx) (WarnGlobal (Obj.repr g)) - | `Right g -> - let em = G.node (ctx.global (V.node g)) in - EM.iter (fun exp tv -> - match tv with - | `Lifted tv -> - let loc = Node.location g in (* TODO: looking up location now doesn't work nicely with incremental *) - let cilinserted = if loc.synthetic then "(possibly inserted by CIL) " else "" in - M.warn ~loc:(Node g) ~tags:[CWE (if tv then 571 else 570)] ~category:Deadcode "condition '%a' %sis always %B" d_exp exp cilinserted tv - | `Bot when not (CilType.Exp.equal exp one) -> (* all branches dead *) - M.error ~loc:(Node g) ~category:Analyzer ~tags:[Category Unsound] "both branches over condition '%a' are dead" d_exp exp - | `Bot (* all branches dead, fine at our inserted Neg(1)-s because no Pos(1) *) - | `Top -> (* may be both true and false *) - () - ) em; - end - | InvariantGlobal g -> - let g: V.t = Obj.obj g in - begin match g with - | `Left g -> - S.query (conv ctx) (InvariantGlobal (Obj.repr g)) - | `Right g -> - Queries.Result.top q - end - | IterSysVars (vq, vf) -> - (* vars for S *) - let vf' x = vf (Obj.repr (V.s (Obj.obj x))) in - S.query (conv ctx) (IterSysVars (vq, vf')); - - (* node vars for dead branches *) - begin match vq with - | Node {node; _} -> - vf (Obj.repr (V.node node)) - | _ -> - () - end - | _ -> - S.query (conv ctx) q - - - let branch ctx = S.branch (conv ctx) - - let branch ctx exp tv = - if !AnalysisState.postsolving then ( - try - let r = branch ctx exp tv in - (* branch is live *) - ctx.sideg (V.node ctx.prev_node) (G.create_node (EM.singleton exp (`Lifted tv))); (* record expression with reached tv *) - r - with Deadcode -> - (* branch is dead *) - ctx.sideg (V.node ctx.prev_node) (G.create_node (EM.singleton exp `Bot)); (* record expression without reached tv *) - raise Deadcode - ) - else ( - ctx.sideg (V.node ctx.prev_node) (G.create_node (EM.bot ())); (* create global variable during solving, to allow postsolving leq hack to pass verify *) - branch ctx exp tv - ) - - let assign ctx = S.assign (conv ctx) - let vdecl ctx = S.vdecl (conv ctx) - let enter ctx = S.enter (conv ctx) - let paths_as_set ctx = S.paths_as_set (conv ctx) - let body ctx = S.body (conv ctx) - let return ctx = S.return (conv ctx) - let combine_env ctx = S.combine_env (conv ctx) - let combine_assign ctx = S.combine_assign (conv ctx) - let special ctx = S.special (conv ctx) - let threadenter ctx = S.threadenter (conv ctx) - let threadspawn ctx lv f args fctx = S.threadspawn (conv ctx) lv f args (conv fctx) - let sync ctx = S.sync (conv ctx) - let skip ctx = S.skip (conv ctx) - let asm ctx = S.asm (conv ctx) - let event ctx e octx = S.event (conv ctx) e (conv octx) -end - - -(** Add cycle detection in the function call graph to a analysis *) -module RecursionTermLifter (S: Spec) - : Spec with module D = S.D - and module G = S.G - and module C = S.C - and module G = S.G -= - -struct - module C = S.C - module P = S.P - module D = S.D - - (*global invariant - - fundec -> Map (S.C) (Set (fundec * S.C)) - So: g -> {c' -> f, c} - in case f, c --> g, c' *) - - (* - - - module CVal = - struct - include C - include Printable.Std (* To make it Groupable *) - let printXml f c = BatPrintf.fprintf f "%a" printXml c (* wrap in for HTML printing *) - end - module M = MapDomain.MapBot (CVal) (CVal) -*) - module V = (*TODO: do I need to change V???*) - struct - include Printable.Option (S.V) (struct let name = "RecursionTerm" end) - let name () = "RecursionTerm" - let is_write_only t = true - let s x = `Left x - end - module G = S.G - (*GMapG (S.G) (S.C)*) - (*struct - include Lattice.Prod (S.G) (M) - let printXml f (d,m) = BatPrintf.fprintf f "\n%a\n%a\n" S.G.printXml d M.printXml m - end*) - let name () = "RecursionTerm (" ^ S.name () ^ ")" - - type marshal = S.marshal - let init = S.init - let finalize = S.finalize (*TODO*) - - let startstate v = S.startstate v - let exitstate v = S.exitstate v - let morphstate = S.morphstate - - let context = S.context - - (**let side_context sideg f c = - if !AnalysisState.postsolving then - sideg (f) (G.create_contexts (G.CSet.singleton c))*) - - let query ctx = S.query (ctx) - let branch ctx = S.branch (ctx) - let assign ctx = S.assign (ctx) - let vdecl ctx = S.vdecl (ctx) - let enter ctx = - if !AnalysisState.postsolving then - printf "hallo hallo"; - S.enter (ctx) (*TODO*) - let paths_as_set ctx = S.paths_as_set (ctx) - let body ctx = S.body (ctx) - let return ctx = S.return (ctx) - let combine_env ctx = S.combine_env (ctx) - let combine_assign ctx = S.combine_assign (ctx) - let special ctx = S.special (ctx) - let threadenter ctx = S.threadenter (ctx) - let threadspawn ctx lv f args fctx = S.threadspawn (ctx) lv f args (fctx) - let sync ctx = S.sync (ctx) - let skip ctx = S.skip (ctx) - let asm ctx = S.asm (ctx) - let event ctx e octx = S.event (ctx) e (octx) -end -*) module CompareGlobSys (SpecSys: SpecSys) = struct diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index 684733c05f..7e1e477262 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -1,13 +1,8 @@ -(* - code in src/analysis/termination.ml contains loopCounterVisitor which might be interesting - - check if overflow happend with new variable - - how do we deal with nested loops? - - make sure only the analyzed files are appended with the code - - return variables that are newly created - *) - open GoblintCil include Printf +module VarToStmt = Map.Make(CilType.Varinfo);; (* maps varinfos (= loop counter variable) to the statement of the corresponding loop*) + let extract_file_name s = (*There still may be a need to filter more chars*) let ls = String.split_on_char '/' s in (*Assuming '/' as path seperator*) let ls = List.rev ls in @@ -35,7 +30,7 @@ class loopCounterVisitor lc lg le (fd : fundec) = object(self) | Loop (b, loc, eloc, _, _) -> let name = "term"^show_location_id loc in let typ = Cil.intType in - let v = (Cil.makeLocalVar fd name typ) in + let v = (Cil.makeLocalVar fd name typ) in (* Not tested for incremental mode*) let init_stmt = mkStmtOneInstr @@ Set (var v, zero, loc, eloc) in let inc_stmt = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in let exit_stmt = mkStmtOneInstr @@ Set ((var !le), (Lval (var v)), loc, eloc) in @@ -43,7 +38,8 @@ class loopCounterVisitor lc lg le (fd : fundec) = object(self) | cont :: cond :: ss -> b.bstmts <- cont :: inc_stmt :: cond :: ss; (*cont :: cond :: inc_stmt :: ss = it is also possible, but for loops with cond at the end, inc is also at the end*) | _ -> ()); - lc := List.append !lc ([v] : varinfo list); + lc := VarToStmt.add (v: varinfo) (s: stmt) !lc; + (*lc := List.append !lc ([v] : varinfo list);*) let nb = mkBlock [init_stmt; mkStmt s.skind; exit_stmt] in s.skind <- Block nb; s @@ -56,42 +52,4 @@ class loopCounterVisitor lc lg le (fd : fundec) = object(self) s | _ -> s in ChangeDoChildrenPost (s, action); - end - -(* just a test -class loopCounterVisitor (fd : fundec) = object(self) -inherit nopCilVisitor -method! vstmt s = - match s.skind with - | Loop (b, loc, eloc, _, _) -> - let name = "term"^show_location_id loc in - let typ = intType in - let v = Goblintutil.create_var (makeLocalVar fd name ~init:(SingleInit zero) typ) in - let init_stmt = mkStmtOneInstr @@ Set (var v, zero, loc, eloc) in - let inc_stmt = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in - b.bstmts <- inc_stmt :: b.bstmts; - let nb = mkBlock [init_stmt; mkStmt s.skind] in (* init_stmt; *) - ChangeDoChildrenPost (s, (fun _ -> s.skind <- Block(nb); s)) - | _ -> DoChildren -end - -let add_var_loopTerm fd f = - let thisVisitor = new loopCounterVisitor in - visitCilFileSameGlobals (thisVisitor fd ) f*) -(* -let action (fd : fundec) s = - let a s = match s.skind with - | Loop (b, loc, eloc, _, _) -> - let name = "term"^show_location_id loc in - let typ = intType in - let v = Goblintutil.create_var (makeLocalVar fd name ~init:(SingleInit zero) typ) in - let init_stmt = mkStmtOneInstr @@ Set (var v, zero, loc, eloc) in - let inc_stmt = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in - b.bstmts <- inc_stmt :: b.bstmts; - let nb = mkBlock [init_stmt; mkStmt s.skind] in (* *) - s.skind <- Block nb; - s - | _ -> s -in ChangeDoChildrenPost (s, a) -*) - + end \ No newline at end of file From 1d8fb5019387bf283a74addac82b42d8e608fb7b Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Sat, 17 Jun 2023 11:20:57 +0200 Subject: [PATCH 0145/1312] comments --- src/util/terminationPreprocessing.ml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index 7e1e477262..409aa2c2c4 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -30,7 +30,7 @@ class loopCounterVisitor lc lg le (fd : fundec) = object(self) | Loop (b, loc, eloc, _, _) -> let name = "term"^show_location_id loc in let typ = Cil.intType in - let v = (Cil.makeLocalVar fd name typ) in (* Not tested for incremental mode*) + let v = (Cil.makeLocalVar fd name typ) in (*Not tested for incremental mode*) let init_stmt = mkStmtOneInstr @@ Set (var v, zero, loc, eloc) in let inc_stmt = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in let exit_stmt = mkStmtOneInstr @@ Set ((var !le), (Lval (var v)), loc, eloc) in @@ -39,7 +39,6 @@ class loopCounterVisitor lc lg le (fd : fundec) = object(self) b.bstmts <- cont :: inc_stmt :: cond :: ss; (*cont :: cond :: inc_stmt :: ss = it is also possible, but for loops with cond at the end, inc is also at the end*) | _ -> ()); lc := VarToStmt.add (v: varinfo) (s: stmt) !lc; - (*lc := List.append !lc ([v] : varinfo list);*) let nb = mkBlock [init_stmt; mkStmt s.skind; exit_stmt] in s.skind <- Block nb; s From e36eefb052b593e95018275ceaa0fd5a85fa7cfe Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Sat, 17 Jun 2023 12:24:55 +0200 Subject: [PATCH 0146/1312] Change loop analysis domain keys to statements Before, the keys of the domain were simply the (loop counter) variables. Now, the keys are the CIL statements which are the analyzed loop. --- output.txt | 62 ++++++++++++++++----------------- src/analyses/termination_new.ml | 27 +++++++------- src/domains/queries.ml | 8 ++--- 3 files changed, 50 insertions(+), 47 deletions(-) diff --git a/output.txt b/output.txt index 07c71d61b9..d751966836 100644 --- a/output.txt +++ b/output.txt @@ -131,7 +131,7 @@ typedef long __intptr_t; typedef unsigned int __socklen_t; #line 215 "/usr/include/x86_64-linux-gnu/bits/types.h" typedef int __sig_atomic_t; -#line 209 "/usr/lib/gcc/x86_64-linux-gnu/11/include/stddef.h" +#line 209 "/usr/lib/gcc/x86_64-linux-gnu/10/include/stddef.h" typedef unsigned long size_t; #line 10 "/usr/include/x86_64-linux-gnu/bits/types/time_t.h" typedef __time_t time_t; @@ -201,12 +201,12 @@ struct __anonstruct___value32_817613185 { unsigned int __high ; }; #line 25 "/usr/include/x86_64-linux-gnu/bits/atomic_wide_counter.h" -union __anonunion___atomic_wide_counter_643133811 { +union __anonunion___atomic_wide_counter_1044835921 { unsigned long long __value64 ; struct __anonstruct___value32_817613185 __value32 ; }; #line 25 "/usr/include/x86_64-linux-gnu/bits/atomic_wide_counter.h" -typedef union __anonunion___atomic_wide_counter_643133811 __atomic_wide_counter; +typedef union __anonunion___atomic_wide_counter_1044835921 __atomic_wide_counter; #line 51 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" struct __pthread_internal_list { struct __pthread_internal_list *__prev ; @@ -261,11 +261,11 @@ typedef unsigned int __tss_t; #line 106 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" typedef unsigned long __thrd_t; #line 108 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" -struct __anonstruct___once_flag_826868709 { +struct __anonstruct___once_flag_1044835922 { int __data ; }; #line 108 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" -typedef struct __anonstruct___once_flag_826868709 __once_flag; +typedef struct __anonstruct___once_flag_1044835922 __once_flag; #line 27 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" typedef unsigned long pthread_t; #line 32 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" @@ -441,16 +441,16 @@ struct __pthread_cleanup_frame { int __do_it ; int __cancel_type ; }; -#line 143 "/usr/lib/gcc/x86_64-linux-gnu/11/include/stddef.h" +#line 143 "/usr/lib/gcc/x86_64-linux-gnu/10/include/stddef.h" typedef long ptrdiff_t; -#line 321 "/usr/lib/gcc/x86_64-linux-gnu/11/include/stddef.h" +#line 321 "/usr/lib/gcc/x86_64-linux-gnu/10/include/stddef.h" typedef int wchar_t; -#line 415 "/usr/lib/gcc/x86_64-linux-gnu/11/include/stddef.h" +#line 415 "/usr/lib/gcc/x86_64-linux-gnu/10/include/stddef.h" struct __anonstruct_max_align_t_896270833 { long long __max_align_ll __attribute__((__aligned__(__alignof__(long long )))) ; long double __max_align_ld __attribute__((__aligned__(__alignof__(long double )))) ; }; -#line 415 "/usr/lib/gcc/x86_64-linux-gnu/11/include/stddef.h" +#line 415 "/usr/lib/gcc/x86_64-linux-gnu/10/include/stddef.h" typedef struct __anonstruct_max_align_t_896270833 max_align_t; /* compiler builtin: void __builtin_va_copy(__builtin_va_list , __builtin_va_list ) ; */ @@ -1683,40 +1683,42 @@ extern void ( __attribute__((__leaf__)) tzset)(void) __attribute__((__nothrow__ extern int daylight ; #line 233 extern long timezone ; -#line 249 +#line 246 extern time_t ( __attribute__((__leaf__)) timegm)(struct tm *__tp ) __attribute__((__nothrow__)) ; -#line 251 +#line 263 extern time_t ( __attribute__((__leaf__)) timelocal)(struct tm *__tp ) __attribute__((__nothrow__)) ; -#line 262 +#line 271 extern int ( __attribute__((__leaf__)) dysize)(int __year ) __attribute__((__nothrow__, __const__)) ; -#line 272 +#line 281 extern int nanosleep(struct timespec const *__requested_time , struct timespec *__remaining ) ; -#line 276 +#line 285 extern int ( __attribute__((__leaf__)) clock_getres)(clockid_t __clock_id , struct timespec *__res ) __attribute__((__nothrow__)) ; -#line 279 -extern int ( __attribute__((__leaf__)) clock_gettime)(clockid_t __clock_id , struct timespec *__tp ) __attribute__((__nothrow__)) ; -#line 282 -extern int ( __attribute__((__leaf__)) clock_settime)(clockid_t __clock_id , struct timespec const *__tp ) __attribute__((__nothrow__)) ; -#line 311 +#line 288 +extern int ( __attribute__((__nonnull__(2), __leaf__)) clock_gettime)(clockid_t __clock_id , + struct timespec *__tp ) __attribute__((__nothrow__)) ; +#line 292 +extern int ( __attribute__((__nonnull__(2), __leaf__)) clock_settime)(clockid_t __clock_id , + struct timespec const *__tp ) __attribute__((__nothrow__)) ; +#line 323 extern int clock_nanosleep(clockid_t __clock_id , int __flags , struct timespec const *__req , struct timespec *__rem ) ; -#line 326 +#line 338 extern int ( __attribute__((__leaf__)) clock_getcpuclockid)(pid_t __pid , clockid_t *__clock_id ) __attribute__((__nothrow__)) ; -#line 331 +#line 343 extern int ( __attribute__((__leaf__)) timer_create)(clockid_t __clock_id , struct sigevent * __restrict __evp , timer_t * __restrict __timerid ) __attribute__((__nothrow__)) ; -#line 336 +#line 348 extern int ( __attribute__((__leaf__)) timer_delete)(timer_t __timerid ) __attribute__((__nothrow__)) ; -#line 340 +#line 352 extern int ( __attribute__((__leaf__)) timer_settime)(timer_t __timerid , int __flags , struct itimerspec const * __restrict __value , struct itimerspec * __restrict __ovalue ) __attribute__((__nothrow__)) ; -#line 345 +#line 357 extern int ( __attribute__((__leaf__)) timer_gettime)(timer_t __timerid , struct itimerspec *__value ) __attribute__((__nothrow__)) ; -#line 364 +#line 376 extern int ( __attribute__((__leaf__)) timer_getoverrun)(timer_t __timerid ) __attribute__((__nothrow__)) ; -#line 371 +#line 383 extern int ( __attribute__((__nonnull__(1), __leaf__)) timespec_get)(struct timespec *__ts , int __base ) __attribute__((__nothrow__)) ; #line 202 "/usr/include/pthread.h" @@ -1827,9 +1829,8 @@ extern void __pthread_unregister_cancel(__pthread_unwind_buf_t *__buf ) ; #line 750 extern void __pthread_unwind_next(__pthread_unwind_buf_t *__buf ) __attribute__((__weak__, __noreturn__)) ; -#line 766 -extern int __sigsetjmp_cancel(struct __cancel_jmp_buf_tag *__env , int __savemask ) __asm__("__sigsetjmp") __attribute__((__returns_twice__, -__nothrow__)) ; +#line 773 +extern int __sigsetjmp(struct __jmp_buf_tag *__env , int __savemask ) __attribute__((__nothrow__)) ; #line 781 extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutex_init)(pthread_mutex_t *__mutex , pthread_mutexattr_t const *__mutexattr ) __attribute__((__nothrow__)) ; @@ -1994,8 +1995,7 @@ extern int ( __attribute__((__leaf__)) pthread_key_delete)(pthread_key_t __key ) extern void *( __attribute__((__leaf__)) pthread_getspecific)(pthread_key_t __key ) __attribute__((__nothrow__)) ; #line 1308 extern int ( __attribute__((__leaf__)) pthread_setspecific)(pthread_key_t __key , - void const *__pointer ) __attribute__((__nothrow__, -__access__(__none__,2))) ; + void const *__pointer ) __attribute__((__nothrow__)) ; #line 1315 extern int ( __attribute__((__nonnull__(2), __leaf__)) pthread_getcpuclockid)(pthread_t __thread_id , __clockid_t *__clock_id ) __attribute__((__nothrow__)) ; diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index 4926c23daa..e722b9a2b2 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -6,13 +6,13 @@ open TerminationPreprocessing exception PreProcessing of string - -(* contains all loop counter variables (varinfo) and maps them to their corresponding loop statement*) -let loop_counters: stmt VarToStmt.t ref = ref VarToStmt.empty +(** Contains all loop counter variables (varinfo) and maps them to their corresponding loop statement. *) +let loop_counters : stmt VarToStmt.t ref = ref VarToStmt.empty (** Contains the locations of the upjumping gotos *) let upjumping_gotos : location list ref = ref [] +(** Indicates the place in the code, right after a loop is exited. *) let loop_exit : varinfo ref = ref (makeVarinfo false "-error" Cil.intType) let is_loop_counter_var (x : varinfo) = @@ -25,10 +25,10 @@ let is_loop_exit_indicator (x : varinfo) = * upjumping goto was already reached. Returns true if no upjumping goto was * reached until now *) let currrently_no_upjumping_gotos (loc : location) = - List.for_all (function (l) -> (l >= loc)) upjumping_gotos.contents + List.for_all (function l -> l >= loc) upjumping_gotos.contents let no_upjumping_gotos () = - (List.length upjumping_gotos.contents) <= 0 + List.length upjumping_gotos.contents = 0 (** Checks whether a variable can be bounded *) let check_bounded ctx varinfo = @@ -45,6 +45,7 @@ struct include Analyses.StdV end +module Statements = Lattice.Flat (CilType.Stmt) (Printable.DefaultNames) (* TODO: Use Basetype.CilStmt instead? *) module Spec : Analyses.MCPSpec = struct @@ -54,7 +55,7 @@ struct let name () = "termination" - module D = MapDomain.MapBot (Basetype.Variables) (BoolDomain.MustBool) + module D = MapDomain.MapBot (Statements) (BoolDomain.MustBool) module C = D module V = FunContextV (* TODO *) @@ -69,12 +70,14 @@ struct match lval, rval with (Var x, NoOffset), _ when is_loop_counter_var x -> (* Assume that the following loop does not terminate *) - D.add x false ctx.local + let loop_statement = VarToStmt.find x !loop_counters in + D.add (`Lifted loop_statement) false ctx.local | (Var y, NoOffset), Lval (Var x, NoOffset) when is_loop_exit_indicator y -> (* Loop exit: Check whether loop counter variable is bounded *) (* TODO: Move *) let is_bounded = check_bounded ctx x in - D.add x is_bounded ctx.local + let loop_statement = VarToStmt.find x !loop_counters in + D.add (`Lifted loop_statement) is_bounded ctx.local | _ -> ctx.local let special ctx (lval : lval option) (f : varinfo) (arglist : exp list) = @@ -82,16 +85,16 @@ struct ctx.local (** Provides information to Goblint *) - (* TODO: Consider gotos and recursion *) + (* TODO: Consider gotos *) let query ctx (type a) (q: a Queries.t): a Queries.result = let open Queries in match q with - | Queries.MustTermLoop v -> - (match D.find_opt v ctx.local with + | Queries.MustTermLoop loop_statement -> + (match D.find_opt (`Lifted loop_statement) ctx.local with Some b -> b | None -> Result.top q) | Queries.MustTermProg -> - D.for_all (fun loop term_info -> term_info) ctx.local + D.for_all (fun _ term_info -> term_info) ctx.local | _ -> Result.top q end diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 022e8e1dee..c5d7d729b6 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -117,7 +117,7 @@ type _ t = | MayAccessed: AccessDomain.EventSet.t t | MayBeTainted: LS.t t | MayBeModifiedSinceSetjmp: JmpBufDomain.BufferEntry.t -> VS.t t - | MustTermLoop: varinfo -> MustBool.t t (** TODO: not sure if it is the MayBool*) + | MustTermLoop: stmt -> MustBool.t t | MustTermProg: MustBool.t t type 'a result = 'a @@ -347,7 +347,7 @@ struct compare (Any q1) (Any q2) | Any (IsHeapVar v1), Any (IsHeapVar v2) -> CilType.Varinfo.compare v1 v2 | Any (IsMultiple v1), Any (IsMultiple v2) -> CilType.Varinfo.compare v1 v2 - | Any (MustTermLoop v1), Any (MustTermLoop v2) -> CilType.Varinfo.compare v1 v2 + | Any (MustTermLoop s1), Any (MustTermLoop s2) -> CilType.Stmt.compare s1 s2 | Any (EvalThread e1), Any (EvalThread e2) -> CilType.Exp.compare e1 e2 | Any (EvalJumpBuf e1), Any (EvalJumpBuf e2) -> CilType.Exp.compare e1 e2 | Any (WarnGlobal vi1), Any (WarnGlobal vi2) -> Stdlib.compare (Hashtbl.hash vi1) (Hashtbl.hash vi2) @@ -386,7 +386,7 @@ struct | Any (IterVars i) -> 0 | Any (PathQuery (i, q)) -> 31 * i + hash (Any q) | Any (IsHeapVar v) -> CilType.Varinfo.hash v - | Any (MustTermLoop v) -> CilType.Varinfo.hash v + | Any (MustTermLoop s) -> CilType.Stmt.hash s | Any (IsMultiple v) -> CilType.Varinfo.hash v | Any (EvalThread e) -> CilType.Exp.hash e | Any (EvalJumpBuf e) -> CilType.Exp.hash e @@ -454,7 +454,7 @@ struct | Any MayBeTainted -> Pretty.dprintf "MayBeTainted" | Any DYojson -> Pretty.dprintf "DYojson" | Any MayBeModifiedSinceSetjmp buf -> Pretty.dprintf "MayBeModifiedSinceSetjmp %a" JmpBufDomain.BufferEntry.pretty buf - | Any (MustTermLoop v) -> Pretty.dprintf "MustTermLoop %a" CilType.Varinfo.pretty v + | Any (MustTermLoop s) -> Pretty.dprintf "MustTermLoop %a" CilType.Stmt.pretty s | Any MustTermProg -> Pretty.dprintf "MustTermProg" end From 234ae2a6516cddb68f124a97b5ec7ccb43efd3db Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 16 Jun 2023 14:55:01 +0300 Subject: [PATCH 0147/1312] Fix typo and dead link in taint analysis tutorial --- src/analyses/tutorials/taint.ml | 2 +- tests/regression/99-tutorials/03-taint_simple.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/analyses/tutorials/taint.ml b/src/analyses/tutorials/taint.ml index 217125c8bd..3067449e31 100644 --- a/src/analyses/tutorials/taint.ml +++ b/src/analyses/tutorials/taint.ml @@ -1,8 +1,8 @@ (** Simple interprocedural taint analysis template ([taint]). *) (** An analysis specification for didactic purposes. *) -(* Helpful link on CIL: https://goblint.in.tum.de/assets/goblint-cil/ *) (* Goblint documentation: https://goblint.readthedocs.io/en/latest/ *) +(* Helpful link on CIL: https://goblint.github.io/cil/ *) (* You may test your analysis on our toy examples by running `ruby scripts/update_suite.rb group tutorials` *) (* after removing the `SKIP` from the beginning of the tests in tests/regression/99-tutorials/{03-taint_simple.c,04-taint_inter.c} *) diff --git a/tests/regression/99-tutorials/03-taint_simple.c b/tests/regression/99-tutorials/03-taint_simple.c index d9d00351c1..4cc206d949 100644 --- a/tests/regression/99-tutorials/03-taint_simple.c +++ b/tests/regression/99-tutorials/03-taint_simple.c @@ -31,7 +31,7 @@ int main(void) { // Trivial example showing how the analysis you just wrote benefits from other analyses - // If we wanted to write a real analysis, we would also aks other analyses questions, to e.g. handle pointers + // If we wanted to write a real analysis, we would also ask other analyses questions, to e.g. handle pointers int z; if(z == 0) { z = 5; From d0b9de95a9128a5e246de5492642422b61c69f9f Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Sun, 18 Jun 2023 12:39:01 +0200 Subject: [PATCH 0148/1312] Consider upjumping gotos in whole program query and a bit of change towards using a global invariant --- src/analyses/termination_new.ml | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index e722b9a2b2..d610a89fe9 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -21,12 +21,6 @@ let is_loop_counter_var (x : varinfo) = let is_loop_exit_indicator (x : varinfo) = x = !loop_exit -(** Checks whether at the current location (=loc) of the analysis an - * upjumping goto was already reached. Returns true if no upjumping goto was - * reached until now *) -let currrently_no_upjumping_gotos (loc : location) = - List.for_all (function l -> l >= loc) upjumping_gotos.contents - let no_upjumping_gotos () = List.length upjumping_gotos.contents = 0 @@ -39,14 +33,18 @@ let check_bounded ctx varinfo = | `Lifted v -> not (is_top_of (ikind v) v) | `Bot -> raise (PreProcessing "Loop variable is Bot") -module FunContextV : Analyses.SpecSysVar = +module UnitV : SpecSysVar = struct - include Printable.Prod (CilType.Fundec) (CilType.Fundec) (* TODO *) - include Analyses.StdV + include Printable.Unit + include StdV end +(** We want to record termination information of loops and use the loop + * statements for that. We use this lifting because we need to have a + * lattice. *) module Statements = Lattice.Flat (CilType.Stmt) (Printable.DefaultNames) (* TODO: Use Basetype.CilStmt instead? *) +(** The termination analysis considering loops and gotos *) module Spec : Analyses.MCPSpec = struct @@ -55,15 +53,13 @@ struct let name () = "termination" - module D = MapDomain.MapBot (Statements) (BoolDomain.MustBool) + module D = MapDomain.MapBot (Statements) (BoolDomain.MustBool) (* TODO *) module C = D - module V = FunContextV - (* TODO *) + module V = UnitV + module G = MapDomain.MapBot (Statements) (BoolDomain.MustBool) let startstate _ = D.bot () - let exitstate = startstate (* TODO *) - - let finalize () = () (* TODO *) + let exitstate = startstate let assign ctx (lval : lval) (rval : exp) = (* Detect assignment to loop counter variable *) @@ -85,7 +81,6 @@ struct ctx.local (** Provides information to Goblint *) - (* TODO: Consider gotos *) let query ctx (type a) (q: a Queries.t): a Queries.result = let open Queries in match q with @@ -95,6 +90,7 @@ struct | None -> Result.top q) | Queries.MustTermProg -> D.for_all (fun _ term_info -> term_info) ctx.local + && no_upjumping_gotos () | _ -> Result.top q end From 88432722ace33653378ae8989ff4903c71f563d4 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Sun, 18 Jun 2023 18:11:11 +0200 Subject: [PATCH 0149/1312] WIP on using find_loop_heads and global invariant --- src/analyses/termination_new.ml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index d610a89fe9..5e56c74a05 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -6,6 +6,17 @@ open TerminationPreprocessing exception PreProcessing of string +(* + * TODO: Make this work +module FileCfg = +struct + let file = !Cilfacade.current_file + module Cfg = (val !MyCFG.current_cfg) +end + +let loop_heads = WitnessUtil.find_loop_heads FileCfg + *) + (** Contains all loop counter variables (varinfo) and maps them to their corresponding loop statement. *) let loop_counters : stmt VarToStmt.t ref = ref VarToStmt.empty @@ -67,6 +78,10 @@ struct (Var x, NoOffset), _ when is_loop_counter_var x -> (* Assume that the following loop does not terminate *) let loop_statement = VarToStmt.find x !loop_counters in + (* + * TODO: Make the below line work + let () = ctx.sideg (() : V.t) (G.add (`Lifted loop_statement) false ctx.local) in + *) D.add (`Lifted loop_statement) false ctx.local | (Var y, NoOffset), Lval (Var x, NoOffset) when is_loop_exit_indicator y -> (* Loop exit: Check whether loop counter variable is bounded *) From 49dcae5dfa79f11d03bb36748b6ebabf854254d9 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Mon, 19 Jun 2023 13:04:28 +0200 Subject: [PATCH 0150/1312] Use global invariant instead of local state Still needs a bit of work. Currently, loops are only ever analyzed if their loop exit indicator is encountered. --- src/analyses/termination_new.ml | 44 ++++++++++++++++----------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index 5e56c74a05..f062030820 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -6,16 +6,14 @@ open TerminationPreprocessing exception PreProcessing of string -(* - * TODO: Make this work -module FileCfg = -struct - let file = !Cilfacade.current_file - module Cfg = (val !MyCFG.current_cfg) -end - -let loop_heads = WitnessUtil.find_loop_heads FileCfg - *) +let loop_heads = + let module FileCfg = + struct + let file = !Cilfacade.current_file + module Cfg = (val !MyCFG.current_cfg) + end in + let module WitnessInvariant = WitnessUtil.Invariant (FileCfg) in + WitnessInvariant.loop_heads (* TODO: Use this *) (** Contains all loop counter variables (varinfo) and maps them to their corresponding loop statement. *) let loop_counters : stmt VarToStmt.t ref = ref VarToStmt.empty @@ -44,7 +42,7 @@ let check_bounded ctx varinfo = | `Lifted v -> not (is_top_of (ikind v) v) | `Bot -> raise (PreProcessing "Loop variable is Bot") -module UnitV : SpecSysVar = +module UnitV = struct include Printable.Unit include StdV @@ -64,7 +62,7 @@ struct let name () = "termination" - module D = MapDomain.MapBot (Statements) (BoolDomain.MustBool) (* TODO *) + module D = Lattice.Unit module C = D module V = UnitV module G = MapDomain.MapBot (Statements) (BoolDomain.MustBool) @@ -75,20 +73,21 @@ struct let assign ctx (lval : lval) (rval : exp) = (* Detect assignment to loop counter variable *) match lval, rval with + (* (Var x, NoOffset), _ when is_loop_counter_var x -> (* Assume that the following loop does not terminate *) let loop_statement = VarToStmt.find x !loop_counters in - (* - * TODO: Make the below line work - let () = ctx.sideg (() : V.t) (G.add (`Lifted loop_statement) false ctx.local) in - *) + let () = ctx.sideg () (G.add (`Lifted loop_statement) false ctx.local) in + let () = print_endline ("Added FALSE for " ^ x.vname) in D.add (`Lifted loop_statement) false ctx.local - | (Var y, NoOffset), Lval (Var x, NoOffset) when is_loop_exit_indicator y -> + *) + (Var y, NoOffset), Lval (Var x, NoOffset) when is_loop_exit_indicator y -> (* Loop exit: Check whether loop counter variable is bounded *) (* TODO: Move *) let is_bounded = check_bounded ctx x in let loop_statement = VarToStmt.find x !loop_counters in - D.add (`Lifted loop_statement) is_bounded ctx.local + let () = ctx.sideg () (G.add (`Lifted loop_statement) is_bounded (ctx.global ())) in + ctx.local | _ -> ctx.local let special ctx (lval : lval option) (f : varinfo) (arglist : exp list) = @@ -97,16 +96,15 @@ struct (** Provides information to Goblint *) let query ctx (type a) (q: a Queries.t): a Queries.result = - let open Queries in match q with | Queries.MustTermLoop loop_statement -> - (match D.find_opt (`Lifted loop_statement) ctx.local with + (match G.find_opt (`Lifted loop_statement) (ctx.global ()) with Some b -> b - | None -> Result.top q) + | None -> false) | Queries.MustTermProg -> - D.for_all (fun _ term_info -> term_info) ctx.local + G.for_all (fun _ term_info -> term_info) (ctx.global ()) && no_upjumping_gotos () - | _ -> Result.top q + | _ -> Queries.Result.top q end From 8fc0c1beff17b61b57e4b26173a4eea49679b01d Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Sat, 3 Jun 2023 13:29:17 +0200 Subject: [PATCH 0151/1312] first tests for recursion termination analysis, added empty functor and GMapG --- src/framework/analyses.ml | 53 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 1d1972ac45..7fd57b357e 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -119,6 +119,59 @@ struct | x -> BatPrintf.fprintf f "%a" printXml x end +module GMapG (G: Lattice.S) (C: Printable.S) = +struct + module CVal = + struct + include Printable.Std (* To make it Groupable *) + include SetDomain.Make ( + struct + include C + let printXml f c = BatPrintf.fprintf f "%a" printXml c (* wrap in for HTML printing *) + end + ) + let name () = "contextsMap" + end + + module RangeVal = + struct + include SetDomain.Make ( + struct + include C (*TODO: sollte hier iwi ein tupel sein*) + let printXml f c = BatPrintf.fprintf f "%a" printXml c (* wrap in for HTML printing *) + end + ) + let name () = "contextsMap" + end + + module CMap = + struct + include MapDomain.MapBot (CVal) (RangeVal) + let name () = "contextsMap" + end + include Lattice.Lift2 (G) (CMap) (Printable.DefaultNames) + + let is_bot () = false + let is_top () = false + + (*let spec = function + | `Bot -> G.bot () + | `Lifted1 x -> x + | _ -> failwith "GVarG.spec" + let contexts = function + | `Bot -> CSet.bot () + | `Lifted2 x -> x + | _ -> failwith "GVarG.contexts" + let create_spec spec = `Lifted1 spec + let create_contexts contexts = `Lifted2 contexts + + let printXml f = function + | `Lifted1 x -> G.printXml f x + | `Lifted2 x -> BatPrintf.fprintf f "%a" CSet.printXml x + | x -> BatPrintf.fprintf f "%a" printXml x +*) +end + module GMapG (G: Lattice.S) (C: Printable.S) = struct From 0efdc788435e4404e1f5883b6fc3f80fb8aebe1f Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Thu, 1 Jun 2023 05:01:09 +0200 Subject: [PATCH 0152/1312] Tests for loop termination --- .../80-termination/15-complex-loop-combination-terminating.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/regression/80-termination/15-complex-loop-combination-terminating.c b/tests/regression/80-termination/15-complex-loop-combination-terminating.c index e5383aed66..54f8cd97c8 100644 --- a/tests/regression/80-termination/15-complex-loop-combination-terminating.c +++ b/tests/regression/80-termination/15-complex-loop-combination-terminating.c @@ -130,7 +130,7 @@ int main() } } - // Loop with a label and goto statement + /* // Loop with a label and goto statement int w = 1; start: if (w <= 5) @@ -138,7 +138,7 @@ int main() printf("Loop with Label and Goto: %d\n", w); w++; goto start; // TERM - } + } */ return 0; } From 8e9d29e4e8cd34531ad0f3a01b1d8ce5a054ae00 Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Mon, 5 Jun 2023 16:04:58 +0200 Subject: [PATCH 0153/1312] Loop termination tests extended --- .../80-termination/09-complex-for-loop-terminating.c | 2 +- .../regression/80-termination/10-complex-loop-terminating.c | 2 +- .../15-complex-loop-combination-terminating.c | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/regression/80-termination/09-complex-for-loop-terminating.c b/tests/regression/80-termination/09-complex-for-loop-terminating.c index ed28fa9b43..508b31500c 100644 --- a/tests/regression/80-termination/09-complex-for-loop-terminating.c +++ b/tests/regression/80-termination/09-complex-for-loop-terminating.c @@ -1,4 +1,4 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval #include int main() diff --git a/tests/regression/80-termination/10-complex-loop-terminating.c b/tests/regression/80-termination/10-complex-loop-terminating.c index 3a19f17bee..9d5cd4b928 100644 --- a/tests/regression/80-termination/10-complex-loop-terminating.c +++ b/tests/regression/80-termination/10-complex-loop-terminating.c @@ -1,4 +1,4 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval #include int main() diff --git a/tests/regression/80-termination/15-complex-loop-combination-terminating.c b/tests/regression/80-termination/15-complex-loop-combination-terminating.c index 54f8cd97c8..1ea228ae55 100644 --- a/tests/regression/80-termination/15-complex-loop-combination-terminating.c +++ b/tests/regression/80-termination/15-complex-loop-combination-terminating.c @@ -1,4 +1,4 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval #include int main() @@ -130,7 +130,7 @@ int main() } } - /* // Loop with a label and goto statement + // Loop with a label and goto statement int w = 1; start: if (w <= 5) @@ -138,7 +138,7 @@ int main() printf("Loop with Label and Goto: %d\n", w); w++; goto start; // TERM - } */ + } return 0; } From 8a47aa678be4ce6d66c0aa159b0878b5b6b77fef Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Mon, 19 Jun 2023 13:34:23 +0200 Subject: [PATCH 0154/1312] Goto test cases --- .../24-upjumping-goto-loopless-terminating.c | 18 +++++++++++++ .../25-leave-loop-goto-terminating.c | 24 +++++++++++++++++ .../26-enter-loop-goto-terminating.c | 27 +++++++++++++++++++ .../27-upjumping-goto-nonterminating.c | 19 +++++++++++++ 4 files changed, 88 insertions(+) create mode 100644 tests/regression/80-termination/24-upjumping-goto-loopless-terminating.c create mode 100644 tests/regression/80-termination/25-leave-loop-goto-terminating.c create mode 100644 tests/regression/80-termination/26-enter-loop-goto-terminating.c create mode 100644 tests/regression/80-termination/27-upjumping-goto-nonterminating.c diff --git a/tests/regression/80-termination/24-upjumping-goto-loopless-terminating.c b/tests/regression/80-termination/24-upjumping-goto-loopless-terminating.c new file mode 100644 index 0000000000..aea0d5dd97 --- /dev/null +++ b/tests/regression/80-termination/24-upjumping-goto-loopless-terminating.c @@ -0,0 +1,18 @@ +#include + +int main() { + goto mark2; + +mark1: + printf("This is mark1\n"); + goto mark3; + +mark2: + printf("This is mark2\n"); + goto mark1; + +mark3: + printf("This is mark3\n"); + + return 0; +} diff --git a/tests/regression/80-termination/25-leave-loop-goto-terminating.c b/tests/regression/80-termination/25-leave-loop-goto-terminating.c new file mode 100644 index 0000000000..34c14eedd6 --- /dev/null +++ b/tests/regression/80-termination/25-leave-loop-goto-terminating.c @@ -0,0 +1,24 @@ +#include + +int main() { + int counter = 0; + + while (1) { + counter++; + + // Dummy code + printf("Iteration %d\n", counter); + int result = counter * 2; + printf("Result: %d\n", result); + + // Condition to terminate the loop + if (result >= 10) { + goto end; + } + } + +end: + printf("Loop exited. Result is greater than or equal to 10.\n"); + + return 0; +} diff --git a/tests/regression/80-termination/26-enter-loop-goto-terminating.c b/tests/regression/80-termination/26-enter-loop-goto-terminating.c new file mode 100644 index 0000000000..d3158ae7f7 --- /dev/null +++ b/tests/regression/80-termination/26-enter-loop-goto-terminating.c @@ -0,0 +1,27 @@ +#include + +int main() { + int counter = 0; + + goto jump_point; + + while (1) { + counter++; + + // Dummy code + printf("Iteration %d\n", counter); + int result = counter * 2; + jump_point: + printf("Result: %d\n", result); + + // Condition to terminate the loop + if (result >= 10) { + goto end; + } + } + +end: + printf("Loop exited. Result is greater than or equal to 10.\n"); + + return 0; +} diff --git a/tests/regression/80-termination/27-upjumping-goto-nonterminating.c b/tests/regression/80-termination/27-upjumping-goto-nonterminating.c new file mode 100644 index 0000000000..b6dc238fe3 --- /dev/null +++ b/tests/regression/80-termination/27-upjumping-goto-nonterminating.c @@ -0,0 +1,19 @@ +#include + +int main() { + goto mark2; + +mark1: + printf("This is mark1\n"); + goto mark3; + +mark2: + printf("This is mark2\n"); + goto mark1; + +mark3: + printf("This is mark3\n"); + goto mark1; + + return 0; +} From bb5ab19e5f1417aae2739ef85ca36c1c54890a84 Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Mon, 19 Jun 2023 13:40:37 +0200 Subject: [PATCH 0155/1312] Added recursion tests --- .../81-recursion/01-simple-terminating.c | 21 +++++++++++ .../81-recursion/02-simple-nonterminating.c | 21 +++++++++++ .../81-recursion/03-nested-terminating.c | 35 +++++++++++++++++++ .../81-recursion/04-nested-nonterminating.c | 25 +++++++++++++ 4 files changed, 102 insertions(+) create mode 100644 tests/regression/81-recursion/01-simple-terminating.c create mode 100644 tests/regression/81-recursion/02-simple-nonterminating.c create mode 100644 tests/regression/81-recursion/03-nested-terminating.c create mode 100644 tests/regression/81-recursion/04-nested-nonterminating.c diff --git a/tests/regression/81-recursion/01-simple-terminating.c b/tests/regression/81-recursion/01-simple-terminating.c new file mode 100644 index 0000000000..9e509ebaa9 --- /dev/null +++ b/tests/regression/81-recursion/01-simple-terminating.c @@ -0,0 +1,21 @@ +#include + +void recursiveFunction(int n) { + // Base case: When n reaches 0, stop recursion + if (n == 0) { + printf("Terminating recursion\n"); + return; + } + + printf("Recursive call with n = %d\n", n); + + // Recursive call: Decrement n and call the function again + recursiveFunction(n - 1); +} + +int main() { + // Call the recursive function with an initial value + recursiveFunction(5); + + return 0; +} diff --git a/tests/regression/81-recursion/02-simple-nonterminating.c b/tests/regression/81-recursion/02-simple-nonterminating.c new file mode 100644 index 0000000000..ec9d31ffcb --- /dev/null +++ b/tests/regression/81-recursion/02-simple-nonterminating.c @@ -0,0 +1,21 @@ +#include + +void recursiveFunction(int n) { + // Base case: When n reaches 0, stop recursion + if (n == 30) { + printf("Terminating recursion\n"); + return; + } + + printf("Recursive call with n = %d\n", n); + + // Recursive call: Decrement n and call the function again + recursiveFunction(n - 1); +} + +int main() { + // Call the recursive function with an initial value + recursiveFunction(5); + + return 0; +} diff --git a/tests/regression/81-recursion/03-nested-terminating.c b/tests/regression/81-recursion/03-nested-terminating.c new file mode 100644 index 0000000000..6cd28043ee --- /dev/null +++ b/tests/regression/81-recursion/03-nested-terminating.c @@ -0,0 +1,35 @@ +#include + +void innerRecursiveFunction(int n) { + if (n == 0) { + printf("Terminating inner recursion\n"); + return; + } + + printf("Inner recursive call with n = %d\n", n); + + // Recursive call to the innerRecursiveFunction + innerRecursiveFunction(n - 1); +} + +void outerRecursiveFunction(int n) { + if (n == 0) { + printf("Terminating outer recursion\n"); + return; + } + + printf("Outer recursive call with n = %d\n", n); + + // Recursive call to the outerRecursiveFunction + outerRecursiveFunction(n - 1); + + // Call to the innerRecursiveFunction + innerRecursiveFunction(n); +} + +int main() { + // Call the outerRecursiveFunction with an initial value + outerRecursiveFunction(3); + + return 0; +} diff --git a/tests/regression/81-recursion/04-nested-nonterminating.c b/tests/regression/81-recursion/04-nested-nonterminating.c new file mode 100644 index 0000000000..96911687da --- /dev/null +++ b/tests/regression/81-recursion/04-nested-nonterminating.c @@ -0,0 +1,25 @@ +#include + +void innerRecursiveFunction() { + printf("Nested recursive call\n"); + + // Recursive call to the innerRecursiveFunction + innerRecursiveFunction(); +} + +void outerRecursiveFunction() { + printf("Outer recursive call\n"); + + // Recursive call to the outerRecursiveFunction + outerRecursiveFunction(); + + // Call to the innerRecursiveFunction + innerRecursiveFunction(); +} + +int main() { + // Call the outerRecursiveFunction + outerRecursiveFunction(); + + return 0; +} From 3525e494835566906a38c493c03ce5a57c2539dc Mon Sep 17 00:00:00 2001 From: karoliineh Date: Mon, 19 Jun 2023 15:14:09 +0300 Subject: [PATCH 0156/1312] Add more tests for covering different cases of access outer distribution Co-authored-by: Simmo Saan --- .../04-mutex/90-distribute-fields-type-1.c | 42 +++++++++++++++++ .../04-mutex/90-distribute-fields-type-1.t | 31 ++++++++++++ .../04-mutex/91-distribute-fields-type-2.c | 43 +++++++++++++++++ .../04-mutex/91-distribute-fields-type-2.t | 31 ++++++++++++ .../04-mutex/92-distribute-fields-type-deep.c | 47 +++++++++++++++++++ .../04-mutex/92-distribute-fields-type-deep.t | 33 +++++++++++++ .../93-distribute-fields-type-global.c | 26 ++++++++++ .../93-distribute-fields-type-global.t | 5 ++ 8 files changed, 258 insertions(+) create mode 100644 tests/regression/04-mutex/90-distribute-fields-type-1.c create mode 100644 tests/regression/04-mutex/90-distribute-fields-type-1.t create mode 100644 tests/regression/04-mutex/91-distribute-fields-type-2.c create mode 100644 tests/regression/04-mutex/91-distribute-fields-type-2.t create mode 100644 tests/regression/04-mutex/92-distribute-fields-type-deep.c create mode 100644 tests/regression/04-mutex/92-distribute-fields-type-deep.t create mode 100644 tests/regression/04-mutex/93-distribute-fields-type-global.c create mode 100644 tests/regression/04-mutex/93-distribute-fields-type-global.t diff --git a/tests/regression/04-mutex/90-distribute-fields-type-1.c b/tests/regression/04-mutex/90-distribute-fields-type-1.c new file mode 100644 index 0000000000..51f0e52426 --- /dev/null +++ b/tests/regression/04-mutex/90-distribute-fields-type-1.c @@ -0,0 +1,42 @@ +//PARAM: --enable ana.race.direct-arithmetic +#include +#include + +// (int) (S) (T) (U) +// \ / \ / \ / +// >f< >s< t +// \ / \ / +// f s +// \ / +// f + +struct S { + int field; +}; + +struct T { + struct S s; +}; + +// struct S s; +// struct T t; + +extern struct S* getS(); +extern struct T* getT(); + +// getS could return the same struct as is contained in getT + +void *t_fun(void *arg) { + // should write to (struct T).s.field in addition to (struct S).field + // but easier to implement the other way around? + getS()->field = 1; // RACE! + return NULL; +} + +int main(void) { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + struct S s1; + getT()->s = s1; // RACE! + return 0; +} diff --git a/tests/regression/04-mutex/90-distribute-fields-type-1.t b/tests/regression/04-mutex/90-distribute-fields-type-1.t new file mode 100644 index 0000000000..dd862fa65a --- /dev/null +++ b/tests/regression/04-mutex/90-distribute-fields-type-1.t @@ -0,0 +1,31 @@ + $ goblint --enable allglobs 90-distribute-fields-type-1.c + [Error][Imprecise][Unsound] Function definition missing for getS (90-distribute-fields-type-1.c:32:3-32:20) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (90-distribute-fields-type-1.c:32:3-32:20) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (90-distribute-fields-type-1.c:32:3-32:20) + [Info][Unsound] Unknown address in {&tmp} has escaped. (90-distribute-fields-type-1.c:32:3-32:20) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (90-distribute-fields-type-1.c:32:3-32:20) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (90-distribute-fields-type-1.c:32:3-32:20) + [Info][Unsound] Write to unknown address: privatization is unsound. (90-distribute-fields-type-1.c:32:3-32:20) + [Error][Imprecise][Unsound] Function definition missing for getT (90-distribute-fields-type-1.c:40:3-40:17) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (90-distribute-fields-type-1.c:40:3-40:17) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (90-distribute-fields-type-1.c:40:3-40:17) + [Info][Unsound] Unknown address in {&tmp} has escaped. (90-distribute-fields-type-1.c:40:3-40:17) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (90-distribute-fields-type-1.c:40:3-40:17) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (90-distribute-fields-type-1.c:40:3-40:17) + [Info][Unsound] Write to unknown address: privatization is unsound. (90-distribute-fields-type-1.c:40:3-40:17) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 0 + total lines: 7 + [Warning][Race] Memory location (struct T).s.field (race with conf. 100): + write with [mhp:{tid=[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]}, thread:[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]] (conf. 100) (exp: & tmp->field) (90-distribute-fields-type-1.c:32:3-32:20) + write with [mhp:{tid=[main]; created={[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->s) (90-distribute-fields-type-1.c:40:3-40:17) + [Success][Race] Memory location (struct T).s (safe): + write with [mhp:{tid=[main]; created={[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->s) (90-distribute-fields-type-1.c:40:3-40:17) + [Success][Race] Memory location (struct S).field (safe): + write with [mhp:{tid=[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]}, thread:[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]] (conf. 100) (exp: & tmp->field) (90-distribute-fields-type-1.c:32:3-32:20) + [Info][Race] Memory locations race summary: + safe: 2 + vulnerable: 0 + unsafe: 1 + total memory locations: 3 diff --git a/tests/regression/04-mutex/91-distribute-fields-type-2.c b/tests/regression/04-mutex/91-distribute-fields-type-2.c new file mode 100644 index 0000000000..12866105f6 --- /dev/null +++ b/tests/regression/04-mutex/91-distribute-fields-type-2.c @@ -0,0 +1,43 @@ +//PARAM: --enable ana.race.direct-arithmetic +#include +#include + +// (int) >(S)< >(T)< (U) +// \ / \ / \ / +// f s t +// \ / \ / +// f s +// \ / +// f + +struct S { + int field; +}; + +struct T { + struct S s; +}; + +// struct S s; +// struct T t; + +extern struct S* getS(); +extern struct T* getT(); + +// getS could return the same struct as is contained in getT + +void *t_fun(void *arg) { + // should write to (struct T).s.field in addition to (struct S).field + // but easier to implement the other way around? + struct S s1; + *(getS()) = s1; // RACE! + return NULL; +} + +int main(void) { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + struct T t1; + *(getT()) = t1; // RACE! + return 0; +} diff --git a/tests/regression/04-mutex/91-distribute-fields-type-2.t b/tests/regression/04-mutex/91-distribute-fields-type-2.t new file mode 100644 index 0000000000..4cfb965e25 --- /dev/null +++ b/tests/regression/04-mutex/91-distribute-fields-type-2.t @@ -0,0 +1,31 @@ + $ goblint --enable allglobs 91-distribute-fields-type-2.c + [Error][Imprecise][Unsound] Function definition missing for getS (91-distribute-fields-type-2.c:33:3-33:17) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (91-distribute-fields-type-2.c:33:3-33:17) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (91-distribute-fields-type-2.c:33:3-33:17) + [Info][Unsound] Unknown address in {&tmp} has escaped. (91-distribute-fields-type-2.c:33:3-33:17) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (91-distribute-fields-type-2.c:33:3-33:17) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (91-distribute-fields-type-2.c:33:3-33:17) + [Info][Unsound] Write to unknown address: privatization is unsound. (91-distribute-fields-type-2.c:33:3-33:17) + [Error][Imprecise][Unsound] Function definition missing for getT (91-distribute-fields-type-2.c:41:3-41:17) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (91-distribute-fields-type-2.c:41:3-41:17) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (91-distribute-fields-type-2.c:41:3-41:17) + [Info][Unsound] Unknown address in {&tmp} has escaped. (91-distribute-fields-type-2.c:41:3-41:17) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (91-distribute-fields-type-2.c:41:3-41:17) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (91-distribute-fields-type-2.c:41:3-41:17) + [Info][Unsound] Write to unknown address: privatization is unsound. (91-distribute-fields-type-2.c:41:3-41:17) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 0 + total lines: 7 + [Warning][Race] Memory location (struct T).s (race with conf. 100): + write with [mhp:{tid=[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]}, thread:[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:33:3-33:17) + write with [mhp:{tid=[main]; created={[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]}}, thread:[main]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:41:3-41:17) + [Success][Race] Memory location (struct T) (safe): + write with [mhp:{tid=[main]; created={[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]}}, thread:[main]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:41:3-41:17) + [Success][Race] Memory location (struct S) (safe): + write with [mhp:{tid=[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]}, thread:[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:33:3-33:17) + [Info][Race] Memory locations race summary: + safe: 2 + vulnerable: 0 + unsafe: 1 + total memory locations: 3 diff --git a/tests/regression/04-mutex/92-distribute-fields-type-deep.c b/tests/regression/04-mutex/92-distribute-fields-type-deep.c new file mode 100644 index 0000000000..891f5fb51f --- /dev/null +++ b/tests/regression/04-mutex/92-distribute-fields-type-deep.c @@ -0,0 +1,47 @@ +//PARAM: --enable ana.race.direct-arithmetic +#include +#include + +// (int) (S) (T) (U) +// \ / \ / \ / +// >f< s >t< +// \ / \ / +// f s +// \ / +// f + +struct S { + int field; +}; + +struct T { + struct S s; +}; + +struct U { + struct T t; +}; + +// struct S s; +// struct T t; + +extern struct S* getS(); +extern struct T* getT(); +extern struct U* getU(); + +// getS could return the same struct as is contained in getT + +void *t_fun(void *arg) { + // should write to (struct U).t.s.field in addition to (struct T).s.field + // but easier to implement the other way around? + getS()->field = 1; // RACE! + return NULL; +} + +int main(void) { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + struct T t1; + getU()->t = t1; // RACE! + return 0; +} diff --git a/tests/regression/04-mutex/92-distribute-fields-type-deep.t b/tests/regression/04-mutex/92-distribute-fields-type-deep.t new file mode 100644 index 0000000000..12dc5e8f52 --- /dev/null +++ b/tests/regression/04-mutex/92-distribute-fields-type-deep.t @@ -0,0 +1,33 @@ + $ goblint --enable allglobs 92-distribute-fields-type-deep.c + [Error][Imprecise][Unsound] Function definition missing for getS (92-distribute-fields-type-deep.c:37:3-37:20) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (92-distribute-fields-type-deep.c:37:3-37:20) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (92-distribute-fields-type-deep.c:37:3-37:20) + [Info][Unsound] Unknown address in {&tmp} has escaped. (92-distribute-fields-type-deep.c:37:3-37:20) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (92-distribute-fields-type-deep.c:37:3-37:20) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (92-distribute-fields-type-deep.c:37:3-37:20) + [Info][Unsound] Write to unknown address: privatization is unsound. (92-distribute-fields-type-deep.c:37:3-37:20) + [Error][Imprecise][Unsound] Function definition missing for getU (92-distribute-fields-type-deep.c:45:3-45:17) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (92-distribute-fields-type-deep.c:45:3-45:17) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (92-distribute-fields-type-deep.c:45:3-45:17) + [Info][Unsound] Unknown address in {&tmp} has escaped. (92-distribute-fields-type-deep.c:45:3-45:17) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (92-distribute-fields-type-deep.c:45:3-45:17) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (92-distribute-fields-type-deep.c:45:3-45:17) + [Info][Unsound] Write to unknown address: privatization is unsound. (92-distribute-fields-type-deep.c:45:3-45:17) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 0 + total lines: 7 + [Success][Race] Memory location (struct T).s.field (safe): + write with [mhp:{tid=[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}, thread:[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (92-distribute-fields-type-deep.c:37:3-37:20) + [Success][Race] Memory location (struct S).field (safe): + write with [mhp:{tid=[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}, thread:[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (92-distribute-fields-type-deep.c:37:3-37:20) + [Warning][Race] Memory location (struct U).t.s.field (race with conf. 100): + write with [mhp:{tid=[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}, thread:[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (92-distribute-fields-type-deep.c:37:3-37:20) + write with [mhp:{tid=[main]; created={[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t) (92-distribute-fields-type-deep.c:45:3-45:17) + [Success][Race] Memory location (struct U).t (safe): + write with [mhp:{tid=[main]; created={[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t) (92-distribute-fields-type-deep.c:45:3-45:17) + [Info][Race] Memory locations race summary: + safe: 3 + vulnerable: 0 + unsafe: 1 + total memory locations: 4 diff --git a/tests/regression/04-mutex/93-distribute-fields-type-global.c b/tests/regression/04-mutex/93-distribute-fields-type-global.c new file mode 100644 index 0000000000..e0065b7870 --- /dev/null +++ b/tests/regression/04-mutex/93-distribute-fields-type-global.c @@ -0,0 +1,26 @@ +//PARAM: --enable ana.race.direct-arithmetic +#include +#include + +struct S { + int field; +}; + +struct S s; + +void *t_fun(void *arg) { + printf("%d",getS()->field); // RACE! + + return NULL; +} + +extern struct S* getS(); + +int main(void) { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + struct S s1; + s = s1; // RACE! + return 0; +} + diff --git a/tests/regression/04-mutex/93-distribute-fields-type-global.t b/tests/regression/04-mutex/93-distribute-fields-type-global.t new file mode 100644 index 0000000000..90baf61492 --- /dev/null +++ b/tests/regression/04-mutex/93-distribute-fields-type-global.t @@ -0,0 +1,5 @@ + $ goblint --enable allglobs 93-distribute-fields-type-global.c + 93-distribute-fields-type-global.c:12: Error: expecting a pointer to a struct + Error: There were parsing errors in .goblint/preprocessed/93-distribute-fields-type-global.i + Fatal error: exception Goblint_lib__Maingoblint.FrontendError("Errormsg.Error") + [2] From 2a003ab733b53c3e89eef205c6a152a9d71aadb7 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Mon, 19 Jun 2023 15:16:24 +0300 Subject: [PATCH 0157/1312] Add cram tests and ASCII art to existing tests that cover access outer distribution --- .../04-mutex/77-type-nested-fields.c | 8 +++++ .../04-mutex/77-type-nested-fields.t | 29 +++++++++++++++++ .../04-mutex/79-type-nested-fields-deep1.c | 8 +++++ .../04-mutex/79-type-nested-fields-deep1.t | 31 +++++++++++++++++++ .../04-mutex/80-type-nested-fields-deep2.c | 8 +++++ .../04-mutex/80-type-nested-fields-deep2.t | 29 +++++++++++++++++ tests/regression/06-symbeq/16-type_rc.c | 8 +++++ tests/regression/06-symbeq/16-type_rc.t | 30 +++++++++--------- 8 files changed, 136 insertions(+), 15 deletions(-) create mode 100644 tests/regression/04-mutex/77-type-nested-fields.t create mode 100644 tests/regression/04-mutex/79-type-nested-fields-deep1.t create mode 100644 tests/regression/04-mutex/80-type-nested-fields-deep2.t diff --git a/tests/regression/04-mutex/77-type-nested-fields.c b/tests/regression/04-mutex/77-type-nested-fields.c index 6f173d6fec..a526defb06 100644 --- a/tests/regression/04-mutex/77-type-nested-fields.c +++ b/tests/regression/04-mutex/77-type-nested-fields.c @@ -2,6 +2,14 @@ #include #include +// (int) (S) (T) (U) +// \ / \ / \ / +// >f< s t +// \ / \ / +// >f< s +// \ / +// f + struct S { int field; }; diff --git a/tests/regression/04-mutex/77-type-nested-fields.t b/tests/regression/04-mutex/77-type-nested-fields.t new file mode 100644 index 0000000000..2cbd339dfa --- /dev/null +++ b/tests/regression/04-mutex/77-type-nested-fields.t @@ -0,0 +1,29 @@ + $ goblint --enable allglobs 77-type-nested-fields.c + [Error][Imprecise][Unsound] Function definition missing for getS (77-type-nested-fields.c:32:3-32:20) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (77-type-nested-fields.c:32:3-32:20) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (77-type-nested-fields.c:32:3-32:20) + [Info][Unsound] Unknown address in {&tmp} has escaped. (77-type-nested-fields.c:32:3-32:20) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (77-type-nested-fields.c:32:3-32:20) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (77-type-nested-fields.c:32:3-32:20) + [Info][Unsound] Write to unknown address: privatization is unsound. (77-type-nested-fields.c:32:3-32:20) + [Error][Imprecise][Unsound] Function definition missing for getT (77-type-nested-fields.c:39:3-39:22) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (77-type-nested-fields.c:39:3-39:22) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (77-type-nested-fields.c:39:3-39:22) + [Info][Unsound] Unknown address in {&tmp} has escaped. (77-type-nested-fields.c:39:3-39:22) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (77-type-nested-fields.c:39:3-39:22) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (77-type-nested-fields.c:39:3-39:22) + [Info][Unsound] Write to unknown address: privatization is unsound. (77-type-nested-fields.c:39:3-39:22) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 0 + total lines: 7 + [Warning][Race] Memory location (struct T).s.field (race with conf. 100): + write with [mhp:{tid=[main, t_fun@77-type-nested-fields.c:38:3-38:40#top]}, thread:[main, t_fun@77-type-nested-fields.c:38:3-38:40#top]] (conf. 100) (exp: & tmp->field) (77-type-nested-fields.c:32:3-32:20) + write with [mhp:{tid=[main]; created={[main, t_fun@77-type-nested-fields.c:38:3-38:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->s.field) (77-type-nested-fields.c:39:3-39:22) + [Success][Race] Memory location (struct S).field (safe): + write with [mhp:{tid=[main, t_fun@77-type-nested-fields.c:38:3-38:40#top]}, thread:[main, t_fun@77-type-nested-fields.c:38:3-38:40#top]] (conf. 100) (exp: & tmp->field) (77-type-nested-fields.c:32:3-32:20) + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 1 + total memory locations: 2 diff --git a/tests/regression/04-mutex/79-type-nested-fields-deep1.c b/tests/regression/04-mutex/79-type-nested-fields-deep1.c index ee99c40973..c38e700829 100644 --- a/tests/regression/04-mutex/79-type-nested-fields-deep1.c +++ b/tests/regression/04-mutex/79-type-nested-fields-deep1.c @@ -2,6 +2,14 @@ #include #include +// (int) (S) (T) (U) +// \ / \ / \ / +// >f< s t +// \ / \ / +// f s +// \ / +// >f< + struct S { int field; }; diff --git a/tests/regression/04-mutex/79-type-nested-fields-deep1.t b/tests/regression/04-mutex/79-type-nested-fields-deep1.t new file mode 100644 index 0000000000..6bb9b040fd --- /dev/null +++ b/tests/regression/04-mutex/79-type-nested-fields-deep1.t @@ -0,0 +1,31 @@ + $ goblint --enable allglobs 79-type-nested-fields-deep1.c + [Error][Imprecise][Unsound] Function definition missing for getS (79-type-nested-fields-deep1.c:37:3-37:20) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (79-type-nested-fields-deep1.c:37:3-37:20) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (79-type-nested-fields-deep1.c:37:3-37:20) + [Info][Unsound] Unknown address in {&tmp} has escaped. (79-type-nested-fields-deep1.c:37:3-37:20) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (79-type-nested-fields-deep1.c:37:3-37:20) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (79-type-nested-fields-deep1.c:37:3-37:20) + [Info][Unsound] Write to unknown address: privatization is unsound. (79-type-nested-fields-deep1.c:37:3-37:20) + [Error][Imprecise][Unsound] Function definition missing for getU (79-type-nested-fields-deep1.c:44:3-44:24) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (79-type-nested-fields-deep1.c:44:3-44:24) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (79-type-nested-fields-deep1.c:44:3-44:24) + [Info][Unsound] Unknown address in {&tmp} has escaped. (79-type-nested-fields-deep1.c:44:3-44:24) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (79-type-nested-fields-deep1.c:44:3-44:24) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (79-type-nested-fields-deep1.c:44:3-44:24) + [Info][Unsound] Write to unknown address: privatization is unsound. (79-type-nested-fields-deep1.c:44:3-44:24) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 0 + total lines: 7 + [Success][Race] Memory location (struct T).s.field (safe): + write with [mhp:{tid=[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]}, thread:[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (79-type-nested-fields-deep1.c:37:3-37:20) + [Success][Race] Memory location (struct S).field (safe): + write with [mhp:{tid=[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]}, thread:[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (79-type-nested-fields-deep1.c:37:3-37:20) + [Warning][Race] Memory location (struct U).t.s.field (race with conf. 100): + write with [mhp:{tid=[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]}, thread:[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (79-type-nested-fields-deep1.c:37:3-37:20) + write with [mhp:{tid=[main]; created={[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t.s.field) (79-type-nested-fields-deep1.c:44:3-44:24) + [Info][Race] Memory locations race summary: + safe: 2 + vulnerable: 0 + unsafe: 1 + total memory locations: 3 diff --git a/tests/regression/04-mutex/80-type-nested-fields-deep2.c b/tests/regression/04-mutex/80-type-nested-fields-deep2.c index 646acd9147..9a1e3028a3 100644 --- a/tests/regression/04-mutex/80-type-nested-fields-deep2.c +++ b/tests/regression/04-mutex/80-type-nested-fields-deep2.c @@ -2,6 +2,14 @@ #include #include +// (int) (S) (T) (U) +// \ / \ / \ / +// f s t +// \ / \ / +// >f< s +// \ / +// >f< + struct S { int field; }; diff --git a/tests/regression/04-mutex/80-type-nested-fields-deep2.t b/tests/regression/04-mutex/80-type-nested-fields-deep2.t new file mode 100644 index 0000000000..f14a315de2 --- /dev/null +++ b/tests/regression/04-mutex/80-type-nested-fields-deep2.t @@ -0,0 +1,29 @@ + $ goblint --enable allglobs 80-type-nested-fields-deep2.c + [Error][Imprecise][Unsound] Function definition missing for getT (80-type-nested-fields-deep2.c:37:3-37:22) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (80-type-nested-fields-deep2.c:37:3-37:22) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (80-type-nested-fields-deep2.c:37:3-37:22) + [Info][Unsound] Unknown address in {&tmp} has escaped. (80-type-nested-fields-deep2.c:37:3-37:22) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (80-type-nested-fields-deep2.c:37:3-37:22) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (80-type-nested-fields-deep2.c:37:3-37:22) + [Info][Unsound] Write to unknown address: privatization is unsound. (80-type-nested-fields-deep2.c:37:3-37:22) + [Error][Imprecise][Unsound] Function definition missing for getU (80-type-nested-fields-deep2.c:44:3-44:24) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (80-type-nested-fields-deep2.c:44:3-44:24) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (80-type-nested-fields-deep2.c:44:3-44:24) + [Info][Unsound] Unknown address in {&tmp} has escaped. (80-type-nested-fields-deep2.c:44:3-44:24) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (80-type-nested-fields-deep2.c:44:3-44:24) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (80-type-nested-fields-deep2.c:44:3-44:24) + [Info][Unsound] Write to unknown address: privatization is unsound. (80-type-nested-fields-deep2.c:44:3-44:24) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 0 + total lines: 7 + [Success][Race] Memory location (struct T).s.field (safe): + write with [mhp:{tid=[main, t_fun@80-type-nested-fields-deep2.c:43:3-43:40#top]}, thread:[main, t_fun@80-type-nested-fields-deep2.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->s.field) (80-type-nested-fields-deep2.c:37:3-37:22) + [Warning][Race] Memory location (struct U).t.s.field (race with conf. 100): + write with [mhp:{tid=[main, t_fun@80-type-nested-fields-deep2.c:43:3-43:40#top]}, thread:[main, t_fun@80-type-nested-fields-deep2.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->s.field) (80-type-nested-fields-deep2.c:37:3-37:22) + write with [mhp:{tid=[main]; created={[main, t_fun@80-type-nested-fields-deep2.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t.s.field) (80-type-nested-fields-deep2.c:44:3-44:24) + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 1 + total memory locations: 2 diff --git a/tests/regression/06-symbeq/16-type_rc.c b/tests/regression/06-symbeq/16-type_rc.c index efeb6c768b..e9e7c7972b 100644 --- a/tests/regression/06-symbeq/16-type_rc.c +++ b/tests/regression/06-symbeq/16-type_rc.c @@ -1,6 +1,14 @@ // PARAM: --enable ana.race.direct-arithmetic --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" #include +//>(int)< (S) (T) (U) +// \ / \ / \ / +// >f< s t +// \ / \ / +// f s +// \ / +// f + struct s { int datum; pthread_mutex_t mutex; diff --git a/tests/regression/06-symbeq/16-type_rc.t b/tests/regression/06-symbeq/16-type_rc.t index 78c293b7ef..06a3b314a4 100644 --- a/tests/regression/06-symbeq/16-type_rc.t +++ b/tests/regression/06-symbeq/16-type_rc.t @@ -1,22 +1,22 @@ Disable info messages because race summary contains (safe) memory location count, which is different on Linux and OSX. $ goblint --enable warn.deterministic --disable warn.info --enable ana.race.direct-arithmetic --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" 16-type_rc.c - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (16-type_rc.c:13:3-13:15) - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (16-type_rc.c:24:3-24:16) - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (16-type_rc.c:25:3-25:16) - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (16-type_rc.c:28:3-28:9) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (16-type_rc.c:21:3-21:15) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (16-type_rc.c:32:3-32:16) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (16-type_rc.c:33:3-33:16) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (16-type_rc.c:36:3-36:9) [Warning][Race] Memory location (struct s).datum (race with conf. 100): - write with [mhp:{tid=[main, t_fun@16-type_rc.c:27:3-27:37#top]}, thread:[main, t_fun@16-type_rc.c:27:3-27:37#top]] (conf. 100) (exp: & s->datum) (16-type_rc.c:13:3-13:15) - write with [mhp:{tid=[main]; created={[main, t_fun@16-type_rc.c:27:3-27:37#top]}}, thread:[main]] (conf. 100) (exp: & *d) (16-type_rc.c:28:3-28:9) - [Error][Imprecise][Unsound] Function definition missing for get_s (16-type_rc.c:12:12-12:24) - [Error][Imprecise][Unsound] Function definition missing for get_s (16-type_rc.c:23:3-23:14) + write with [mhp:{tid=[main, t_fun@16-type_rc.c:35:3-35:37#top]}, thread:[main, t_fun@16-type_rc.c:35:3-35:37#top]] (conf. 100) (exp: & s->datum) (16-type_rc.c:21:3-21:15) + write with [mhp:{tid=[main]; created={[main, t_fun@16-type_rc.c:35:3-35:37#top]}}, thread:[main]] (conf. 100) (exp: & *d) (16-type_rc.c:36:3-36:9) + [Error][Imprecise][Unsound] Function definition missing for get_s (16-type_rc.c:20:12-20:24) + [Error][Imprecise][Unsound] Function definition missing for get_s (16-type_rc.c:31:3-31:14) $ goblint --enable warn.deterministic --disable warn.info --disable ana.race.direct-arithmetic --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" --enable allglobs 16-type_rc.c - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (16-type_rc.c:13:3-13:15) - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (16-type_rc.c:24:3-24:16) - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (16-type_rc.c:25:3-25:16) - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (16-type_rc.c:28:3-28:9) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (16-type_rc.c:21:3-21:15) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (16-type_rc.c:32:3-32:16) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (16-type_rc.c:33:3-33:16) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (16-type_rc.c:36:3-36:9) [Success][Race] Memory location (struct s).datum (safe): - write with [mhp:{tid=[main, t_fun@16-type_rc.c:27:3-27:37#top]}, thread:[main, t_fun@16-type_rc.c:27:3-27:37#top]] (conf. 100) (exp: & s->datum) (16-type_rc.c:13:3-13:15) - [Error][Imprecise][Unsound] Function definition missing for get_s (16-type_rc.c:12:12-12:24) - [Error][Imprecise][Unsound] Function definition missing for get_s (16-type_rc.c:23:3-23:14) + write with [mhp:{tid=[main, t_fun@16-type_rc.c:35:3-35:37#top]}, thread:[main, t_fun@16-type_rc.c:35:3-35:37#top]] (conf. 100) (exp: & s->datum) (16-type_rc.c:21:3-21:15) + [Error][Imprecise][Unsound] Function definition missing for get_s (16-type_rc.c:20:12-20:24) + [Error][Imprecise][Unsound] Function definition missing for get_s (16-type_rc.c:31:3-31:14) From c2e08ef4724659bb05baeefdc3f18d9fe0d968b2 Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Mon, 19 Jun 2023 14:16:33 +0200 Subject: [PATCH 0158/1312] 80/23 fixed --- .../80-termination/23-exit-on-rand-terminating.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/regression/80-termination/23-exit-on-rand-terminating.c b/tests/regression/80-termination/23-exit-on-rand-terminating.c index f793275b1f..c8118a4bde 100644 --- a/tests/regression/80-termination/23-exit-on-rand-terminating.c +++ b/tests/regression/80-termination/23-exit-on-rand-terminating.c @@ -1,16 +1,18 @@ // PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include +#include int main() { - int shortrun, i = 0; + int short_run, i = 0; - while (i < 90 || shortrun == 1) + while (i < 90 && short_run != 1) { i++; if (rand()) { - shortrun = 1; + short_run = 1; } + printf("%i %i\n", i, short_run); } } \ No newline at end of file From 4ec1e9862fddea279e4a23a328bf9481ef1c4552 Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Mon, 19 Jun 2023 14:16:58 +0200 Subject: [PATCH 0159/1312] removed print --- tests/regression/80-termination/23-exit-on-rand-terminating.c | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/regression/80-termination/23-exit-on-rand-terminating.c b/tests/regression/80-termination/23-exit-on-rand-terminating.c index c8118a4bde..228fc3b15e 100644 --- a/tests/regression/80-termination/23-exit-on-rand-terminating.c +++ b/tests/regression/80-termination/23-exit-on-rand-terminating.c @@ -13,6 +13,5 @@ int main() { short_run = 1; } - printf("%i %i\n", i, short_run); } } \ No newline at end of file From 9bba9a1061b6ec1504b247cf26d866d91f5dfc8f Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Mon, 19 Jun 2023 14:24:56 +0200 Subject: [PATCH 0160/1312] Param unified --- .../regression/80-termination/09-complex-for-loop-terminating.c | 2 +- tests/regression/80-termination/10-complex-loop-terminating.c | 2 +- .../80-termination/15-complex-loop-combination-terminating.c | 2 +- .../80-termination/24-upjumping-goto-loopless-terminating.c | 1 + .../regression/80-termination/25-leave-loop-goto-terminating.c | 1 + .../regression/80-termination/26-enter-loop-goto-terminating.c | 1 + .../80-termination/27-upjumping-goto-nonterminating.c | 1 + tests/regression/81-recursion/01-simple-terminating.c | 1 + tests/regression/81-recursion/02-simple-nonterminating.c | 1 + tests/regression/81-recursion/03-nested-terminating.c | 1 + tests/regression/81-recursion/04-nested-nonterminating.c | 1 + 11 files changed, 11 insertions(+), 3 deletions(-) diff --git a/tests/regression/80-termination/09-complex-for-loop-terminating.c b/tests/regression/80-termination/09-complex-for-loop-terminating.c index 508b31500c..ed28fa9b43 100644 --- a/tests/regression/80-termination/09-complex-for-loop-terminating.c +++ b/tests/regression/80-termination/09-complex-for-loop-terminating.c @@ -1,4 +1,4 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/80-termination/10-complex-loop-terminating.c b/tests/regression/80-termination/10-complex-loop-terminating.c index 9d5cd4b928..3a19f17bee 100644 --- a/tests/regression/80-termination/10-complex-loop-terminating.c +++ b/tests/regression/80-termination/10-complex-loop-terminating.c @@ -1,4 +1,4 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/80-termination/15-complex-loop-combination-terminating.c b/tests/regression/80-termination/15-complex-loop-combination-terminating.c index 1ea228ae55..e5383aed66 100644 --- a/tests/regression/80-termination/15-complex-loop-combination-terminating.c +++ b/tests/regression/80-termination/15-complex-loop-combination-terminating.c @@ -1,4 +1,4 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/80-termination/24-upjumping-goto-loopless-terminating.c b/tests/regression/80-termination/24-upjumping-goto-loopless-terminating.c index aea0d5dd97..f4b6b8fbe2 100644 --- a/tests/regression/80-termination/24-upjumping-goto-loopless-terminating.c +++ b/tests/regression/80-termination/24-upjumping-goto-loopless-terminating.c @@ -1,3 +1,4 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/80-termination/25-leave-loop-goto-terminating.c b/tests/regression/80-termination/25-leave-loop-goto-terminating.c index 34c14eedd6..c30e65f44b 100644 --- a/tests/regression/80-termination/25-leave-loop-goto-terminating.c +++ b/tests/regression/80-termination/25-leave-loop-goto-terminating.c @@ -1,3 +1,4 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/80-termination/26-enter-loop-goto-terminating.c b/tests/regression/80-termination/26-enter-loop-goto-terminating.c index d3158ae7f7..5d34e5c523 100644 --- a/tests/regression/80-termination/26-enter-loop-goto-terminating.c +++ b/tests/regression/80-termination/26-enter-loop-goto-terminating.c @@ -1,3 +1,4 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/80-termination/27-upjumping-goto-nonterminating.c b/tests/regression/80-termination/27-upjumping-goto-nonterminating.c index b6dc238fe3..6e4432dc5e 100644 --- a/tests/regression/80-termination/27-upjumping-goto-nonterminating.c +++ b/tests/regression/80-termination/27-upjumping-goto-nonterminating.c @@ -1,3 +1,4 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/81-recursion/01-simple-terminating.c b/tests/regression/81-recursion/01-simple-terminating.c index 9e509ebaa9..1c52faec68 100644 --- a/tests/regression/81-recursion/01-simple-terminating.c +++ b/tests/regression/81-recursion/01-simple-terminating.c @@ -1,3 +1,4 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include void recursiveFunction(int n) { diff --git a/tests/regression/81-recursion/02-simple-nonterminating.c b/tests/regression/81-recursion/02-simple-nonterminating.c index ec9d31ffcb..a6d6b3ab17 100644 --- a/tests/regression/81-recursion/02-simple-nonterminating.c +++ b/tests/regression/81-recursion/02-simple-nonterminating.c @@ -1,3 +1,4 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include void recursiveFunction(int n) { diff --git a/tests/regression/81-recursion/03-nested-terminating.c b/tests/regression/81-recursion/03-nested-terminating.c index 6cd28043ee..096a1b0121 100644 --- a/tests/regression/81-recursion/03-nested-terminating.c +++ b/tests/regression/81-recursion/03-nested-terminating.c @@ -1,3 +1,4 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include void innerRecursiveFunction(int n) { diff --git a/tests/regression/81-recursion/04-nested-nonterminating.c b/tests/regression/81-recursion/04-nested-nonterminating.c index 96911687da..ab5e35d80f 100644 --- a/tests/regression/81-recursion/04-nested-nonterminating.c +++ b/tests/regression/81-recursion/04-nested-nonterminating.c @@ -1,3 +1,4 @@ +// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include void innerRecursiveFunction() { From 91b64e827dbaeacac52497482c72cbf8a7ceadd6 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Mon, 19 Jun 2023 16:24:48 +0300 Subject: [PATCH 0161/1312] Fix test 04 93 --- .../93-distribute-fields-type-global.c | 4 +-- .../93-distribute-fields-type-global.t | 28 ++++++++++++++++--- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/tests/regression/04-mutex/93-distribute-fields-type-global.c b/tests/regression/04-mutex/93-distribute-fields-type-global.c index e0065b7870..ad7839d95f 100644 --- a/tests/regression/04-mutex/93-distribute-fields-type-global.c +++ b/tests/regression/04-mutex/93-distribute-fields-type-global.c @@ -8,14 +8,14 @@ struct S { struct S s; +extern struct S* getS(); + void *t_fun(void *arg) { printf("%d",getS()->field); // RACE! return NULL; } -extern struct S* getS(); - int main(void) { pthread_t id; pthread_create(&id, NULL, t_fun, NULL); diff --git a/tests/regression/04-mutex/93-distribute-fields-type-global.t b/tests/regression/04-mutex/93-distribute-fields-type-global.t index 90baf61492..30f61cb3cc 100644 --- a/tests/regression/04-mutex/93-distribute-fields-type-global.t +++ b/tests/regression/04-mutex/93-distribute-fields-type-global.t @@ -1,5 +1,25 @@ $ goblint --enable allglobs 93-distribute-fields-type-global.c - 93-distribute-fields-type-global.c:12: Error: expecting a pointer to a struct - Error: There were parsing errors in .goblint/preprocessed/93-distribute-fields-type-global.i - Fatal error: exception Goblint_lib__Maingoblint.FrontendError("Errormsg.Error") - [2] + [Error][Imprecise][Unsound] Function definition missing for getS (93-distribute-fields-type-global.c:14:3-14:29) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (93-distribute-fields-type-global.c:14:3-14:29) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(s, NoOffset)) (93-distribute-fields-type-global.c:14:3-14:29) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (93-distribute-fields-type-global.c:14:3-14:29) + [Info][Unsound] Unknown address in {&tmp} has escaped. (93-distribute-fields-type-global.c:14:3-14:29) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (93-distribute-fields-type-global.c:14:3-14:29) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (93-distribute-fields-type-global.c:14:3-14:29) + [Info][Unsound] Write to unknown address: privatization is unsound. (93-distribute-fields-type-global.c:14:3-14:29) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 0 + total lines: 7 + [Warning][Race] Memory location s.field@93-distribute-fields-type-global.c:9:10-9:11 (race with conf. 110): + read with [mhp:{tid=[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]}, thread:[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]] (conf. 100) (exp: & tmp->field) (93-distribute-fields-type-global.c:14:3-14:29) + write with [mhp:{tid=[main]; created={[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]}}, thread:[main]] (conf. 110) (exp: & s) (93-distribute-fields-type-global.c:23:3-23:9) + [Success][Race] Memory location s@93-distribute-fields-type-global.c:9:10-9:11 (safe): + write with [mhp:{tid=[main]; created={[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]}}, thread:[main]] (conf. 110) (exp: & s) (93-distribute-fields-type-global.c:23:3-23:9) + [Success][Race] Memory location (struct S).field (safe): + read with [mhp:{tid=[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]}, thread:[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]] (conf. 100) (exp: & tmp->field) (93-distribute-fields-type-global.c:14:3-14:29) + [Info][Race] Memory locations race summary: + safe: 2 + vulnerable: 0 + unsafe: 1 + total memory locations: 3 From 4ac338dbfa8813197663a250b742d71ed5b9bf51 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Mon, 19 Jun 2023 16:47:09 +0300 Subject: [PATCH 0162/1312] Move access outer distribute to raceAnalysis Co-authored-by: Simmo Saan --- src/analyses/raceAnalysis.ml | 41 ++++++++++++++++--- src/domains/access.ml | 23 +---------- .../04-mutex/79-type-nested-fields-deep1.t | 6 +-- .../04-mutex/84-distribute-fields-1.t | 4 +- .../04-mutex/85-distribute-fields-2.t | 4 +- .../04-mutex/86-distribute-fields-3.t | 4 +- 6 files changed, 44 insertions(+), 38 deletions(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index 802c83dadc..f2d2554abc 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -54,6 +54,12 @@ struct struct include TrieDomain.Make (OneOffset) (Access.AS) + let rec find (offset : Offset.Unit.t) ((accs, children) : t) : value = + match offset with + | `NoOffset -> accs + | `Field (f, offset') -> find offset' (ChildMap.find (Field f) children) + | `Index ((), offset') -> find offset' (ChildMap.find Index children) + let rec singleton (offset : Offset.Unit.t) (value : value) : t = match offset with | `NoOffset -> (value, ChildMap.empty ()) @@ -99,6 +105,22 @@ struct ctx.sideg (V.access memoroot) (G.create_access (OffsetTrie.singleton offset (Access.AS.singleton (conf, w, loc, e, a)))); side_vars ctx memo + let outer_memo ((root, offset) : Access.Memo.t) : Access.Memo.t option = + match offset with + | `NoOffset -> None + | `Field (f, offset') -> Some (`Type f.ftype, offset') + | `Index ((), offset') -> None (* TODO *) + + let rec distribute_outer ctx ((root, offset) : Access.Memo.t) : Access.AS.t = + let trie = G.access (ctx.global (V.access root)) in + let accs = OffsetTrie.find offset trie in + let outer_accs = + match outer_memo (root, offset) with + | Some outer_memo -> distribute_outer ctx outer_memo + | None -> Access.AS.empty () + in + Access.AS.union accs outer_accs + let query ctx (type a) (q: a Queries.t): a Queries.result = match q with | WarnGlobal g -> @@ -109,15 +131,22 @@ struct let trie = G.access (ctx.global g) in (** Distribute access to contained fields. *) let rec distribute_inner offset (accs, children) ancestor_accs = - let ancestor_accs' = Access.AS.union ancestor_accs accs in - OffsetTrie.ChildMap.iter (fun child_key child_trie -> - distribute_inner (Offset.Unit.add_offset offset (OneOffset.to_offset child_key)) child_trie ancestor_accs' - ) children; + let outer_accs = + match outer_memo (g', offset) with + | Some outer_memo -> distribute_outer ctx outer_memo + | None -> Access.AS.empty () + in + M.trace "access" "outer accs = %a" Access.AS.pretty outer_accs; + let ancestor_accs' = Access.AS.union ancestor_accs outer_accs in if not (Access.AS.is_empty accs) then ( let memo = (g', offset) in let mem_loc_str = GobPretty.sprint Access.Memo.pretty memo in - Timing.wrap ~args:[("memory location", `String mem_loc_str)] "race" (Access.warn_global ~safe ~vulnerable ~unsafe ~ancestor_accs memo) accs - ) + Timing.wrap ~args:[("memory location", `String mem_loc_str)] "race" (Access.warn_global ~safe ~vulnerable ~unsafe ~ancestor_accs:ancestor_accs' memo) accs + ); + let ancestor_accs'' = Access.AS.union ancestor_accs' accs in + OffsetTrie.ChildMap.iter (fun child_key child_trie -> + distribute_inner (Offset.Unit.add_offset offset (OneOffset.to_offset child_key)) child_trie ancestor_accs'' + ) children; in distribute_inner `NoOffset trie (Access.AS.empty ()) | `Right _ -> (* vars *) diff --git a/src/domains/access.ml b/src/domains/access.ml index 798a35ee9c..0e029ff128 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -211,27 +211,6 @@ let add_one side memo: unit = if not ignorable then side memo -(** Distribute type-based access to variables and containing fields. *) -let rec add_distribute_outer side (t: typ) (o: Offset.Unit.t) = - let memo = (`Type t, o) in - if M.tracing then M.tracei "access" "add_distribute_outer %a\n" Memo.pretty memo; - add_one side memo; - - (* distribute to variables of the type *) - let ts = typeSig t in - let vars = TSH.find_all typeVar ts in - List.iter (fun v -> - add_one side (`Var v, o) (* same offset, but on variable *) - ) vars; - - (* recursively distribute to fields containing the type *) - let fields = TSH.find_all typeIncl ts in - List.iter (fun f -> - (* prepend field and distribute to outer struct *) - add_distribute_outer side (TComp (f.fcomp, [])) (`Field (f, o)) - ) fields; - - if M.tracing then M.traceu "access" "add_distribute_outer\n" (** Add access to known variable with offsets or unknown variable from expression. *) let add side e voffs = @@ -249,7 +228,7 @@ let add side e voffs = in match o with | `NoOffset when not !collect_direct_arithmetic && isArithmeticType t -> () - | _ -> add_distribute_outer side t o (* distribute to variables and outer offsets *) + | _ -> add_one side (`Type t, o) (* add_distribute_outer side t o (* distribute to variables and outer offsets *)*) end; if M.tracing then M.traceu "access" "add\n" diff --git a/tests/regression/04-mutex/79-type-nested-fields-deep1.t b/tests/regression/04-mutex/79-type-nested-fields-deep1.t index 6bb9b040fd..4075dab33b 100644 --- a/tests/regression/04-mutex/79-type-nested-fields-deep1.t +++ b/tests/regression/04-mutex/79-type-nested-fields-deep1.t @@ -17,15 +17,13 @@ live: 7 dead: 0 total lines: 7 - [Success][Race] Memory location (struct T).s.field (safe): - write with [mhp:{tid=[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]}, thread:[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (79-type-nested-fields-deep1.c:37:3-37:20) [Success][Race] Memory location (struct S).field (safe): write with [mhp:{tid=[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]}, thread:[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (79-type-nested-fields-deep1.c:37:3-37:20) [Warning][Race] Memory location (struct U).t.s.field (race with conf. 100): write with [mhp:{tid=[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]}, thread:[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (79-type-nested-fields-deep1.c:37:3-37:20) write with [mhp:{tid=[main]; created={[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t.s.field) (79-type-nested-fields-deep1.c:44:3-44:24) [Info][Race] Memory locations race summary: - safe: 2 + safe: 1 vulnerable: 0 unsafe: 1 - total memory locations: 3 + total memory locations: 2 diff --git a/tests/regression/04-mutex/84-distribute-fields-1.t b/tests/regression/04-mutex/84-distribute-fields-1.t index eb2c43623f..27653d4759 100644 --- a/tests/regression/04-mutex/84-distribute-fields-1.t +++ b/tests/regression/04-mutex/84-distribute-fields-1.t @@ -3,11 +3,11 @@ live: 8 dead: 0 total lines: 8 + [Success][Race] Memory location s@84-distribute-fields-1.c:9:10-9:11 (safe): + write with [mhp:{tid=[main]; created={[main, t_fun@84-distribute-fields-1.c:18:3-18:40#top]}}, thread:[main]] (conf. 110) (exp: & s) (84-distribute-fields-1.c:20:3-20:9) [Warning][Race] Memory location s.data@84-distribute-fields-1.c:9:10-9:11 (race with conf. 110): write with [mhp:{tid=[main, t_fun@84-distribute-fields-1.c:18:3-18:40#top]}, thread:[main, t_fun@84-distribute-fields-1.c:18:3-18:40#top]] (conf. 110) (exp: & s.data) (84-distribute-fields-1.c:12:3-12:13) write with [mhp:{tid=[main]; created={[main, t_fun@84-distribute-fields-1.c:18:3-18:40#top]}}, thread:[main]] (conf. 110) (exp: & s) (84-distribute-fields-1.c:20:3-20:9) - [Success][Race] Memory location s@84-distribute-fields-1.c:9:10-9:11 (safe): - write with [mhp:{tid=[main]; created={[main, t_fun@84-distribute-fields-1.c:18:3-18:40#top]}}, thread:[main]] (conf. 110) (exp: & s) (84-distribute-fields-1.c:20:3-20:9) [Info][Race] Memory locations race summary: safe: 1 vulnerable: 0 diff --git a/tests/regression/04-mutex/85-distribute-fields-2.t b/tests/regression/04-mutex/85-distribute-fields-2.t index 7039fc399c..19355f7bc9 100644 --- a/tests/regression/04-mutex/85-distribute-fields-2.t +++ b/tests/regression/04-mutex/85-distribute-fields-2.t @@ -3,11 +3,11 @@ live: 8 dead: 0 total lines: 8 + [Success][Race] Memory location t.s@85-distribute-fields-2.c:15:10-15:11 (safe): + write with [mhp:{tid=[main]; created={[main, t_fun@85-distribute-fields-2.c:24:3-24:40#top]}}, thread:[main]] (conf. 110) (exp: & t.s) (85-distribute-fields-2.c:26:3-26:11) [Warning][Race] Memory location t.s.data@85-distribute-fields-2.c:15:10-15:11 (race with conf. 110): write with [mhp:{tid=[main, t_fun@85-distribute-fields-2.c:24:3-24:40#top]}, thread:[main, t_fun@85-distribute-fields-2.c:24:3-24:40#top]] (conf. 110) (exp: & t.s.data) (85-distribute-fields-2.c:18:3-18:15) write with [mhp:{tid=[main]; created={[main, t_fun@85-distribute-fields-2.c:24:3-24:40#top]}}, thread:[main]] (conf. 110) (exp: & t.s) (85-distribute-fields-2.c:26:3-26:11) - [Success][Race] Memory location t.s@85-distribute-fields-2.c:15:10-15:11 (safe): - write with [mhp:{tid=[main]; created={[main, t_fun@85-distribute-fields-2.c:24:3-24:40#top]}}, thread:[main]] (conf. 110) (exp: & t.s) (85-distribute-fields-2.c:26:3-26:11) [Info][Race] Memory locations race summary: safe: 1 vulnerable: 0 diff --git a/tests/regression/04-mutex/86-distribute-fields-3.t b/tests/regression/04-mutex/86-distribute-fields-3.t index 5557f3400a..9651a91923 100644 --- a/tests/regression/04-mutex/86-distribute-fields-3.t +++ b/tests/regression/04-mutex/86-distribute-fields-3.t @@ -3,11 +3,11 @@ live: 8 dead: 0 total lines: 8 + [Success][Race] Memory location t@86-distribute-fields-3.c:15:10-15:11 (safe): + write with [mhp:{tid=[main]; created={[main, t_fun@86-distribute-fields-3.c:24:3-24:40#top]}}, thread:[main]] (conf. 110) (exp: & t) (86-distribute-fields-3.c:26:3-26:9) [Warning][Race] Memory location t.s.data@86-distribute-fields-3.c:15:10-15:11 (race with conf. 110): write with [mhp:{tid=[main, t_fun@86-distribute-fields-3.c:24:3-24:40#top]}, thread:[main, t_fun@86-distribute-fields-3.c:24:3-24:40#top]] (conf. 110) (exp: & t.s.data) (86-distribute-fields-3.c:18:3-18:15) write with [mhp:{tid=[main]; created={[main, t_fun@86-distribute-fields-3.c:24:3-24:40#top]}}, thread:[main]] (conf. 110) (exp: & t) (86-distribute-fields-3.c:26:3-26:9) - [Success][Race] Memory location t@86-distribute-fields-3.c:15:10-15:11 (safe): - write with [mhp:{tid=[main]; created={[main, t_fun@86-distribute-fields-3.c:24:3-24:40#top]}}, thread:[main]] (conf. 110) (exp: & t) (86-distribute-fields-3.c:26:3-26:9) [Info][Race] Memory locations race summary: safe: 1 vulnerable: 0 From 0c41ac6ef089ac5c486687a29bb1eb6a3b42cc4e Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Mon, 19 Jun 2023 16:12:48 +0200 Subject: [PATCH 0163/1312] Fix from master --- src/framework/analyses.ml | 53 --------------------------------------- 1 file changed, 53 deletions(-) diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 7fd57b357e..1d1972ac45 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -119,59 +119,6 @@ struct | x -> BatPrintf.fprintf f "%a" printXml x end -module GMapG (G: Lattice.S) (C: Printable.S) = -struct - module CVal = - struct - include Printable.Std (* To make it Groupable *) - include SetDomain.Make ( - struct - include C - let printXml f c = BatPrintf.fprintf f "%a" printXml c (* wrap in for HTML printing *) - end - ) - let name () = "contextsMap" - end - - module RangeVal = - struct - include SetDomain.Make ( - struct - include C (*TODO: sollte hier iwi ein tupel sein*) - let printXml f c = BatPrintf.fprintf f "%a" printXml c (* wrap in for HTML printing *) - end - ) - let name () = "contextsMap" - end - - module CMap = - struct - include MapDomain.MapBot (CVal) (RangeVal) - let name () = "contextsMap" - end - include Lattice.Lift2 (G) (CMap) (Printable.DefaultNames) - - let is_bot () = false - let is_top () = false - - (*let spec = function - | `Bot -> G.bot () - | `Lifted1 x -> x - | _ -> failwith "GVarG.spec" - let contexts = function - | `Bot -> CSet.bot () - | `Lifted2 x -> x - | _ -> failwith "GVarG.contexts" - let create_spec spec = `Lifted1 spec - let create_contexts contexts = `Lifted2 contexts - - let printXml f = function - | `Lifted1 x -> G.printXml f x - | `Lifted2 x -> BatPrintf.fprintf f "%a" CSet.printXml x - | x -> BatPrintf.fprintf f "%a" printXml x -*) -end - module GMapG (G: Lattice.S) (C: Printable.S) = struct From c2ababd0d10671e3c59ec46a361733b2d6ed6c89 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Mon, 19 Jun 2023 17:08:34 +0200 Subject: [PATCH 0164/1312] added new massage category --- src/framework/constraints.ml | 4 ++-- src/util/options.schema.json | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 1e2644e394..a46abccb49 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1739,7 +1739,7 @@ struct [ (Pretty.dprintf "The program might not terminate! (Fundec %a is contained in a call graph cycle)\n" CilType.Fundec.pretty fundec_e, Some (M.Location.CilLocation locUnknown)); ] in - M.msg_group Warning "Recursion cycle" msgs) + M.msg_group Warning ~category:NonTerminating "Recursion cycle" msgs) else if not (LH.mem global_visited_calls call) then begin try LH.replace global_visited_calls call (); @@ -1773,7 +1773,7 @@ struct [ (Pretty.dprintf "The program might not terminate! (Loop analysis)\n", Some (M.Location.CilLocation locUnknown)); ] in - M.msg_group Warning "Possibly non terminating loops" msgs); + M.msg_group Warning ~category:NonTerminating "Possibly non terminating loops" msgs); printf "true" diff --git a/src/util/options.schema.json b/src/util/options.schema.json index 02fc929a8a..d49b30aca7 100644 --- a/src/util/options.schema.json +++ b/src/util/options.schema.json @@ -2078,6 +2078,12 @@ "type": "boolean", "default": true }, + "nonTerminating": { + "title": "warn.nonTerminating", + "description": "nonTerminating warning", + "type": "boolean", + "default": true + }, "unknown": { "title": "warn.unknown", "description": "Unknown (of string) warnings", From 49c3a75751564fb78ce5dda18fc792928b3ace45 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Mon, 19 Jun 2023 17:44:21 +0200 Subject: [PATCH 0165/1312] deleted queries, comments and todos refreshed :), fixed indentation (hopefully) --- src/domains/queries.ml | 5 --- src/framework/analyses.ml | 76 ++++-------------------------------- src/framework/constraints.ml | 28 ++++++------- 3 files changed, 19 insertions(+), 90 deletions(-) diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 6603175f36..c5d7d729b6 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -119,7 +119,6 @@ type _ t = | MayBeModifiedSinceSetjmp: JmpBufDomain.BufferEntry.t -> VS.t t | MustTermLoop: stmt -> MustBool.t t | MustTermProg: MustBool.t t - | MustTermProgWithRec: MustBool.t t type 'a result = 'a @@ -186,7 +185,6 @@ struct | MayBeModifiedSinceSetjmp _ -> (module VS) | MustTermLoop _ -> (module MustBool) | MustTermProg -> (module MustBool) - | MustTermProgWithRec -> (module MustBool) (** Get bottom result for query. *) let bot (type a) (q: a t): a result = @@ -252,7 +250,6 @@ struct | MayBeModifiedSinceSetjmp _ -> VS.top () | MustTermLoop _ -> MustBool.top () | MustTermProg -> MustBool.top () - | MustTermProgWithRec -> MustBool.top () end (* The type any_query can't be directly defined in Any as t, @@ -315,7 +312,6 @@ struct | Any ThreadsJoinedCleanly -> 52 | Any (MustTermLoop _) -> 53 | Any MustTermProg -> 54 - | Any MustTermProgWithRec -> 55 let rec compare a b = let r = Stdlib.compare (order a) (order b) in @@ -460,7 +456,6 @@ struct | Any MayBeModifiedSinceSetjmp buf -> Pretty.dprintf "MayBeModifiedSinceSetjmp %a" JmpBufDomain.BufferEntry.pretty buf | Any (MustTermLoop s) -> Pretty.dprintf "MustTermLoop %a" CilType.Stmt.pretty s | Any MustTermProg -> Pretty.dprintf "MustTermProg" - | Any MustTermProgWithRec -> Pretty.dprintf "MustTermProgWithRec" end let to_value_domain_ask (ask: ask) = diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 763ffd0e56..e11903ba06 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -121,7 +121,7 @@ end (* Tuple of fundec and S.C*) -module T (Base1: Printable.S) (Base2: Printable.S) = (*Todo: is this Printable.S or S.C*) +module T (Base1: Printable.S) (Base2: Printable.S) = struct include Printable.Std type t = (Base1.t * Base2.t) @@ -140,13 +140,6 @@ struct caller_context\n%a\n\n \n" Base1.printXml a Base2.printXml b - (*Result of compare: - start with inital value of 0 - - a1 > a2: +1 - - a1 < a2: -1 - - b1 > b2: +3 - - b1 < b2: -3 - *) let compare (a1,b1) (a2,b2) = (*Todo: is this ok?*) if equal (a1, b1) (a2, b2) then 0 else( let res = ref 0 in @@ -157,6 +150,7 @@ struct if (comp_b > 0) then res := !(res) + 3 else if (comp_b < 0) then res := !(res) - 3; !res) + let pretty () x = text (show x) @@ -164,7 +158,7 @@ struct end -module GVarGG (G: Lattice.S) (C: Printable.S) (Base: Printable.S) = +module GVarGSet (G: Lattice.S) (C: Printable.S) (Base: Printable.S) = struct module CSet = @@ -186,10 +180,10 @@ struct struct include C include Printable.Std (* To make it Groupable *) - let printXml f c = BatPrintf.fprintf f (*Todo: Make this print pretty*) + let printXml f c = BatPrintf.fprintf f "\n callee_context\n%a\n\n - " printXml c (* wrap in for HTML printing *) + " printXml c end module CMap = @@ -198,7 +192,7 @@ struct let printXml f c = BatPrintf.fprintf f " ContextTupleMap\n %a\n\n - " printXml c (*TODO*) + " printXml c end include Lattice.Lift2 (G) (CMap) (Printable.DefaultNames) @@ -219,12 +213,12 @@ struct | `Lifted2 x -> BatPrintf.fprintf f "%a" CMap.printXml x | x -> BatPrintf.fprintf f "%a" printXml x - let s = function (*TODO: does this work? copied from DeadBranch*) + let s = function | `Bot -> G.bot () | `Lifted1 x -> x | _ -> failwith "RecursionTerm.s" - let create_s s = `Lifted1 s (*TODO: does this work? copied from DeadBranch*) + let create_s s = `Lifted1 s let base2 instance = match instance with @@ -232,60 +226,6 @@ struct | _ -> None end - -module GMapG (G: Lattice.S) (C: Printable.S) = -struct - module CVal = - struct - include Printable.Std (* To make it Groupable *) - include SetDomain.Make ( - struct - include C - let printXml f c = BatPrintf.fprintf f "%a" printXml c (* wrap in for HTML printing *) - end - ) - let name () = "contextsMap" - end - - module RangeVal = - struct - include SetDomain.Make ( - struct - include C (*TODO: sollte hier iwi ein tupel sein*) - let printXml f c = BatPrintf.fprintf f "%a" printXml c (* wrap in for HTML printing *) - end - ) - let name () = "contextsMap" - end - - module CMap = - struct - include MapDomain.MapBot (CVal) (RangeVal) - let name () = "contextsMap" - end - include Lattice.Lift2 (G) (CMap) (Printable.DefaultNames) - - let is_bot () = false - let is_top () = false - - (*let spec = function - | `Bot -> G.bot () - | `Lifted1 x -> x - | _ -> failwith "GVarG.spec" - let contexts = function - | `Bot -> CSet.bot () - | `Lifted2 x -> x - | _ -> failwith "GVarG.contexts" - let create_spec spec = `Lifted1 spec - let create_contexts contexts = `Lifted2 contexts - - let printXml f = function - | `Lifted1 x -> G.printXml f x - | `Lifted2 x -> BatPrintf.fprintf f "%a" CSet.printXml x - | x -> BatPrintf.fprintf f "%a" printXml x -*) -end - exception Deadcode (** [Dom (D)] produces D lifted where bottom means dead-code *) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index a46abccb49..730f0f41eb 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1712,11 +1712,10 @@ struct include GVarF(S.V) end - module G = GVarGG (S.G) (S.C) (T (CilType.Fundec) (S.C)) + module G = GVarGSet (S.G) (S.C) (T (CilType.Fundec) (S.C)) let name () = "RecursionTerm (" ^ S.name () ^ ")" - (*TODO Change the body??*) let conv (ctx: (_, G.t, _, V.t) ctx): (_, S.G.t, _, S.V.t) ctx = { ctx with global = (fun v -> G.s (ctx.global (V.spec v))); @@ -1726,7 +1725,7 @@ struct let cycleDetection ctx v v' = let module LH = Hashtbl.Make (T (CilType.Fundec) (S.C)) in let module LS = Set.Make (T (CilType.Fundec) (S.C)) in - (* TODO: find all cycles/SCCs *) + (* find all cycles/SCCs *) let global_visited_calls = LH.create 100 in (* DFS *) @@ -1748,7 +1747,7 @@ struct let fundec_e_typeV: V.t = V.relift (`Right fundec_e) in let gmap_opt = G.base2 (ctx.global (fundec_e_typeV)) in let gmap = Option.get (gmap_opt) in (*might be empty*) - let callers: G.CSet.t = G.CMap.find (context_e) gmap in (*TODO: how do we get our Map out of g*) (*Todo: the context should be the domain of the map*) + let callers: G.CSet.t = G.CMap.find (context_e) gmap in G.CSet.iter (fun to_call -> iter_call new_path_visited_calls to_call ) callers; @@ -1756,14 +1755,13 @@ struct end in try - let gmap_opt = G.base2 (ctx.global (v)) in - let gmap = Option.get (gmap_opt) in - (*let c = Option.get(G.CMap.PMap.keys gmap) in *)(*Todo: the context should be the domain of the map*) - G.CMap.iter(fun key value -> - let call = (v', key) in - iter_call LS.empty call - ) gmap (* try all fundec + context pairs that are in the map *) - with Invalid_argument _ -> () + let gmap_opt = G.base2 (ctx.global (v)) in + let gmap = Option.get (gmap_opt) in + G.CMap.iter(fun key value -> + let call = (v', key) in + iter_call LS.empty call + ) gmap (* try all fundec + context pairs that are in the map *) + with Invalid_argument _ -> () let checkTerminating ctx v v' = (*Check if the loops terminated*) @@ -1773,11 +1771,8 @@ struct [ (Pretty.dprintf "The program might not terminate! (Loop analysis)\n", Some (M.Location.CilLocation locUnknown)); ] in - M.msg_group Warning ~category:NonTerminating "Possibly non terminating loops" msgs); - printf "true" + M.msg_group Warning ~category:NonTerminating "Possibly non terminating loops" msgs) - - (*TODO: We may need to add new queries here*) let query ctx (type a) (q: a Queries.t): a Queries.result = match q with | WarnGlobal v -> @@ -1795,7 +1790,6 @@ struct | `Right v -> Queries.Result.top q end - | MustTermProgWithRec -> false (*TODO*) | _ -> S.query (conv ctx) q let branch ctx = S.branch (conv ctx) From 3c9da8d3f7952b67882c0191b022aafde52ef9b8 Mon Sep 17 00:00:00 2001 From: Isidor Zweckstetter Date: Mon, 19 Jun 2023 17:55:21 +0200 Subject: [PATCH 0166/1312] position of special function changed from after the loop to right after the loopHead --- src/util/terminationPreprocessing.ml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index 409aa2c2c4..3453aa7110 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -33,13 +33,13 @@ class loopCounterVisitor lc lg le (fd : fundec) = object(self) let v = (Cil.makeLocalVar fd name typ) in (*Not tested for incremental mode*) let init_stmt = mkStmtOneInstr @@ Set (var v, zero, loc, eloc) in let inc_stmt = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in - let exit_stmt = mkStmtOneInstr @@ Set ((var !le), (Lval (var v)), loc, eloc) in + let check_stmt = mkStmtOneInstr @@ Set ((var !le), (Lval (var v)), loc, eloc) in (match b.bstmts with | cont :: cond :: ss -> - b.bstmts <- cont :: inc_stmt :: cond :: ss; (*cont :: cond :: inc_stmt :: ss = it is also possible, but for loops with cond at the end, inc is also at the end*) + b.bstmts <- cont :: inc_stmt :: check_stmt :: cond :: ss; (*cont :: cond :: inc_stmt :: ss = it is also possible, but for loops with cond at the end, inc is also at the end*) | _ -> ()); lc := VarToStmt.add (v: varinfo) (s: stmt) !lc; - let nb = mkBlock [init_stmt; mkStmt s.skind; exit_stmt] in + let nb = mkBlock [init_stmt; mkStmt s.skind] in s.skind <- Block nb; s | Goto (sref, l) -> From 400c8102b9da2c890058a7b47a81afdf9939870c Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Mon, 19 Jun 2023 17:57:35 +0200 Subject: [PATCH 0167/1312] indentation --- src/framework/analyses.ml | 9 ++------- src/framework/constraints.ml | 23 ++++++++--------------- 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index e11903ba06..36b80567aa 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -42,7 +42,6 @@ struct let var_id = Node.show_id end - module VarF (LD: Printable.S) = struct type t = Node.t * LD.t [@@deriving eq, ord, hash] @@ -119,7 +118,6 @@ struct | x -> BatPrintf.fprintf f "%a" printXml x end - (* Tuple of fundec and S.C*) module T (Base1: Printable.S) (Base2: Printable.S) = struct @@ -149,17 +147,14 @@ struct else if (comp_a < 0) then res := !(res) - 1; if (comp_b > 0) then res := !(res) + 3 else if (comp_b < 0) then res := !(res) - 3; - !res) - + !res) let pretty () x = text (show x) - let hash (a,b) = Hashtbl.hash (Base1.hash a * Base2.hash b) (*Todo: is this ok?*) - + let hash (a,b) = Hashtbl.hash (Base1.hash a * Base2.hash b) (*Todo: is this ok?*) end module GVarGSet (G: Lattice.S) (C: Printable.S) (Base: Printable.S) = - struct module CSet = struct diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 730f0f41eb..9508aa28a2 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1711,9 +1711,8 @@ struct struct include GVarF(S.V) end - module G = GVarGSet (S.G) (S.C) (T (CilType.Fundec) (S.C)) - + let name () = "RecursionTerm (" ^ S.name () ^ ")" let conv (ctx: (_, G.t, _, V.t) ctx): (_, S.G.t, _, S.V.t) ctx = @@ -1727,11 +1726,9 @@ struct let module LS = Set.Make (T (CilType.Fundec) (S.C)) in (* find all cycles/SCCs *) let global_visited_calls = LH.create 100 in - (* DFS *) let rec iter_call (path_visited_calls: LS.t) (call:T (CilType.Fundec) (S.C).t) = let ((fundec_e:fundec), (context_e: C.t)) = call in (*unpack tuple for later use*) - if LS.mem call path_visited_calls then ( (*Cycle found*) let msgs = @@ -1743,7 +1740,6 @@ struct try LH.replace global_visited_calls call (); let new_path_visited_calls = LS.add call path_visited_calls in - let fundec_e_typeV: V.t = V.relift (`Right fundec_e) in let gmap_opt = G.base2 (ctx.global (fundec_e_typeV)) in let gmap = Option.get (gmap_opt) in (*might be empty*) @@ -1755,13 +1751,13 @@ struct end in try - let gmap_opt = G.base2 (ctx.global (v)) in - let gmap = Option.get (gmap_opt) in - G.CMap.iter(fun key value -> - let call = (v', key) in - iter_call LS.empty call - ) gmap (* try all fundec + context pairs that are in the map *) - with Invalid_argument _ -> () + let gmap_opt = G.base2 (ctx.global (v)) in + let gmap = Option.get (gmap_opt) in + G.CMap.iter(fun key value -> + let call = (v', key) in + iter_call LS.empty call + ) gmap (* try all fundec + context pairs that are in the map *) + with Invalid_argument _ -> () let checkTerminating ctx v v' = (*Check if the loops terminated*) @@ -1814,10 +1810,8 @@ struct let fd_r : fundec = Node.find_fundec nodeF in (*Caller fundec*) let c_e: S.C.t = Option.get (fc) in (*Callee context*) let fd_e : fundec = f in (*Callee fundec*) - let tup: (fundec * S.C.t) = (fd_r, c_r) in let t = G.CSet.singleton (tup) in - side_context ctx.sideg fd_e (c_e) t; S.combine_env (conv ctx) r fe f args fc es f_ask else @@ -1832,7 +1826,6 @@ struct let event ctx e octx = S.event (conv ctx) e (conv octx) end - module CompareGlobSys (SpecSys: SpecSys) = struct open SpecSys From 91d3c79acdd9d23834012cf59f093d0e4e3f4884 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Tue, 20 Jun 2023 09:57:26 +0200 Subject: [PATCH 0168/1312] indentation --- src/framework/analyses.ml | 5 ++++- src/framework/constraints.ml | 34 ++++++++++++++++++---------------- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 36b80567aa..fd56c65c2b 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -42,6 +42,7 @@ struct let var_id = Node.show_id end + module VarF (LD: Printable.S) = struct type t = Node.t * LD.t [@@deriving eq, ord, hash] @@ -118,6 +119,7 @@ struct | x -> BatPrintf.fprintf f "%a" printXml x end + (* Tuple of fundec and S.C*) module T (Base1: Printable.S) (Base2: Printable.S) = struct @@ -151,9 +153,10 @@ struct let pretty () x = text (show x) - let hash (a,b) = Hashtbl.hash (Base1.hash a * Base2.hash b) (*Todo: is this ok?*) + let hash (a,b) = Hashtbl.hash (Base1.hash a * Base2.hash b) (*Todo: is this ok?*) end + module GVarGSet (G: Lattice.S) (C: Printable.S) (Base: Printable.S) = struct module CSet = diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 9508aa28a2..2db480de86 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1711,6 +1711,7 @@ struct struct include GVarF(S.V) end + module G = GVarGSet (S.G) (S.C) (T (CilType.Fundec) (S.C)) let name () = "RecursionTerm (" ^ S.name () ^ ")" @@ -1722,15 +1723,15 @@ struct } let cycleDetection ctx v v' = - let module LH = Hashtbl.Make (T (CilType.Fundec) (S.C)) in - let module LS = Set.Make (T (CilType.Fundec) (S.C)) in + let module LH = Hashtbl.Make (T (CilType.Fundec) (S.C)) in + let module LS = Set.Make (T (CilType.Fundec) (S.C)) in (* find all cycles/SCCs *) - let global_visited_calls = LH.create 100 in + let global_visited_calls = LH.create 100 in (* DFS *) let rec iter_call (path_visited_calls: LS.t) (call:T (CilType.Fundec) (S.C).t) = let ((fundec_e:fundec), (context_e: C.t)) = call in (*unpack tuple for later use*) if LS.mem call path_visited_calls then ( - (*Cycle found*) + (*Cycle found*) let msgs = [ (Pretty.dprintf "The program might not terminate! (Fundec %a is contained in a call graph cycle)\n" CilType.Fundec.pretty fundec_e, Some (M.Location.CilLocation locUnknown)); @@ -1739,11 +1740,11 @@ struct else if not (LH.mem global_visited_calls call) then begin try LH.replace global_visited_calls call (); - let new_path_visited_calls = LS.add call path_visited_calls in - let fundec_e_typeV: V.t = V.relift (`Right fundec_e) in - let gmap_opt = G.base2 (ctx.global (fundec_e_typeV)) in + let new_path_visited_calls = LS.add call path_visited_calls in + let fundec_e_typeV: V.t = V.relift (`Right fundec_e) in + let gmap_opt = G.base2 (ctx.global (fundec_e_typeV)) in let gmap = Option.get (gmap_opt) in (*might be empty*) - let callers: G.CSet.t = G.CMap.find (context_e) gmap in + let callers: G.CSet.t = G.CMap.find (context_e) gmap in G.CSet.iter (fun to_call -> iter_call new_path_visited_calls to_call ) callers; @@ -1751,19 +1752,19 @@ struct end in try - let gmap_opt = G.base2 (ctx.global (v)) in - let gmap = Option.get (gmap_opt) in + let gmap_opt = G.base2 (ctx.global (v)) in + let gmap = Option.get (gmap_opt) in G.CMap.iter(fun key value -> - let call = (v', key) in - iter_call LS.empty call - ) gmap (* try all fundec + context pairs that are in the map *) + let call = (v', key) in + iter_call LS.empty call + ) gmap (* try all fundec + context pairs that are in the map *) with Invalid_argument _ -> () let checkTerminating ctx v v' = (*Check if the loops terminated*) if ctx.ask Queries.MustTermProg then (cycleDetection ctx v v') - else(let msgs = + else (let msgs = [ (Pretty.dprintf "The program might not terminate! (Loop analysis)\n", Some (M.Location.CilLocation locUnknown)); ] in @@ -1808,10 +1809,10 @@ struct let c_r: S.C.t = ctx.context () in (*Caller context*) let nodeF = ctx.node in let fd_r : fundec = Node.find_fundec nodeF in (*Caller fundec*) - let c_e: S.C.t = Option.get (fc) in (*Callee context*) + let c_e: S.C.t = Option.get fc in (*Callee context*) let fd_e : fundec = f in (*Callee fundec*) let tup: (fundec * S.C.t) = (fd_r, c_r) in - let t = G.CSet.singleton (tup) in + let t = G.CSet.singleton (tup) in side_context ctx.sideg fd_e (c_e) t; S.combine_env (conv ctx) r fe f args fc es f_ask else @@ -1826,6 +1827,7 @@ struct let event ctx e octx = S.event (conv ctx) e (conv octx) end + module CompareGlobSys (SpecSys: SpecSys) = struct open SpecSys From c3a96e4c94181a95da6641dc39191af7b6bed439 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Tue, 20 Jun 2023 10:07:36 +0200 Subject: [PATCH 0169/1312] indentation --- src/framework/analyses.ml | 4 ---- src/framework/constraints.ml | 5 +++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index fd56c65c2b..f3c76d554b 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -42,7 +42,6 @@ struct let var_id = Node.show_id end - module VarF (LD: Printable.S) = struct type t = Node.t * LD.t [@@deriving eq, ord, hash] @@ -119,7 +118,6 @@ struct | x -> BatPrintf.fprintf f "%a" printXml x end - (* Tuple of fundec and S.C*) module T (Base1: Printable.S) (Base2: Printable.S) = struct @@ -152,11 +150,9 @@ struct !res) let pretty () x = text (show x) - let hash (a,b) = Hashtbl.hash (Base1.hash a * Base2.hash b) (*Todo: is this ok?*) end - module GVarGSet (G: Lattice.S) (C: Printable.S) (Base: Printable.S) = struct module CSet = diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 2db480de86..66b2c92614 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1711,7 +1711,7 @@ struct struct include GVarF(S.V) end - + module G = GVarGSet (S.G) (S.C) (T (CilType.Fundec) (S.C)) let name () = "RecursionTerm (" ^ S.name () ^ ")" @@ -1727,10 +1727,12 @@ struct let module LS = Set.Make (T (CilType.Fundec) (S.C)) in (* find all cycles/SCCs *) let global_visited_calls = LH.create 100 in + (* DFS *) let rec iter_call (path_visited_calls: LS.t) (call:T (CilType.Fundec) (S.C).t) = let ((fundec_e:fundec), (context_e: C.t)) = call in (*unpack tuple for later use*) if LS.mem call path_visited_calls then ( + (*Cycle found*) let msgs = [ @@ -1827,7 +1829,6 @@ struct let event ctx e octx = S.event (conv ctx) e (conv octx) end - module CompareGlobSys (SpecSys: SpecSys) = struct open SpecSys From 7956342e1de1b117725db23c278840dcf39afecb Mon Sep 17 00:00:00 2001 From: Isidor Zweckstetter Date: Tue, 20 Jun 2023 10:17:02 +0200 Subject: [PATCH 0170/1312] fixed indentation --- src/util/terminationPreprocessing.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index 3453aa7110..45794b56cc 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -36,7 +36,7 @@ class loopCounterVisitor lc lg le (fd : fundec) = object(self) let check_stmt = mkStmtOneInstr @@ Set ((var !le), (Lval (var v)), loc, eloc) in (match b.bstmts with | cont :: cond :: ss -> - b.bstmts <- cont :: inc_stmt :: check_stmt :: cond :: ss; (*cont :: cond :: inc_stmt :: ss = it is also possible, but for loops with cond at the end, inc is also at the end*) + b.bstmts <- cont :: inc_stmt :: check_stmt :: cond :: ss; (*cont :: cond :: inc_stmt :: ss = it is also possible, but for loops with cond at the end, inc is also at the end*) | _ -> ()); lc := VarToStmt.add (v: varinfo) (s: stmt) !lc; let nb = mkBlock [init_stmt; mkStmt s.skind] in From 7191fabc0f4cfdb888b77ba3782100e65d7f3481 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Tue, 20 Jun 2023 10:30:34 +0200 Subject: [PATCH 0171/1312] made the compare function more ocamalyy :) --- src/framework/analyses.ml | 17 +++++++---------- src/framework/constraints.ml | 10 ++++++---- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index f3c76d554b..5a38af3e15 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -139,15 +139,12 @@ struct \n" Base1.printXml a Base2.printXml b let compare (a1,b1) (a2,b2) = (*Todo: is this ok?*) - if equal (a1, b1) (a2, b2) then 0 else( - let res = ref 0 in - let comp_a = Base1.compare a1 a2 in - let comp_b = Base2.compare b1 b2 in - if (comp_a > 0) then res := !(res) + 1 - else if (comp_a < 0) then res := !(res) - 1; - if (comp_b > 0) then res := !(res) + 3 - else if (comp_b < 0) then res := !(res) - 3; - !res) + if equal (a1, b1) (a2, b2) then 0 + else( + let val_a a = if (a > 0) then 1 else -1 in + let val_b b = if (b > 0) then 3 else -3 in + val_a (Base1.compare a1 a2) + val_b (Base2.compare b1 b2) + ) let pretty () x = text (show x) let hash (a,b) = Hashtbl.hash (Base1.hash a * Base2.hash b) (*Todo: is this ok?*) @@ -170,7 +167,7 @@ struct end (* Make the given module Goupable*) - module C_Printable (C: Printable.S)= + module C_Printable (C: Printable.S) = struct include C include Printable.Std (* To make it Groupable *) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 66b2c92614..eff98c1a8f 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1700,9 +1700,11 @@ module RecursionTermLifter (S: Spec) : Spec with module D = S.D and module C = S.C = -(*global invariant +(*global invariants: + - V -> G - fundec -> Map (S.C) (Set (fundec * S.C)) - So: g -> {c' -> f, c} + Therefore: + g -> {c' -> {(f, c)}} in case f, c --> g, c' *) struct @@ -1732,7 +1734,7 @@ struct let rec iter_call (path_visited_calls: LS.t) (call:T (CilType.Fundec) (S.C).t) = let ((fundec_e:fundec), (context_e: C.t)) = call in (*unpack tuple for later use*) if LS.mem call path_visited_calls then ( - + (*Cycle found*) let msgs = [ @@ -1760,7 +1762,7 @@ struct let call = (v', key) in iter_call LS.empty call ) gmap (* try all fundec + context pairs that are in the map *) - with Invalid_argument _ -> () + with Invalid_argument _ -> () (* path ended: no cycle*) let checkTerminating ctx v v' = (*Check if the loops terminated*) From 2df57d36e07495029d865d6264aa93855705d8ad Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Tue, 20 Jun 2023 11:47:20 +0200 Subject: [PATCH 0172/1312] fixed testcase --- runningGob.sh | 2 +- src/framework/constraints.ml | 14 +++++++------- .../regression/55-loop-unrolling/01-simple-cases.c | 3 --- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/runningGob.sh b/runningGob.sh index 783ad90fec..fcb5417192 100755 --- a/runningGob.sh +++ b/runningGob.sh @@ -21,6 +21,6 @@ cfile_goto="tests/incremental/02-cfg-comparison/01-added-return-stmt.c" ./goblint $cfile_loops $options_term --html # set up server to see visualizatino -python3 -m http.server --directory result 8081 +python3 -m http.server --directory result 8080 #./goblint --enable dbg.debug tests/regression/55-loop-unrolling/01-simple-cases.c --enable ana.int.interval --set "ana.activated[+]" signs --enable justcil > output.txt diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index eff98c1a8f..53fe3e5772 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1766,13 +1766,13 @@ struct let checkTerminating ctx v v' = (*Check if the loops terminated*) - if ctx.ask Queries.MustTermProg - then (cycleDetection ctx v v') - else (let msgs = - [ - (Pretty.dprintf "The program might not terminate! (Loop analysis)\n", Some (M.Location.CilLocation locUnknown)); - ] in - M.msg_group Warning ~category:NonTerminating "Possibly non terminating loops" msgs) + if ctx.ask Queries.MustTermProg + then (cycleDetection ctx v v') + else (let msgs = + [ + (Pretty.dprintf "The program might not terminate! (Loop analysis)\n", Some (M.Location.CilLocation locUnknown)); + ] in + M.msg_group Warning ~category:NonTerminating "Possibly non terminating loops" msgs) let query ctx (type a) (q: a Queries.t): a Queries.result = match q with diff --git a/tests/regression/55-loop-unrolling/01-simple-cases.c b/tests/regression/55-loop-unrolling/01-simple-cases.c index 6790add384..0073717187 100644 --- a/tests/regression/55-loop-unrolling/01-simple-cases.c +++ b/tests/regression/55-loop-unrolling/01-simple-cases.c @@ -31,8 +31,6 @@ void example1(void) __goblint_check(a[0] == 0); // UNKNOWN __goblint_check(a[3] == 3); // UNKNOWN - - example2(); } // Do-while loop simple example @@ -48,7 +46,6 @@ void example2(void) __goblint_check(a[0] == 0); // UNKNOWN __goblint_check(a[3] == 3); // UNKNOWN - example1(); } // Initialization not completed, yet the array representation is not precise From 8ef89ec34bbe55781c37c7d966bc90abb76284e0 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Tue, 20 Jun 2023 12:42:04 +0200 Subject: [PATCH 0173/1312] small changes, changed concrete name and indentation --- src/framework/constraints.ml | 12 ++++++------ src/util/options.schema.json | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 53fe3e5772..48dab4e84c 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1767,12 +1767,12 @@ struct let checkTerminating ctx v v' = (*Check if the loops terminated*) if ctx.ask Queries.MustTermProg - then (cycleDetection ctx v v') - else (let msgs = - [ - (Pretty.dprintf "The program might not terminate! (Loop analysis)\n", Some (M.Location.CilLocation locUnknown)); - ] in - M.msg_group Warning ~category:NonTerminating "Possibly non terminating loops" msgs) + then (cycleDetection ctx v v') + else (let msgs = + [ + (Pretty.dprintf "The program might not terminate! (Loop analysis)\n", Some (M.Location.CilLocation locUnknown)); + ] in + M.msg_group Warning ~category:NonTerminating "Possibly non terminating loops" msgs) let query ctx (type a) (q: a Queries.t): a Queries.result = match q with diff --git a/src/util/options.schema.json b/src/util/options.schema.json index d49b30aca7..3efaef2e83 100644 --- a/src/util/options.schema.json +++ b/src/util/options.schema.json @@ -2080,7 +2080,7 @@ }, "nonTerminating": { "title": "warn.nonTerminating", - "description": "nonTerminating warning", + "description": "Non Termination warning", "type": "boolean", "default": true }, From bdf116d466db245f7021eaf312df9392e8629adb Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 20 Jun 2023 15:56:12 +0300 Subject: [PATCH 0174/1312] Handle global variables in outer_memo Co-authored-by: Simmo Saan --- src/analyses/raceAnalysis.ml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index f2d2554abc..18426ebc4f 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -106,10 +106,11 @@ struct side_vars ctx memo let outer_memo ((root, offset) : Access.Memo.t) : Access.Memo.t option = - match offset with - | `NoOffset -> None - | `Field (f, offset') -> Some (`Type f.ftype, offset') - | `Index ((), offset') -> None (* TODO *) + match root, offset with + | `Var v, _ -> Some (`Type v.vtype, offset) (* TODO: Alloc variables void type *) + | _, `NoOffset -> None + | _, `Field (f, offset') -> Some (`Type f.ftype, offset') + | _, `Index ((), offset') -> None (* TODO *) let rec distribute_outer ctx ((root, offset) : Access.Memo.t) : Access.AS.t = let trie = G.access (ctx.global (V.access root)) in From 4ea84b275f2246fc82c9192be9c4e52013ad0ec6 Mon Sep 17 00:00:00 2001 From: Isidor Zweckstetter Date: Tue, 20 Jun 2023 15:27:27 +0200 Subject: [PATCH 0175/1312] test shouldn't fail --- output.txt | 2235 ------------------------------------------------- runningGob.sh | 2 +- 2 files changed, 1 insertion(+), 2236 deletions(-) diff --git a/output.txt b/output.txt index d751966836..e69de29bb2 100644 --- a/output.txt +++ b/output.txt @@ -1,2235 +0,0 @@ -/* Generated by CIL v. 2.0.1-48-g4df989f */ -/* print_CIL_Input is true */ - -#line 31 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned char __u_char; -#line 32 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned short __u_short; -#line 33 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned int __u_int; -#line 34 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __u_long; -#line 37 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef signed char __int8_t; -#line 38 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned char __uint8_t; -#line 39 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef short __int16_t; -#line 40 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned short __uint16_t; -#line 41 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef int __int32_t; -#line 42 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned int __uint32_t; -#line 44 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __int64_t; -#line 45 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __uint64_t; -#line 52 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef __int8_t __int_least8_t; -#line 53 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef __uint8_t __uint_least8_t; -#line 54 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef __int16_t __int_least16_t; -#line 55 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef __uint16_t __uint_least16_t; -#line 56 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef __int32_t __int_least32_t; -#line 57 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef __uint32_t __uint_least32_t; -#line 58 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef __int64_t __int_least64_t; -#line 59 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef __uint64_t __uint_least64_t; -#line 63 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __quad_t; -#line 64 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __u_quad_t; -#line 72 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __intmax_t; -#line 73 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __uintmax_t; -#line 145 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __dev_t; -#line 146 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned int __uid_t; -#line 147 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned int __gid_t; -#line 148 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __ino_t; -#line 149 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __ino64_t; -#line 150 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned int __mode_t; -#line 151 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __nlink_t; -#line 152 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __off_t; -#line 153 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __off64_t; -#line 154 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef int __pid_t; -#line 155 "/usr/include/x86_64-linux-gnu/bits/types.h" -struct __anonstruct___fsid_t_109580352 { - int __val[2] ; -}; -#line 155 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef struct __anonstruct___fsid_t_109580352 __fsid_t; -#line 156 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __clock_t; -#line 157 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __rlim_t; -#line 158 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __rlim64_t; -#line 159 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned int __id_t; -#line 160 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __time_t; -#line 161 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned int __useconds_t; -#line 162 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __suseconds_t; -#line 163 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __suseconds64_t; -#line 165 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef int __daddr_t; -#line 166 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef int __key_t; -#line 169 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef int __clockid_t; -#line 172 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef void *__timer_t; -#line 175 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __blksize_t; -#line 180 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __blkcnt_t; -#line 181 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __blkcnt64_t; -#line 184 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __fsblkcnt_t; -#line 185 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __fsblkcnt64_t; -#line 188 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __fsfilcnt_t; -#line 189 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __fsfilcnt64_t; -#line 192 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __fsword_t; -#line 194 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __ssize_t; -#line 197 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __syscall_slong_t; -#line 199 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __syscall_ulong_t; -#line 203 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef __off64_t __loff_t; -#line 204 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef char *__caddr_t; -#line 207 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __intptr_t; -#line 210 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned int __socklen_t; -#line 215 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef int __sig_atomic_t; -#line 209 "/usr/lib/gcc/x86_64-linux-gnu/10/include/stddef.h" -typedef unsigned long size_t; -#line 10 "/usr/include/x86_64-linux-gnu/bits/types/time_t.h" -typedef __time_t time_t; -#line 11 "/usr/include/x86_64-linux-gnu/bits/types/struct_timespec.h" -struct timespec { - __time_t tv_sec ; - __syscall_slong_t tv_nsec ; -}; -#line 38 "/usr/include/sched.h" -typedef __pid_t pid_t; -#line 23 "/usr/include/x86_64-linux-gnu/bits/types/struct_sched_param.h" -struct sched_param { - int sched_priority ; -}; -#line 32 "/usr/include/x86_64-linux-gnu/bits/cpu-set.h" -typedef unsigned long __cpu_mask; -#line 39 "/usr/include/x86_64-linux-gnu/bits/cpu-set.h" -struct __anonstruct_cpu_set_t_826868708 { - __cpu_mask __bits[1024UL / (8UL * sizeof(__cpu_mask ))] ; -}; -#line 39 "/usr/include/x86_64-linux-gnu/bits/cpu-set.h" -typedef struct __anonstruct_cpu_set_t_826868708 cpu_set_t; -#line 7 "/usr/include/x86_64-linux-gnu/bits/types/clock_t.h" -typedef __clock_t clock_t; -#line 7 "/usr/include/x86_64-linux-gnu/bits/types/struct_tm.h" -struct tm { - int tm_sec ; - int tm_min ; - int tm_hour ; - int tm_mday ; - int tm_mon ; - int tm_year ; - int tm_wday ; - int tm_yday ; - int tm_isdst ; - long tm_gmtoff ; - char const *tm_zone ; -}; -#line 7 "/usr/include/x86_64-linux-gnu/bits/types/clockid_t.h" -typedef __clockid_t clockid_t; -#line 7 "/usr/include/x86_64-linux-gnu/bits/types/timer_t.h" -typedef __timer_t timer_t; -#line 8 "/usr/include/x86_64-linux-gnu/bits/types/struct_itimerspec.h" -struct itimerspec { - struct timespec it_interval ; - struct timespec it_value ; -}; -#line 49 "/usr/include/time.h" -struct sigevent ; -#line 27 "/usr/include/x86_64-linux-gnu/bits/types/__locale_t.h" -struct __locale_data ; -#line 27 "/usr/include/x86_64-linux-gnu/bits/types/__locale_t.h" -struct __locale_struct { - struct __locale_data *__locales[13] ; - unsigned short const *__ctype_b ; - int const *__ctype_tolower ; - int const *__ctype_toupper ; - char const *__names[13] ; -}; -#line 41 "/usr/include/x86_64-linux-gnu/bits/types/__locale_t.h" -typedef struct __locale_struct *__locale_t; -#line 24 "/usr/include/x86_64-linux-gnu/bits/types/locale_t.h" -typedef __locale_t locale_t; -#line 25 "/usr/include/x86_64-linux-gnu/bits/atomic_wide_counter.h" -struct __anonstruct___value32_817613185 { - unsigned int __low ; - unsigned int __high ; -}; -#line 25 "/usr/include/x86_64-linux-gnu/bits/atomic_wide_counter.h" -union __anonunion___atomic_wide_counter_1044835921 { - unsigned long long __value64 ; - struct __anonstruct___value32_817613185 __value32 ; -}; -#line 25 "/usr/include/x86_64-linux-gnu/bits/atomic_wide_counter.h" -typedef union __anonunion___atomic_wide_counter_1044835921 __atomic_wide_counter; -#line 51 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" -struct __pthread_internal_list { - struct __pthread_internal_list *__prev ; - struct __pthread_internal_list *__next ; -}; -#line 51 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" -typedef struct __pthread_internal_list __pthread_list_t; -#line 57 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" -struct __pthread_internal_slist { - struct __pthread_internal_slist *__next ; -}; -#line 57 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" -typedef struct __pthread_internal_slist __pthread_slist_t; -#line 22 "/usr/include/x86_64-linux-gnu/bits/struct_mutex.h" -struct __pthread_mutex_s { - int __lock ; - unsigned int __count ; - int __owner ; - unsigned int __nusers ; - int __kind ; - short __spins ; - short __elision ; - __pthread_list_t __list ; -}; -#line 23 "/usr/include/x86_64-linux-gnu/bits/struct_rwlock.h" -struct __pthread_rwlock_arch_t { - unsigned int __readers ; - unsigned int __writers ; - unsigned int __wrphase_futex ; - unsigned int __writers_futex ; - unsigned int __pad3 ; - unsigned int __pad4 ; - int __cur_writer ; - int __shared ; - signed char __rwelision ; - unsigned char __pad1[7] ; - unsigned long __pad2 ; - unsigned int __flags ; -}; -#line 94 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" -struct __pthread_cond_s { - __atomic_wide_counter __wseq ; - __atomic_wide_counter __g1_start ; - unsigned int __g_refs[2] ; - unsigned int __g_size[2] ; - unsigned int __g1_orig_size ; - unsigned int __wrefs ; - unsigned int __g_signals[2] ; -}; -#line 105 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" -typedef unsigned int __tss_t; -#line 106 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" -typedef unsigned long __thrd_t; -#line 108 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" -struct __anonstruct___once_flag_1044835922 { - int __data ; -}; -#line 108 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" -typedef struct __anonstruct___once_flag_1044835922 __once_flag; -#line 27 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -typedef unsigned long pthread_t; -#line 32 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -union __anonunion_pthread_mutexattr_t_488594144 { - char __size[4] ; - int __align ; -}; -#line 32 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -typedef union __anonunion_pthread_mutexattr_t_488594144 pthread_mutexattr_t; -#line 41 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -union __anonunion_pthread_condattr_t_488594145 { - char __size[4] ; - int __align ; -}; -#line 41 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -typedef union __anonunion_pthread_condattr_t_488594145 pthread_condattr_t; -#line 49 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -typedef unsigned int pthread_key_t; -#line 53 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -typedef int pthread_once_t; -#line 56 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -union pthread_attr_t { - char __size[56] ; - long __align ; -}; -#line 62 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -typedef union pthread_attr_t pthread_attr_t; -#line 67 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -union __anonunion_pthread_mutex_t_335460617 { - struct __pthread_mutex_s __data ; - char __size[40] ; - long __align ; -}; -#line 67 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -typedef union __anonunion_pthread_mutex_t_335460617 pthread_mutex_t; -#line 75 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -union __anonunion_pthread_cond_t_951761805 { - struct __pthread_cond_s __data ; - char __size[48] ; - long long __align ; -}; -#line 75 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -typedef union __anonunion_pthread_cond_t_951761805 pthread_cond_t; -#line 86 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -union __anonunion_pthread_rwlock_t_656928968 { - struct __pthread_rwlock_arch_t __data ; - char __size[56] ; - long __align ; -}; -#line 86 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -typedef union __anonunion_pthread_rwlock_t_656928968 pthread_rwlock_t; -#line 93 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -union __anonunion_pthread_rwlockattr_t_145707745 { - char __size[8] ; - long __align ; -}; -#line 93 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -typedef union __anonunion_pthread_rwlockattr_t_145707745 pthread_rwlockattr_t; -#line 103 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -typedef int volatile pthread_spinlock_t; -#line 108 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -union __anonunion_pthread_barrier_t_145707746 { - char __size[32] ; - long __align ; -}; -#line 108 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -typedef union __anonunion_pthread_barrier_t_145707746 pthread_barrier_t; -#line 114 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -union __anonunion_pthread_barrierattr_t_951761806 { - char __size[4] ; - int __align ; -}; -#line 114 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -typedef union __anonunion_pthread_barrierattr_t_951761806 pthread_barrierattr_t; -#line 31 "/usr/include/x86_64-linux-gnu/bits/setjmp.h" -typedef long __jmp_buf[8]; -#line 5 "/usr/include/x86_64-linux-gnu/bits/types/__sigset_t.h" -struct __anonstruct___sigset_t_764561023 { - unsigned long __val[1024UL / (8UL * sizeof(unsigned long ))] ; -}; -#line 5 "/usr/include/x86_64-linux-gnu/bits/types/__sigset_t.h" -typedef struct __anonstruct___sigset_t_764561023 __sigset_t; -#line 26 "/usr/include/x86_64-linux-gnu/bits/types/struct___jmp_buf_tag.h" -struct __jmp_buf_tag { - __jmp_buf __jmpbuf ; - int __mask_was_saved ; - __sigset_t __saved_mask ; -}; -#line 37 "/usr/include/pthread.h" -enum __anonenum_34415463 { - PTHREAD_CREATE_JOINABLE = 0, - PTHREAD_CREATE_DETACHED = 1 -} ; -#line 47 -enum __anonenum_508643754 { - PTHREAD_MUTEX_TIMED_NP = 0, - PTHREAD_MUTEX_RECURSIVE_NP = 1, - PTHREAD_MUTEX_ERRORCHECK_NP = 2, - PTHREAD_MUTEX_ADAPTIVE_NP = 3, - PTHREAD_MUTEX_NORMAL = 0, - PTHREAD_MUTEX_RECURSIVE = 1, - PTHREAD_MUTEX_ERRORCHECK = 2, - PTHREAD_MUTEX_DEFAULT = 0 -} ; -#line 69 -enum __anonenum_931900394 { - PTHREAD_MUTEX_STALLED = 0, - PTHREAD_MUTEX_STALLED_NP = 0, - PTHREAD_MUTEX_ROBUST = 1, - PTHREAD_MUTEX_ROBUST_NP = 1 -} ; -#line 81 -enum __anonenum_205214487 { - PTHREAD_PRIO_NONE = 0, - PTHREAD_PRIO_INHERIT = 1, - PTHREAD_PRIO_PROTECT = 2 -} ; -#line 104 -enum __anonenum_25043950 { - PTHREAD_RWLOCK_PREFER_READER_NP = 0, - PTHREAD_RWLOCK_PREFER_WRITER_NP = 1, - PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP = 2, - PTHREAD_RWLOCK_DEFAULT_NP = 0 -} ; -#line 124 -enum __anonenum_436439511 { - PTHREAD_INHERIT_SCHED = 0, - PTHREAD_EXPLICIT_SCHED = 1 -} ; -#line 134 -enum __anonenum_998661166 { - PTHREAD_SCOPE_SYSTEM = 0, - PTHREAD_SCOPE_PROCESS = 1 -} ; -#line 144 -enum __anonenum_146137331 { - PTHREAD_PROCESS_PRIVATE = 0, - PTHREAD_PROCESS_SHARED = 1 -} ; -#line 159 "/usr/include/pthread.h" -struct _pthread_cleanup_buffer { - void (*__routine)(void * ) ; - void *__arg ; - int __canceltype ; - struct _pthread_cleanup_buffer *__prev ; -}; -#line 168 -enum __anonenum_53396917 { - PTHREAD_CANCEL_ENABLE = 0, - PTHREAD_CANCEL_DISABLE = 1 -} ; -#line 175 -enum __anonenum_904563783 { - PTHREAD_CANCEL_DEFERRED = 0, - PTHREAD_CANCEL_ASYNCHRONOUS = 1 -} ; -#line 538 "/usr/include/pthread.h" -struct __cancel_jmp_buf_tag { - __jmp_buf __cancel_jmp_buf ; - int __mask_was_saved ; -}; -#line 544 "/usr/include/pthread.h" -struct __anonstruct___pthread_unwind_buf_t_530692248 { - struct __cancel_jmp_buf_tag __cancel_jmp_buf[1] ; - void *__pad[4] ; -}; -#line 544 "/usr/include/pthread.h" -typedef struct __anonstruct___pthread_unwind_buf_t_530692248 __attribute__((__aligned__)) __pthread_unwind_buf_t; -#line 557 "/usr/include/pthread.h" -struct __pthread_cleanup_frame { - void (*__cancel_routine)(void * ) ; - void *__cancel_arg ; - int __do_it ; - int __cancel_type ; -}; -#line 143 "/usr/lib/gcc/x86_64-linux-gnu/10/include/stddef.h" -typedef long ptrdiff_t; -#line 321 "/usr/lib/gcc/x86_64-linux-gnu/10/include/stddef.h" -typedef int wchar_t; -#line 415 "/usr/lib/gcc/x86_64-linux-gnu/10/include/stddef.h" -struct __anonstruct_max_align_t_896270833 { - long long __max_align_ll __attribute__((__aligned__(__alignof__(long long )))) ; - long double __max_align_ld __attribute__((__aligned__(__alignof__(long double )))) ; -}; -#line 415 "/usr/lib/gcc/x86_64-linux-gnu/10/include/stddef.h" -typedef struct __anonstruct_max_align_t_896270833 max_align_t; -/* compiler builtin: - void __builtin_va_copy(__builtin_va_list , __builtin_va_list ) ; */ -/* compiler builtin: - void *__builtin_frob_return_address(void * ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_and_and_fetch(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_fetch_and_or(...) ; */ -/* compiler builtin: - int __builtin_popcountll(unsigned long long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_or_fetch(...) ; */ -/* compiler builtin: - float __builtin_atanf(float ) ; */ -/* compiler builtin: - float __attribute__((____vector_size____(16))) __builtin_ia32_addps(float __attribute__((____vector_size____(16))) , - float __attribute__((____vector_size____(16))) ) ; */ -/* compiler builtin: - unsigned long __builtin_strcspn(char const * , char const * ) ; */ -/* compiler builtin: - float __builtin_asinf(float ) ; */ -/* compiler builtin: - float __attribute__((____vector_size____(16))) __builtin_ia32_maxps(float __attribute__((____vector_size____(16))) , - float __attribute__((____vector_size____(16))) ) ; */ -/* compiler builtin: - float __attribute__((____vector_size____(16))) __builtin_ia32_unpckhps(float __attribute__((____vector_size____(16))) , - float __attribute__((____vector_size____(16))) ) ; */ -/* compiler builtin: - double __builtin_acos(double ) ; */ -/* compiler builtin: - int __builtin___sprintf_chk(char * , int , unsigned long , char const * , ...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_nand_fetch_8(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_and(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_xor_fetch_16(...) ; */ -/* compiler builtin: - double __builtin_cosh(double ) ; */ -/* compiler builtin: - float __builtin_tanhf(float ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_nand_16(...) ; */ -/* compiler builtin: - void *__builtin_mempcpy(void * , void const * , unsigned long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_sub_fetch_1(...) ; */ -/* compiler builtin: - long double __builtin_sqrtl(long double ) ; */ -/* compiler builtin: - int __builtin_parity(unsigned int ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_or(...) ; */ -/* compiler builtin: - long double __builtin_coshl(long double ) ; */ -/* compiler builtin: - long double __builtin_cosl(long double ) ; */ -/* compiler builtin: - float __builtin_cosf(float ) ; */ -/* compiler builtin: - void __sync_synchronize(...) ; */ -/* compiler builtin: - long double __builtin_acosl(long double ) ; */ -/* compiler builtin: - void *__builtin___mempcpy_chk(void * , void const * , unsigned long , unsigned long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_or_and_fetch(...) ; */ -/* compiler builtin: - int __builtin_clz(unsigned int ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_nand_fetch_4(...) ; */ -/* compiler builtin: - double __builtin_log10(double ) ; */ -/* compiler builtin: - char *__builtin___strcat_chk(char * , char const * , unsigned long ) ; */ -/* compiler builtin: - float __builtin_modff(float , float * ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_sub_fetch_4(...) ; */ -/* compiler builtin: - _Bool __atomic_compare_exchange_n(...) ; */ -/* compiler builtin: - double __builtin_sin(double ) ; */ -/* compiler builtin: - double __builtin_frexp(double , int * ) ; */ -/* compiler builtin: - float __builtin_acosf(float ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_add_and_fetch(...) ; */ -/* compiler builtin: - long double __builtin_sinhl(long double ) ; */ -/* compiler builtin: - char *__builtin___stpcpy_chk(char * , char const * , unsigned long ) ; */ -/* compiler builtin: - void __atomic_signal_fence(int ) ; */ -/* compiler builtin: - double __builtin_fabs(double ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_nand_1(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_and_fetch_16(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_fetch_and_nand(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_xor_fetch_8(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_xor_fetch_2(...) ; */ -/* compiler builtin: - void __atomic_thread_fence(int ) ; */ -/* compiler builtin: - void __atomic_store_16(...) ; */ -/* compiler builtin: - void __builtin_va_start(__builtin_va_list ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_and_8(...) ; */ -/* compiler builtin: - short __builtin_bswap16(short ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_and_fetch_2(...) ; */ -/* compiler builtin: - _Bool __atomic_test_and_set(void * , int ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_add_8(...) ; */ -/* compiler builtin: - int __builtin_ctz(unsigned int ) ; */ -/* compiler builtin: - char *__builtin_strpbrk(char const * , char const * ) ; */ -/* compiler builtin: - char *__builtin_strcpy(char * , char const * ) ; */ -/* compiler builtin: - double __builtin_sqrt(double ) ; */ -/* compiler builtin: - __builtin_va_list __builtin_next_arg(void) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_nand(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_exchange_16(...) ; */ -/* compiler builtin: - void __atomic_clear(_Bool * , int ) ; */ -/* compiler builtin: - void __atomic_store(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_add_fetch_2(...) ; */ -/* compiler builtin: - float __builtin_log10f(float ) ; */ -/* compiler builtin: - long double __builtin_fabsl(long double ) ; */ -/* compiler builtin: - long double __builtin_floorl(long double ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_xor_1(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_and_fetch(...) ; */ -/* compiler builtin: - float __builtin_floorf(float ) ; */ -/* compiler builtin: - _Bool __atomic_compare_exchange_4(...) ; */ -/* compiler builtin: - void *__builtin_memcpy(void * , void const * , unsigned long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_sub_and_fetch(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_nand_and_fetch(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_load_16(...) ; */ -/* compiler builtin: - float __attribute__((____vector_size____(16))) __builtin_ia32_subps(float __attribute__((____vector_size____(16))) , - float __attribute__((____vector_size____(16))) ) ; */ -/* compiler builtin: - int __builtin_parityll(unsigned long long ) ; */ -/* compiler builtin: - void __builtin_va_end(__builtin_va_list ) ; */ -/* compiler builtin: - void __builtin_bzero(void * , unsigned long ) ; */ -/* compiler builtin: - _Bool __atomic_always_lock_free(unsigned long , void * ) ; */ -/* compiler builtin: - int __builtin_strncmp(char const * , char const * , unsigned long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_nand_fetch_16(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_xor_and_fetch(...) ; */ -/* compiler builtin: - int __builtin___vsprintf_chk(char * , int , unsigned long , char const * , - __builtin_va_list ) ; */ -/* compiler builtin: - float __builtin_sqrtf(float ) ; */ -/* compiler builtin: - double __builtin_nans(char const * ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_xor_8(...) ; */ -/* compiler builtin: - double __builtin_exp(double ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_exchange_8(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_exchange_1(...) ; */ -/* compiler builtin: - int __builtin_strcmp(char const * , char const * ) ; */ -/* compiler builtin: - float __builtin_ldexpf(float , int ) ; */ -/* compiler builtin: - float __builtin_powif(float , int ) ; */ -/* compiler builtin: - long double __builtin_log10l(long double ) ; */ -/* compiler builtin: - void *__builtin___memmove_chk(void * , void const * , unsigned long , unsigned long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_fetch_and_and(...) ; */ -/* compiler builtin: - void *__builtin_return_address(unsigned int ) ; */ -/* compiler builtin: - void __atomic_feraiseexcept(int ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_and_fetch_4(...) ; */ -/* compiler builtin: - float __builtin_fabsf(float ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_or_fetch_8(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_or_fetch_4(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_or_fetch_1(...) ; */ -/* compiler builtin: - unsigned long __builtin_object_size(void * , int ) ; */ -/* compiler builtin: - void *__builtin_alloca(unsigned long ) ; */ -/* compiler builtin: - int __builtin_va_arg_pack_len(void) ; */ -/* compiler builtin: - long double __builtin_tanl(long double ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_and_2(...) ; */ -/* compiler builtin: - void __sync_lock_release(...) ; */ -/* compiler builtin: - long double __builtin_modfl(long double , long double * ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_nand_8(...) ; */ -/* compiler builtin: - char *__builtin_stpcpy(char * , char const * ) ; */ -/* compiler builtin: - long double __builtin_sinl(long double ) ; */ -/* compiler builtin: - double __builtin_asin(double ) ; */ -/* compiler builtin: - float __builtin_sinhf(float ) ; */ -/* compiler builtin: - int __builtin_ctzl(unsigned long ) ; */ -/* compiler builtin: - long double __builtin_tanhl(long double ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_add(...) ; */ -/* compiler builtin: - long __builtin_bswap64(long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_nand_fetch_1(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_nand_2(...) ; */ -/* compiler builtin: - float __attribute__((____vector_size____(16))) __builtin_ia32_mulps(float __attribute__((____vector_size____(16))) , - float __attribute__((____vector_size____(16))) ) ; */ -/* compiler builtin: - double __builtin_tan(double ) ; */ -/* compiler builtin: - char *__builtin_strncpy(char * , char const * , unsigned long ) ; */ -/* compiler builtin: - float __builtin_inff(void) ; */ -/* compiler builtin: - void *__builtin___memset_chk(void * , int , unsigned long , unsigned long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_load_n(...) ; */ -/* compiler builtin: - double __builtin_huge_val(void) ; */ -/* compiler builtin: - int __builtin_clzl(unsigned long ) ; */ -/* compiler builtin: - _Bool __atomic_compare_exchange_16(...) ; */ -/* compiler builtin: - float __builtin_frexpf(float , int * ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_exchange_n(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_or_1(...) ; */ -/* compiler builtin: - long double __builtin_fmodl(long double ) ; */ -/* compiler builtin: - double __builtin_atan(double ) ; */ -/* compiler builtin: - int __builtin___fprintf_chk(void * , int , char const * , ...) ; */ -/* compiler builtin: - float __builtin_ceilf(float ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_or_8(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_add_1(...) ; */ -/* compiler builtin: - void __builtin_return(void const * ) ; */ -/* compiler builtin: - long double __builtin_asinl(long double ) ; */ -/* compiler builtin: - int __builtin_ffsll(unsigned long long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_sub_1(...) ; */ -/* compiler builtin: - int __builtin_va_arg_pack(void) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_or_4(...) ; */ -/* compiler builtin: - char *__builtin___strncpy_chk(char * , char const * , unsigned long , unsigned long ) ; */ -/* compiler builtin: - double __builtin_powi(double , int ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_load_2(...) ; */ -/* compiler builtin: - char *__builtin_strchr(char * , int ) ; */ -/* compiler builtin: - char *__builtin___strncat_chk(char * , char const * , unsigned long , unsigned long ) ; */ -/* compiler builtin: - void __atomic_store_2(...) ; */ -/* compiler builtin: - long double __builtin_huge_vall(void) ; */ -/* compiler builtin: - int __builtin_ffsl(unsigned long ) ; */ -/* compiler builtin: - int __builtin___vprintf_chk(int , char const * , __builtin_va_list ) ; */ -/* compiler builtin: - float __attribute__((____vector_size____(16))) __builtin_ia32_unpcklps(float __attribute__((____vector_size____(16))) , - float __attribute__((____vector_size____(16))) ) ; */ -/* compiler builtin: - char *__builtin_strncat(char * , char const * , unsigned long ) ; */ -/* compiler builtin: - int __builtin_ctzll(unsigned long long ) ; */ -/* compiler builtin: - void __builtin_stdarg_start(__builtin_va_list ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_exchange_4(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_fetch_and_xor(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_sub_4(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_and_4(...) ; */ -/* compiler builtin: - long double __builtin_frexpl(long double , int * ) ; */ -/* compiler builtin: - _Bool __atomic_compare_exchange(...) ; */ -/* compiler builtin: - float __builtin_tanf(float ) ; */ -/* compiler builtin: - long double __builtin_logl(long double ) ; */ -/* compiler builtin: - void __builtin_va_arg(__builtin_va_list , unsigned long , void * ) ; */ -/* compiler builtin: - long __builtin_expect(long , long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_load_1(...) ; */ -/* compiler builtin: - _Bool __atomic_compare_exchange_1(...) ; */ -/* compiler builtin: - int __builtin___printf_chk(int , char const * , ...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_xor_2(...) ; */ -/* compiler builtin: - int __builtin___vfprintf_chk(void * , int , char const * , __builtin_va_list ) ; */ -/* compiler builtin: - void __builtin_prefetch(void const * , ...) ; */ -/* compiler builtin: - long double __builtin_nansl(char const * ) ; */ -/* compiler builtin: - double __builtin_fmod(double ) ; */ -/* compiler builtin: - void __atomic_load(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_or_fetch_16(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_add_fetch_16(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_val_compare_and_swap(...) ; */ -/* compiler builtin: - void __atomic_store_4(...) ; */ -/* compiler builtin: - double __builtin_tanh(double ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_and_1(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_nand_4(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_add_16(...) ; */ -/* compiler builtin: - void __builtin_unreachable(void) ; */ -/* compiler builtin: - _Bool __atomic_compare_exchange_2(...) ; */ -/* compiler builtin: - long double __builtin_ldexpl(long double , int ) ; */ -/* compiler builtin: - void *__builtin_apply(void (*)() , void * , unsigned long ) ; */ -/* compiler builtin: - float __builtin_sinf(float ) ; */ -/* compiler builtin: - double __builtin_ceil(double ) ; */ -/* compiler builtin: - void __atomic_exchange(...) ; */ -/* compiler builtin: - long double __builtin_powil(long double , int ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_sub_fetch_8(...) ; */ -/* compiler builtin: - long double __builtin_expl(long double ) ; */ -/* compiler builtin: - int __builtin_constant_p(int ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_sub_16(...) ; */ -/* compiler builtin: - double __builtin_log(double ) ; */ -/* compiler builtin: - float __builtin_expf(float ) ; */ -/* compiler builtin: - int __builtin_types_compatible_p(unsigned long , unsigned long ) ; */ -/* compiler builtin: - long double __builtin_atan2l(long double , long double ) ; */ -/* compiler builtin: - void *__builtin_apply_args(void) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_exchange_2(...) ; */ -/* compiler builtin: - float __builtin_logf(float ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_sub_fetch_2(...) ; */ -/* compiler builtin: - unsigned long __builtin_strlen(char const * ) ; */ -/* compiler builtin: - int __builtin_ffs(unsigned int ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_xor_16(...) ; */ -/* compiler builtin: - double __builtin_inf(void) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_or_16(...) ; */ -/* compiler builtin: - void *__builtin___memcpy_chk(void * , void const * , unsigned long , unsigned long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_load_4(...) ; */ -/* compiler builtin: - void __atomic_store_n(...) ; */ -/* compiler builtin: - void __builtin_trap(void) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_add_4(...) ; */ -/* compiler builtin: - int __builtin_parityl(unsigned long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_nand_fetch_2(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_lock_test_and_set(...) ; */ -/* compiler builtin: - unsigned long __builtin_strspn(char const * , char const * ) ; */ -/* compiler builtin: - void __builtin_varargs_start(__builtin_va_list ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_sub_fetch(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_and_16(...) ; */ -/* compiler builtin: - _Bool __atomic_compare_exchange_8(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_nand_fetch(...) ; */ -/* compiler builtin: - double __builtin_nan(char const * ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_load_8(...) ; */ -/* compiler builtin: - int __builtin___snprintf_chk(char * , unsigned long , int , unsigned long , - char const * , ...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_xor_fetch_4(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_sub_2(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_add_fetch(...) ; */ -/* compiler builtin: - long double __builtin_atanl(long double ) ; */ -/* compiler builtin: - int __builtin_clzll(unsigned long long ) ; */ -/* compiler builtin: - float __builtin_huge_valf(void) ; */ -/* compiler builtin: - float __builtin_coshf(float ) ; */ -/* compiler builtin: - float __builtin_nansf(char const * ) ; */ -/* compiler builtin: - void __atomic_store_8(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_xor_fetch(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_fetch_and_add(...) ; */ -/* compiler builtin: - int __builtin___vsnprintf_chk(char * , unsigned long , int , unsigned long , - char const * , __builtin_va_list ) ; */ -/* compiler builtin: - float __builtin_nanf(char const * ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_or_fetch_2(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_add_fetch_1(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_xor(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_add_fetch_8(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_xor_4(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_sub_8(...) ; */ -/* compiler builtin: - _Bool __sync_bool_compare_and_swap(...) ; */ -/* compiler builtin: - double __builtin_atan2(double , double ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __builtin_tgmath(...) ; */ -/* compiler builtin: - int __builtin_popcountl(unsigned long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_and_fetch_1(...) ; */ -/* compiler builtin: - long double __builtin_ceill(long double ) ; */ -/* compiler builtin: - void __atomic_store_1(...) ; */ -/* compiler builtin: - char *__builtin___strcpy_chk(char * , char const * , unsigned long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_xor_fetch_1(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_or_2(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_sub_fetch_16(...) ; */ -/* compiler builtin: - double __builtin_floor(double ) ; */ -/* compiler builtin: - double __builtin_cos(double ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_fetch_and_sub(...) ; */ -/* compiler builtin: - void *__builtin_memset(void * , int , int ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_add_2(...) ; */ -/* compiler builtin: - long double __builtin_nanl(char const * ) ; */ -/* compiler builtin: - float __builtin_atan2f(float , float ) ; */ -/* compiler builtin: - _Bool __atomic_is_lock_free(unsigned long , void * ) ; */ -/* compiler builtin: - int __builtin_popcount(unsigned int ) ; */ -/* compiler builtin: - double __builtin_sinh(double ) ; */ -/* compiler builtin: - void __builtin_bcopy(void const * , void * , unsigned long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_and_fetch_8(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_sub(...) ; */ -/* compiler builtin: - void *__builtin_extract_return_addr(void * ) ; */ -/* compiler builtin: - int __builtin_bswap32(int ) ; */ -/* compiler builtin: - double __builtin_ldexp(double , int ) ; */ -/* compiler builtin: - long double __builtin_infl(void) ; */ -/* compiler builtin: - float __builtin_fmodf(float ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_add_fetch_4(...) ; */ -/* compiler builtin: - void *__builtin_frame_address(unsigned int ) ; */ -#line 1 "lib/goblint/runtime/include/goblint.h" -extern void __goblint_check(int exp ) ; -#line 2 -extern void __goblint_assume(int exp ) ; -#line 3 -extern void __goblint_assert(int exp ) ; -#line 5 -extern void __goblint_assume_join() ; -#line 7 -extern void __goblint_split_begin(int exp ) ; -#line 8 -extern void __goblint_split_end(int exp ) ; -#line 4 "tests/regression/55-loop-unrolling/01-simple-cases.c" -int global ; -#line 8 -void example1(void) ; -#line 9 -void example2(void) ; -#line 10 -void example3(void) ; -#line 11 -void example4(void) ; -#line 12 -void example5(void) ; -#line 13 -void example6(void) ; -#line 14 -void example7(void) ; -#line 15 -void example8(void) ; -#line 16 -void example9(void) ; -#line 17 -void example10(void) ; -#line 6 "tests/regression/55-loop-unrolling/01-simple-cases.c" -int main(void) -{ - - - { -#line 8 - example1(); -#line 9 - example2(); -#line 10 - example3(); -#line 11 - example4(); -#line 12 - example5(); -#line 13 - example6(); -#line 14 - example7(); -#line 15 - example8(); -#line 16 - example9(); -#line 17 - example10(); -#line 18 - return (0); -} -} -#line 22 "tests/regression/55-loop-unrolling/01-simple-cases.c" -void example1(void) -{ - int a[5] ; - int i ; - int term27_5-file_01-simple-cases ; - - { -#line 25 - i = 0; - { -#line 27 - term27_5-file_01-simple-cases = 0; - { -#line 27 - while (1) { - while_continue: /* CIL Label */ ; -#line 27 - if (! (i < 5)) { -#line 27 - goto while_break; - } -#line 27 - term27_5-file_01-simple-cases ++; -#line 28 - a[i] = i; -#line 29 - i ++; - } - while_break: /* CIL Label */ ; - } -#line 27 - term_exit- = term27_5-file_01-simple-cases; - } -#line 32 - __goblint_check(a[0] == 0); -#line 33 - __goblint_check(a[3] == 3); -#line 34 - return; -} -} -#line 37 "tests/regression/55-loop-unrolling/01-simple-cases.c" -void example2(void) -{ - int a[5] ; - int i ; - int term42_5-file_01-simple-cases ; - - { -#line 40 - i = 0; - { -#line 42 - term42_5-file_01-simple-cases = 0; - { -#line 42 - while (1) { - while_continue: /* CIL Label */ ; -#line 43 - a[i] = i; -#line 44 - i ++; -#line 42 - term42_5-file_01-simple-cases ++; -#line 42 - if (! (i <= 5)) { -#line 42 - goto while_break; - } - } - while_break: /* CIL Label */ ; - } -#line 42 - term_exit- = term42_5-file_01-simple-cases; - } -#line 47 - __goblint_check(a[0] == 0); -#line 48 - __goblint_check(a[3] == 3); -#line 49 - return; -} -} -#line 52 "tests/regression/55-loop-unrolling/01-simple-cases.c" -void example3(void) -{ - int a[10] ; - int i ; - int term57_5-file_01-simple-cases ; - - { -#line 55 - i = 0; - { -#line 57 - term57_5-file_01-simple-cases = 0; - { -#line 57 - while (1) { - while_continue: /* CIL Label */ ; -#line 57 - if (! (i < 5)) { -#line 57 - goto while_break; - } -#line 57 - term57_5-file_01-simple-cases ++; -#line 58 - a[i] = i; -#line 59 - i ++; - } - while_break: /* CIL Label */ ; - } -#line 57 - term_exit- = term57_5-file_01-simple-cases; - } -#line 62 - __goblint_check(a[0] == 0); -#line 63 - __goblint_check(a[3] == 0); -#line 64 - __goblint_check(a[7] == 0); -#line 65 - return; -} -} -#line 68 "tests/regression/55-loop-unrolling/01-simple-cases.c" -void example4(void) -{ - int a[10] ; - int i ; - int first_iteration ; - int term74_5-file_01-simple-cases ; - - { -#line 71 - i = 0; -#line 72 - first_iteration = 1; - { -#line 74 - term74_5-file_01-simple-cases = 0; - { -#line 74 - while (1) { - while_continue: /* CIL Label */ ; -#line 74 - if (! (i < 10)) { -#line 74 - goto while_break; - } -#line 74 - term74_5-file_01-simple-cases ++; -#line 75 - if (first_iteration == 1) { -#line 75 - __goblint_check(i == 0); - } else -#line 76 - if (i > 5) { -#line 76 - __goblint_check(i == 6); - } -#line 77 - first_iteration = 0; -#line 78 - a[i] = 0; -#line 79 - i ++; - } - while_break: /* CIL Label */ ; - } -#line 74 - term_exit- = term74_5-file_01-simple-cases; - } -#line 82 - __goblint_check(a[0] == 0); -#line 83 - __goblint_check(first_iteration == 0); -#line 84 - return; -} -} -#line 89 "tests/regression/55-loop-unrolling/01-simple-cases.c" -void example5(void) -{ - int a[4] ; - int i ; - int top ; - int term95_5-file_01-simple-cases ; - - { -#line 92 - i = 0; -#line 93 - top = 0; - { -#line 95 - term95_5-file_01-simple-cases = 0; - { -#line 95 - while (1) { - while_continue: /* CIL Label */ ; -#line 95 - if (! (i < 4)) { -#line 95 - goto while_break; - } -#line 95 - term95_5-file_01-simple-cases ++; -#line 96 - a[i] = 0; -#line 97 - top += i; -#line 98 - if (i == 2) { -#line 99 - __goblint_check(top == 3); - } else { -#line 102 - __goblint_check(top == 3); - } -#line 104 - i ++; - } - while_break: /* CIL Label */ ; - } -#line 95 - term_exit- = term95_5-file_01-simple-cases; - } -#line 107 - __goblint_check(a[0] == 0); -#line 108 - __goblint_check(a[3] == 0); -#line 109 - __goblint_check(top == 6); -#line 110 - return; -} -} -#line 113 "tests/regression/55-loop-unrolling/01-simple-cases.c" -void example6(void) -{ - int a[5] ; - int i ; - int top ; - int term119_5-file_01-simple-cases ; - - { -#line 116 - i = 0; -#line 117 - top = 0; - { -#line 119 - term119_5-file_01-simple-cases = 0; - { -#line 119 - while (1) { - while_continue: /* CIL Label */ ; -#line 119 - if (! (i < 3)) { -#line 119 - goto while_break; - } -#line 119 - term119_5-file_01-simple-cases ++; -#line 120 - a[i] = 0; -#line 121 - __goblint_check(a[0] == 0); -#line 122 - i ++; - } - while_break: /* CIL Label */ ; - } -#line 119 - term_exit- = term119_5-file_01-simple-cases; - } -#line 125 - __goblint_check(a[0] == 0); -#line 126 - __goblint_check(a[3] == 0); -#line 127 - __goblint_check(top == 6); -#line 128 - return; -} -} -#line 131 "tests/regression/55-loop-unrolling/01-simple-cases.c" -int update(int i ) -{ - - - { -#line 132 - if (i > 5) { -#line 133 - return (0); - } else { -#line 136 - return (1); - } -} -} -#line 139 "tests/regression/55-loop-unrolling/01-simple-cases.c" -void example7(void) -{ - int a[10] ; - int i ; - int tmp ; - int term143_2-file_01-simple-cases ; - - { -#line 142 - i = 0; - { -#line 143 - term143_2-file_01-simple-cases = 0; - { -#line 143 - while (1) { - while_continue: /* CIL Label */ ; -#line 143 - tmp = update(i); -#line 143 - term143_2-file_01-simple-cases ++; -#line 143 - if (! tmp) { -#line 143 - goto while_break; - } -#line 144 - a[i] = i; -#line 145 - i ++; - } - while_break: /* CIL Label */ ; - } -#line 143 - term_exit- = term143_2-file_01-simple-cases; - } -#line 147 - __goblint_check(a[0] == 0); -#line 148 - __goblint_check(a[6] == 0); -#line 149 - return; -} -} -#line 152 "tests/regression/55-loop-unrolling/01-simple-cases.c" -void example8(void) -{ - int a[5] ; - int b[5] ; - int i ; - int j ; - int term160_9-file_01-simple-cases ; - int term157_2-file_01-simple-cases ; - - { -#line 155 - b[0] = 0; -#line 155 - b[1] = 0; -#line 155 - b[2] = 0; -#line 155 - b[3] = 0; -#line 155 - b[4] = 0; -#line 156 - i = 0; - { -#line 157 - term157_2-file_01-simple-cases = 0; - { -#line 157 - while (1) { - while_continue: /* CIL Label */ ; -#line 157 - if (! (i < 5)) { -#line 157 - goto while_break; - } -#line 157 - term157_2-file_01-simple-cases ++; -#line 158 - a[i] = i; -#line 159 - j = 0; - { -#line 160 - term160_9-file_01-simple-cases = 0; - { -#line 160 - while (1) { - while_continue___0: /* CIL Label */ ; -#line 160 - if (! (j < 5)) { -#line 160 - goto while_break___0; - } -#line 160 - term160_9-file_01-simple-cases ++; -#line 161 - b[j] += a[i]; -#line 162 - j ++; - } - while_break___0: /* CIL Label */ ; - } -#line 160 - term_exit- = term160_9-file_01-simple-cases; - } -#line 164 - i ++; - } - while_break: /* CIL Label */ ; - } -#line 157 - term_exit- = term157_2-file_01-simple-cases; - } -#line 166 - return; -} -} -#line 170 "tests/regression/55-loop-unrolling/01-simple-cases.c" -void example9(void) -{ - int a[5] ; - int i ; - int term174_2-file_01-simple-cases ; - - { -#line 173 - i = 0; - { -#line 174 - term174_2-file_01-simple-cases = 0; - { -#line 174 - while (1) { - while_continue: /* CIL Label */ ; -#line 174 - if (! 1) { -#line 174 - goto while_break; - } -#line 174 - term174_2-file_01-simple-cases ++; -#line 175 - a[i] = i; -#line 176 - i ++; -#line 177 - if (i == 5) { -#line 177 - goto while_break; - } - } - while_break: /* CIL Label */ ; - } -#line 174 - term_exit- = term174_2-file_01-simple-cases; - } -#line 179 - return; -} -} -#line 183 "tests/regression/55-loop-unrolling/01-simple-cases.c" -void example10(void) -{ - int a[5] ; - int i ; - int term187_2-file_01-simple-cases ; - - { -#line 186 - i = 0; - { -#line 187 - term187_2-file_01-simple-cases = 0; - { -#line 187 - while (1) { - while_continue: /* CIL Label */ ; -#line 187 - if (! (i < 5)) { -#line 187 - goto while_break; - } -#line 187 - term187_2-file_01-simple-cases ++; -#line 188 - if (i == 3) { -#line 189 - i ++; -#line 190 - goto while_continue; - } -#line 192 - a[i] = i; -#line 193 - i ++; - } - while_break: /* CIL Label */ ; - } -#line 187 - term_exit- = term187_2-file_01-simple-cases; - } -#line 195 - return; -} -} -#line 117 "/usr/include/x86_64-linux-gnu/bits/cpu-set.h" -extern int ( __attribute__((__leaf__)) __sched_cpucount)(size_t __setsize , cpu_set_t const *__setp ) __attribute__((__nothrow__)) ; -#line 119 -extern cpu_set_t *( __attribute__((__leaf__)) __sched_cpualloc)(size_t __count ) __attribute__((__nothrow__)) ; -#line 120 -extern void ( __attribute__((__leaf__)) __sched_cpufree)(cpu_set_t *__set ) __attribute__((__nothrow__)) ; -#line 54 "/usr/include/sched.h" -extern int ( __attribute__((__leaf__)) sched_setparam)(__pid_t __pid , struct sched_param const *__param ) __attribute__((__nothrow__)) ; -#line 58 -extern int ( __attribute__((__leaf__)) sched_getparam)(__pid_t __pid , struct sched_param *__param ) __attribute__((__nothrow__)) ; -#line 61 -extern int ( __attribute__((__leaf__)) sched_setscheduler)(__pid_t __pid , int __policy , - struct sched_param const *__param ) __attribute__((__nothrow__)) ; -#line 65 -extern int ( __attribute__((__leaf__)) sched_getscheduler)(__pid_t __pid ) __attribute__((__nothrow__)) ; -#line 68 -extern int ( __attribute__((__leaf__)) sched_yield)(void) __attribute__((__nothrow__)) ; -#line 71 -extern int ( __attribute__((__leaf__)) sched_get_priority_max)(int __algorithm ) __attribute__((__nothrow__)) ; -#line 74 -extern int ( __attribute__((__leaf__)) sched_get_priority_min)(int __algorithm ) __attribute__((__nothrow__)) ; -#line 78 -extern int ( __attribute__((__leaf__)) sched_rr_get_interval)(__pid_t __pid , struct timespec *__t ) __attribute__((__nothrow__)) ; -#line 72 "/usr/include/time.h" -extern clock_t ( __attribute__((__leaf__)) clock)(void) __attribute__((__nothrow__)) ; -#line 76 -extern time_t ( __attribute__((__leaf__)) time)(time_t *__timer ) __attribute__((__nothrow__)) ; -#line 79 -extern double ( __attribute__((__leaf__)) difftime)(time_t __time1 , time_t __time0 ) __attribute__((__nothrow__, -__const__)) ; -#line 83 -extern time_t ( __attribute__((__leaf__)) mktime)(struct tm *__tp ) __attribute__((__nothrow__)) ; -#line 100 -extern size_t ( __attribute__((__leaf__)) strftime)(char * __restrict __s , size_t __maxsize , - char const * __restrict __format , - struct tm const * __restrict __tp ) __attribute__((__nothrow__)) ; -#line 116 -extern size_t ( __attribute__((__leaf__)) strftime_l)(char * __restrict __s , size_t __maxsize , - char const * __restrict __format , - struct tm const * __restrict __tp , - locale_t __loc ) __attribute__((__nothrow__)) ; -#line 132 -extern struct tm *( __attribute__((__leaf__)) gmtime)(time_t const *__timer ) __attribute__((__nothrow__)) ; -#line 136 -extern struct tm *( __attribute__((__leaf__)) localtime)(time_t const *__timer ) __attribute__((__nothrow__)) ; -#line 154 -extern struct tm *( __attribute__((__leaf__)) gmtime_r)(time_t const * __restrict __timer , - struct tm * __restrict __tp ) __attribute__((__nothrow__)) ; -#line 159 -extern struct tm *( __attribute__((__leaf__)) localtime_r)(time_t const * __restrict __timer , - struct tm * __restrict __tp ) __attribute__((__nothrow__)) ; -#line 179 -extern char *( __attribute__((__leaf__)) asctime)(struct tm const *__tp ) __attribute__((__nothrow__)) ; -#line 183 -extern char *( __attribute__((__leaf__)) ctime)(time_t const *__timer ) __attribute__((__nothrow__)) ; -#line 197 -extern char *( __attribute__((__leaf__)) asctime_r)(struct tm const * __restrict __tp , - char * __restrict __buf ) __attribute__((__nothrow__)) ; -#line 202 -extern char *( __attribute__((__leaf__)) ctime_r)(time_t const * __restrict __timer , - char * __restrict __buf ) __attribute__((__nothrow__)) ; -#line 217 -extern char *__tzname[2] ; -#line 218 -extern int __daylight ; -#line 219 -extern long __timezone ; -#line 224 -extern char *tzname[2] ; -#line 228 -extern void ( __attribute__((__leaf__)) tzset)(void) __attribute__((__nothrow__)) ; -#line 232 -extern int daylight ; -#line 233 -extern long timezone ; -#line 246 -extern time_t ( __attribute__((__leaf__)) timegm)(struct tm *__tp ) __attribute__((__nothrow__)) ; -#line 263 -extern time_t ( __attribute__((__leaf__)) timelocal)(struct tm *__tp ) __attribute__((__nothrow__)) ; -#line 271 -extern int ( __attribute__((__leaf__)) dysize)(int __year ) __attribute__((__nothrow__, -__const__)) ; -#line 281 -extern int nanosleep(struct timespec const *__requested_time , struct timespec *__remaining ) ; -#line 285 -extern int ( __attribute__((__leaf__)) clock_getres)(clockid_t __clock_id , struct timespec *__res ) __attribute__((__nothrow__)) ; -#line 288 -extern int ( __attribute__((__nonnull__(2), __leaf__)) clock_gettime)(clockid_t __clock_id , - struct timespec *__tp ) __attribute__((__nothrow__)) ; -#line 292 -extern int ( __attribute__((__nonnull__(2), __leaf__)) clock_settime)(clockid_t __clock_id , - struct timespec const *__tp ) __attribute__((__nothrow__)) ; -#line 323 -extern int clock_nanosleep(clockid_t __clock_id , int __flags , struct timespec const *__req , - struct timespec *__rem ) ; -#line 338 -extern int ( __attribute__((__leaf__)) clock_getcpuclockid)(pid_t __pid , clockid_t *__clock_id ) __attribute__((__nothrow__)) ; -#line 343 -extern int ( __attribute__((__leaf__)) timer_create)(clockid_t __clock_id , struct sigevent * __restrict __evp , - timer_t * __restrict __timerid ) __attribute__((__nothrow__)) ; -#line 348 -extern int ( __attribute__((__leaf__)) timer_delete)(timer_t __timerid ) __attribute__((__nothrow__)) ; -#line 352 -extern int ( __attribute__((__leaf__)) timer_settime)(timer_t __timerid , int __flags , - struct itimerspec const * __restrict __value , - struct itimerspec * __restrict __ovalue ) __attribute__((__nothrow__)) ; -#line 357 -extern int ( __attribute__((__leaf__)) timer_gettime)(timer_t __timerid , struct itimerspec *__value ) __attribute__((__nothrow__)) ; -#line 376 -extern int ( __attribute__((__leaf__)) timer_getoverrun)(timer_t __timerid ) __attribute__((__nothrow__)) ; -#line 383 -extern int ( __attribute__((__nonnull__(1), __leaf__)) timespec_get)(struct timespec *__ts , - int __base ) __attribute__((__nothrow__)) ; -#line 202 "/usr/include/pthread.h" -extern int ( __attribute__((__nonnull__(1,3))) pthread_create)(pthread_t * __restrict __newthread , - pthread_attr_t const * __restrict __attr , - void *(*__start_routine)(void * ) , - void * __restrict __arg ) __attribute__((__nothrow__)) ; -#line 211 -extern void pthread_exit(void *__retval ) __attribute__((__noreturn__)) ; -#line 219 -extern int pthread_join(pthread_t __th , void **__thread_return ) ; -#line 269 -extern int ( __attribute__((__leaf__)) pthread_detach)(pthread_t __th ) __attribute__((__nothrow__)) ; -#line 273 -extern pthread_t ( __attribute__((__leaf__)) pthread_self)(void) __attribute__((__nothrow__, -__const__)) ; -#line 276 -extern int ( __attribute__((__leaf__)) pthread_equal)(pthread_t __thread1 , pthread_t __thread2 ) __attribute__((__nothrow__, -__const__)) ; -#line 285 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_attr_init)(pthread_attr_t *__attr ) __attribute__((__nothrow__)) ; -#line 288 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_attr_destroy)(pthread_attr_t *__attr ) __attribute__((__nothrow__)) ; -#line 292 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_attr_getdetachstate)(pthread_attr_t const *__attr , - int *__detachstate ) __attribute__((__nothrow__)) ; -#line 297 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_attr_setdetachstate)(pthread_attr_t *__attr , - int __detachstate ) __attribute__((__nothrow__)) ; -#line 303 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_attr_getguardsize)(pthread_attr_t const *__attr , - size_t *__guardsize ) __attribute__((__nothrow__)) ; -#line 308 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_attr_setguardsize)(pthread_attr_t *__attr , - size_t __guardsize ) __attribute__((__nothrow__)) ; -#line 314 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_attr_getschedparam)(pthread_attr_t const * __restrict __attr , - struct sched_param * __restrict __param ) __attribute__((__nothrow__)) ; -#line 319 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_attr_setschedparam)(pthread_attr_t * __restrict __attr , - struct sched_param const * __restrict __param ) __attribute__((__nothrow__)) ; -#line 324 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_attr_getschedpolicy)(pthread_attr_t const * __restrict __attr , - int * __restrict __policy ) __attribute__((__nothrow__)) ; -#line 329 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_attr_setschedpolicy)(pthread_attr_t *__attr , - int __policy ) __attribute__((__nothrow__)) ; -#line 333 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_attr_getinheritsched)(pthread_attr_t const * __restrict __attr , - int * __restrict __inherit ) __attribute__((__nothrow__)) ; -#line 338 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_attr_setinheritsched)(pthread_attr_t *__attr , - int __inherit ) __attribute__((__nothrow__)) ; -#line 344 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_attr_getscope)(pthread_attr_t const * __restrict __attr , - int * __restrict __scope ) __attribute__((__nothrow__)) ; -#line 349 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_attr_setscope)(pthread_attr_t *__attr , - int __scope ) __attribute__((__nothrow__)) ; -#line 353 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_attr_getstackaddr)(pthread_attr_t const * __restrict __attr , - void ** __restrict __stackaddr ) __attribute__((__nothrow__, -__deprecated__)) ; -#line 361 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_attr_setstackaddr)(pthread_attr_t *__attr , - void *__stackaddr ) __attribute__((__nothrow__, -__deprecated__)) ; -#line 366 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_attr_getstacksize)(pthread_attr_t const * __restrict __attr , - size_t * __restrict __stacksize ) __attribute__((__nothrow__)) ; -#line 373 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_attr_setstacksize)(pthread_attr_t *__attr , - size_t __stacksize ) __attribute__((__nothrow__)) ; -#line 379 -extern int ( __attribute__((__nonnull__(1,2,3), __leaf__)) pthread_attr_getstack)(pthread_attr_t const * __restrict __attr , - void ** __restrict __stackaddr , - size_t * __restrict __stacksize ) __attribute__((__nothrow__)) ; -#line 387 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_attr_setstack)(pthread_attr_t *__attr , - void *__stackaddr , - size_t __stacksize ) __attribute__((__nothrow__)) ; -#line 441 -extern int ( __attribute__((__nonnull__(3), __leaf__)) pthread_setschedparam)(pthread_t __target_thread , - int __policy , - struct sched_param const *__param ) __attribute__((__nothrow__)) ; -#line 446 -extern int ( __attribute__((__nonnull__(2,3), __leaf__)) pthread_getschedparam)(pthread_t __target_thread , - int * __restrict __policy , - struct sched_param * __restrict __param ) __attribute__((__nothrow__)) ; -#line 452 -extern int ( __attribute__((__leaf__)) pthread_setschedprio)(pthread_t __target_thread , - int __prio ) __attribute__((__nothrow__)) ; -#line 509 -int ( __attribute__((__nonnull__(1,2))) pthread_once)(pthread_once_t *once_control , - void (*init_routine)(void) ) __attribute__((__goblint_stub__)) ; -#line 521 -extern int pthread_setcancelstate(int __state , int *__oldstate ) ; -#line 525 -extern int pthread_setcanceltype(int __type , int *__oldtype ) ; -#line 528 -extern int pthread_cancel(pthread_t __th ) ; -#line 533 -extern void pthread_testcancel(void) ; -#line 697 -extern void __pthread_register_cancel(__pthread_unwind_buf_t *__buf ) ; -#line 709 -extern void __pthread_unregister_cancel(__pthread_unwind_buf_t *__buf ) ; -#line 750 -extern void __pthread_unwind_next(__pthread_unwind_buf_t *__buf ) __attribute__((__weak__, -__noreturn__)) ; -#line 773 -extern int __sigsetjmp(struct __jmp_buf_tag *__env , int __savemask ) __attribute__((__nothrow__)) ; -#line 781 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutex_init)(pthread_mutex_t *__mutex , - pthread_mutexattr_t const *__mutexattr ) __attribute__((__nothrow__)) ; -#line 786 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutex_destroy)(pthread_mutex_t *__mutex ) __attribute__((__nothrow__)) ; -#line 790 -extern int ( __attribute__((__nonnull__(1))) pthread_mutex_trylock)(pthread_mutex_t *__mutex ) __attribute__((__nothrow__)) ; -#line 794 -extern int ( __attribute__((__nonnull__(1))) pthread_mutex_lock)(pthread_mutex_t *__mutex ) __attribute__((__nothrow__)) ; -#line 800 -extern int ( __attribute__((__nonnull__(1,2))) pthread_mutex_timedlock)(pthread_mutex_t * __restrict __mutex , - struct timespec const * __restrict __abstime ) __attribute__((__nothrow__)) ; -#line 835 -extern int ( __attribute__((__nonnull__(1))) pthread_mutex_unlock)(pthread_mutex_t *__mutex ) __attribute__((__nothrow__)) ; -#line 840 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_mutex_getprioceiling)(pthread_mutex_t const * __restrict __mutex , - int * __restrict __prioceiling ) __attribute__((__nothrow__)) ; -#line 847 -extern int ( __attribute__((__nonnull__(1,3), __leaf__)) pthread_mutex_setprioceiling)(pthread_mutex_t * __restrict __mutex , - int __prioceiling , - int * __restrict __old_ceiling ) __attribute__((__nothrow__)) ; -#line 855 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutex_consistent)(pthread_mutex_t *__mutex ) __attribute__((__nothrow__)) ; -#line 874 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutexattr_init)(pthread_mutexattr_t *__attr ) __attribute__((__nothrow__)) ; -#line 878 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutexattr_destroy)(pthread_mutexattr_t *__attr ) __attribute__((__nothrow__)) ; -#line 882 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_mutexattr_getpshared)(pthread_mutexattr_t const * __restrict __attr , - int * __restrict __pshared ) __attribute__((__nothrow__)) ; -#line 888 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutexattr_setpshared)(pthread_mutexattr_t *__attr , - int __pshared ) __attribute__((__nothrow__)) ; -#line 894 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_mutexattr_gettype)(pthread_mutexattr_t const * __restrict __attr , - int * __restrict __kind ) __attribute__((__nothrow__)) ; -#line 901 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutexattr_settype)(pthread_mutexattr_t *__attr , - int __kind ) __attribute__((__nothrow__)) ; -#line 906 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_mutexattr_getprotocol)(pthread_mutexattr_t const * __restrict __attr , - int * __restrict __protocol ) __attribute__((__nothrow__)) ; -#line 913 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutexattr_setprotocol)(pthread_mutexattr_t *__attr , - int __protocol ) __attribute__((__nothrow__)) ; -#line 918 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_mutexattr_getprioceiling)(pthread_mutexattr_t const * __restrict __attr , - int * __restrict __prioceiling ) __attribute__((__nothrow__)) ; -#line 924 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutexattr_setprioceiling)(pthread_mutexattr_t *__attr , - int __prioceiling ) __attribute__((__nothrow__)) ; -#line 930 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_mutexattr_getrobust)(pthread_mutexattr_t const *__attr , - int *__robustness ) __attribute__((__nothrow__)) ; -#line 946 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutexattr_setrobust)(pthread_mutexattr_t *__attr , - int __robustness ) __attribute__((__nothrow__)) ; -#line 967 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_rwlock_init)(pthread_rwlock_t * __restrict __rwlock , - pthread_rwlockattr_t const * __restrict __attr ) __attribute__((__nothrow__)) ; -#line 972 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_rwlock_destroy)(pthread_rwlock_t *__rwlock ) __attribute__((__nothrow__)) ; -#line 976 -extern int ( __attribute__((__nonnull__(1))) pthread_rwlock_rdlock)(pthread_rwlock_t *__rwlock ) __attribute__((__nothrow__)) ; -#line 980 -extern int ( __attribute__((__nonnull__(1))) pthread_rwlock_tryrdlock)(pthread_rwlock_t *__rwlock ) __attribute__((__nothrow__)) ; -#line 986 -extern int ( __attribute__((__nonnull__(1,2))) pthread_rwlock_timedrdlock)(pthread_rwlock_t * __restrict __rwlock , - struct timespec const * __restrict __abstime ) __attribute__((__nothrow__)) ; -#line 1023 -extern int ( __attribute__((__nonnull__(1))) pthread_rwlock_wrlock)(pthread_rwlock_t *__rwlock ) __attribute__((__nothrow__)) ; -#line 1027 -extern int ( __attribute__((__nonnull__(1))) pthread_rwlock_trywrlock)(pthread_rwlock_t *__rwlock ) __attribute__((__nothrow__)) ; -#line 1033 -extern int ( __attribute__((__nonnull__(1,2))) pthread_rwlock_timedwrlock)(pthread_rwlock_t * __restrict __rwlock , - struct timespec const * __restrict __abstime ) __attribute__((__nothrow__)) ; -#line 1071 -extern int ( __attribute__((__nonnull__(1))) pthread_rwlock_unlock)(pthread_rwlock_t *__rwlock ) __attribute__((__nothrow__)) ; -#line 1078 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_rwlockattr_init)(pthread_rwlockattr_t *__attr ) __attribute__((__nothrow__)) ; -#line 1082 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_rwlockattr_destroy)(pthread_rwlockattr_t *__attr ) __attribute__((__nothrow__)) ; -#line 1086 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_rwlockattr_getpshared)(pthread_rwlockattr_t const * __restrict __attr , - int * __restrict __pshared ) __attribute__((__nothrow__)) ; -#line 1092 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_rwlockattr_setpshared)(pthread_rwlockattr_t *__attr , - int __pshared ) __attribute__((__nothrow__)) ; -#line 1097 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_rwlockattr_getkind_np)(pthread_rwlockattr_t const * __restrict __attr , - int * __restrict __pref ) __attribute__((__nothrow__)) ; -#line 1103 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_rwlockattr_setkind_np)(pthread_rwlockattr_t *__attr , - int __pref ) __attribute__((__nothrow__)) ; -#line 1112 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_cond_init)(pthread_cond_t * __restrict __cond , - pthread_condattr_t const * __restrict __cond_attr ) __attribute__((__nothrow__)) ; -#line 1117 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_cond_destroy)(pthread_cond_t *__cond ) __attribute__((__nothrow__)) ; -#line 1121 -extern int ( __attribute__((__nonnull__(1))) pthread_cond_signal)(pthread_cond_t *__cond ) __attribute__((__nothrow__)) ; -#line 1125 -extern int ( __attribute__((__nonnull__(1))) pthread_cond_broadcast)(pthread_cond_t *__cond ) __attribute__((__nothrow__)) ; -#line 1133 -extern int ( __attribute__((__nonnull__(1,2))) pthread_cond_wait)(pthread_cond_t * __restrict __cond , - pthread_mutex_t * __restrict __mutex ) ; -#line 1145 -extern int ( __attribute__((__nonnull__(1,2,3))) pthread_cond_timedwait)(pthread_cond_t * __restrict __cond , - pthread_mutex_t * __restrict __mutex , - struct timespec const * __restrict __abstime ) ; -#line 1194 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_condattr_init)(pthread_condattr_t *__attr ) __attribute__((__nothrow__)) ; -#line 1198 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_condattr_destroy)(pthread_condattr_t *__attr ) __attribute__((__nothrow__)) ; -#line 1202 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_condattr_getpshared)(pthread_condattr_t const * __restrict __attr , - int * __restrict __pshared ) __attribute__((__nothrow__)) ; -#line 1208 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_condattr_setpshared)(pthread_condattr_t *__attr , - int __pshared ) __attribute__((__nothrow__)) ; -#line 1213 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_condattr_getclock)(pthread_condattr_t const * __restrict __attr , - __clockid_t * __restrict __clock_id ) __attribute__((__nothrow__)) ; -#line 1219 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_condattr_setclock)(pthread_condattr_t *__attr , - __clockid_t __clock_id ) __attribute__((__nothrow__)) ; -#line 1230 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_spin_init)(pthread_spinlock_t *__lock , - int __pshared ) __attribute__((__nothrow__)) ; -#line 1234 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_spin_destroy)(pthread_spinlock_t *__lock ) __attribute__((__nothrow__)) ; -#line 1238 -extern int ( __attribute__((__nonnull__(1))) pthread_spin_lock)(pthread_spinlock_t *__lock ) __attribute__((__nothrow__)) ; -#line 1242 -extern int ( __attribute__((__nonnull__(1))) pthread_spin_trylock)(pthread_spinlock_t *__lock ) __attribute__((__nothrow__)) ; -#line 1246 -extern int ( __attribute__((__nonnull__(1))) pthread_spin_unlock)(pthread_spinlock_t *__lock ) __attribute__((__nothrow__)) ; -#line 1254 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_barrier_init)(pthread_barrier_t * __restrict __barrier , - pthread_barrierattr_t const * __restrict __attr , - unsigned int __count ) __attribute__((__nothrow__)) ; -#line 1260 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_barrier_destroy)(pthread_barrier_t *__barrier ) __attribute__((__nothrow__)) ; -#line 1264 -extern int ( __attribute__((__nonnull__(1))) pthread_barrier_wait)(pthread_barrier_t *__barrier ) __attribute__((__nothrow__)) ; -#line 1269 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_barrierattr_init)(pthread_barrierattr_t *__attr ) __attribute__((__nothrow__)) ; -#line 1273 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_barrierattr_destroy)(pthread_barrierattr_t *__attr ) __attribute__((__nothrow__)) ; -#line 1277 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_barrierattr_getpshared)(pthread_barrierattr_t const * __restrict __attr , - int * __restrict __pshared ) __attribute__((__nothrow__)) ; -#line 1283 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_barrierattr_setpshared)(pthread_barrierattr_t *__attr , - int __pshared ) __attribute__((__nothrow__)) ; -#line 1297 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_key_create)(pthread_key_t *__key , - void (*__destr_function)(void * ) ) __attribute__((__nothrow__)) ; -#line 1302 -extern int ( __attribute__((__leaf__)) pthread_key_delete)(pthread_key_t __key ) __attribute__((__nothrow__)) ; -#line 1305 -extern void *( __attribute__((__leaf__)) pthread_getspecific)(pthread_key_t __key ) __attribute__((__nothrow__)) ; -#line 1308 -extern int ( __attribute__((__leaf__)) pthread_setspecific)(pthread_key_t __key , - void const *__pointer ) __attribute__((__nothrow__)) ; -#line 1315 -extern int ( __attribute__((__nonnull__(2), __leaf__)) pthread_getcpuclockid)(pthread_t __thread_id , - __clockid_t *__clock_id ) __attribute__((__nothrow__)) ; -#line 1332 -extern int ( __attribute__((__leaf__)) pthread_atfork)(void (*__prepare)(void) , void (*__parent)(void) , - void (*__child)(void) ) __attribute__((__nothrow__)) ; -#line 5 "lib/libc/stub/src/pthread.c" -int ( __attribute__((__nonnull__(1,2))) pthread_once)(pthread_once_t *once_control , - void (*init_routine)(void) ) __attribute__((__goblint_stub__)) ; -#line 5 "lib/libc/stub/src/pthread.c" -int ( __attribute__((__nonnull__(1,2))) pthread_once)(pthread_once_t *once_control , - void (*init_routine)(void) ) -{ - int top ; - - { -#line 8 - (*init_routine)(); -#line 9 - return (top); -} -} -#line 6 "lib/libc/stub/src/stdlib.c" -void qsort(void *ptr , size_t count , size_t size , int (*comp)(void const * , void const * ) ) __attribute__((__goblint_stub__)) ; -#line 7 -void qsort(void *ptr , size_t count , size_t size , int (*comp)(void const * , void const * ) ) __attribute__((__goblint_stub__)) ; -#line 7 "lib/libc/stub/src/stdlib.c" -void qsort(void *ptr , size_t count , size_t size , int (*comp)(void const * , void const * ) ) -{ - size_t i ; - size_t j ; - size_t i___0 ; - size_t j___0 ; - int r ; - size_t k ; - char *a ; - char *b ; - char c ; - int term10_5-file_stdlib ; - int term9_3-file_stdlib ; - int term21_9-file_stdlib ; - int term17_5-file_stdlib ; - int term16_3-file_stdlib ; - - { -#line 9 - i = (size_t )0; - { -#line 9 - term9_3-file_stdlib = 0; - { -#line 9 - while (1) { - while_continue: /* CIL Label */ ; -#line 9 - if (! (i < count)) { -#line 9 - goto while_break; - } -#line 9 - term9_3-file_stdlib ++; -#line 10 - j = (size_t )0; - { -#line 10 - term10_5-file_stdlib = 0; - { -#line 10 - while (1) { - while_continue___0: /* CIL Label */ ; -#line 10 - if (! (j < count)) { -#line 10 - goto while_break___0; - } -#line 10 - term10_5-file_stdlib ++; -#line 11 - (*comp)((void const *)(ptr + i * size), (void const *)(ptr + j * size)); -#line 10 - j ++; - } - while_break___0: /* CIL Label */ ; - } -#line 10 - term_exit- = term10_5-file_stdlib; - } -#line 9 - i ++; - } - while_break: /* CIL Label */ ; - } -#line 9 - term_exit- = term9_3-file_stdlib; - } -#line 16 - i___0 = (size_t )0; - { -#line 16 - term16_3-file_stdlib = 0; - { -#line 16 - while (1) { - while_continue___1: /* CIL Label */ ; -#line 16 - if (! (i___0 < count)) { -#line 16 - goto while_break___1; - } -#line 16 - term16_3-file_stdlib ++; -#line 17 - j___0 = (size_t )0; - { -#line 17 - term17_5-file_stdlib = 0; - { -#line 17 - while (1) { - while_continue___2: /* CIL Label */ ; -#line 17 - if (! (j___0 < count)) { -#line 17 - goto while_break___2; - } -#line 17 - term17_5-file_stdlib ++; -#line 19 - if (r) { -#line 21 - k = (size_t )0; - { -#line 21 - term21_9-file_stdlib = 0; - { -#line 21 - while (1) { - while_continue___3: /* CIL Label */ ; -#line 21 - if (! (k < size)) { -#line 21 - goto while_break___3; - } -#line 21 - term21_9-file_stdlib ++; -#line 22 - a = (char *)((ptr + i___0 * size) + k); -#line 23 - b = (char *)((ptr + j___0 * size) + k); -#line 24 - c = *a; -#line 25 - *a = *b; -#line 26 - *b = c; -#line 21 - k ++; - } - while_break___3: /* CIL Label */ ; - } -#line 21 - term_exit- = term21_9-file_stdlib; - } - } -#line 17 - j___0 ++; - } - while_break___2: /* CIL Label */ ; - } -#line 17 - term_exit- = term17_5-file_stdlib; - } -#line 16 - i___0 ++; - } - while_break___1: /* CIL Label */ ; - } -#line 16 - term_exit- = term16_3-file_stdlib; - } -#line 33 - return; -} -} -#line 37 -void *bsearch(void const *key , void const *ptr , size_t count , size_t size , - int (*comp)(void const * , void const * ) ) __attribute__((__goblint_stub__)) ; -#line 38 -void *bsearch(void const *key , void const *ptr , size_t count , size_t size , - int (*comp)(void const * , void const * ) ) __attribute__((__goblint_stub__)) ; -#line 38 "lib/libc/stub/src/stdlib.c" -void *bsearch(void const *key , void const *ptr , size_t count , size_t size , - int (*comp)(void const * , void const * ) ) -{ - size_t i ; - void const *a ; - int tmp ; - int term40_3-file_stdlib ; - - { -#line 40 - i = (size_t )0; - { -#line 40 - term40_3-file_stdlib = 0; - { -#line 40 - while (1) { - while_continue: /* CIL Label */ ; -#line 40 - if (! (i < count)) { -#line 40 - goto while_break; - } -#line 40 - term40_3-file_stdlib ++; -#line 41 - a = ptr + i * size; -#line 42 - tmp = (*comp)(key, a); -#line 42 - if (tmp == 0) { -#line 43 - return ((void *)a); - } -#line 40 - i ++; - } - while_break: /* CIL Label */ ; - } -#line 40 - term_exit- = term40_3-file_stdlib; - } -#line 47 - return ((void *)0); -} -} diff --git a/runningGob.sh b/runningGob.sh index fcb5417192..0e43ee45c5 100755 --- a/runningGob.sh +++ b/runningGob.sh @@ -8,7 +8,7 @@ options_apron="--set ana.activated[+] apron --enable ana.int.interval --set ana. options_signs="--set "ana.activated[+]" signs --enable warn.debug" options_term="--set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra" -cfile_loops="tests/regression/55-loop-unrolling/01-simple-cases.c" +cfile_loops="tests/regression/00-sanity/36-strict-loop-dead.c" cfile_signs="tests/regression/99-tutorials/01-first.c" cfile_goto="tests/incremental/02-cfg-comparison/01-added-return-stmt.c" From 31ec579e2d7b3b51ee293db8b42d96290cfaccfd Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 20 Jun 2023 16:43:01 +0300 Subject: [PATCH 0176/1312] Split ancestor accs and outer ancestor accs Co-authored-by: Simmo Saan --- src/analyses/raceAnalysis.ml | 12 ++++++------ src/domains/access.ml | 21 +++++++++++++-------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index 18426ebc4f..2e9a3d5b4b 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -131,25 +131,25 @@ struct (* ignore (Pretty.printf "WarnGlobal %a\n" CilType.Varinfo.pretty g); *) let trie = G.access (ctx.global g) in (** Distribute access to contained fields. *) - let rec distribute_inner offset (accs, children) ancestor_accs = + let rec distribute_inner offset (accs, children) ~ancestor_accs ~ancestor_outer_accs = let outer_accs = match outer_memo (g', offset) with | Some outer_memo -> distribute_outer ctx outer_memo | None -> Access.AS.empty () in M.trace "access" "outer accs = %a" Access.AS.pretty outer_accs; - let ancestor_accs' = Access.AS.union ancestor_accs outer_accs in if not (Access.AS.is_empty accs) then ( let memo = (g', offset) in let mem_loc_str = GobPretty.sprint Access.Memo.pretty memo in - Timing.wrap ~args:[("memory location", `String mem_loc_str)] "race" (Access.warn_global ~safe ~vulnerable ~unsafe ~ancestor_accs:ancestor_accs' memo) accs + Timing.wrap ~args:[("memory location", `String mem_loc_str)] "race" (Access.warn_global ~safe ~vulnerable ~unsafe ~ancestor_accs ~ancestor_outer_accs ~outer_accs memo) accs ); - let ancestor_accs'' = Access.AS.union ancestor_accs' accs in + let ancestor_outer_accs' = Access.AS.union ancestor_outer_accs outer_accs in + let ancestor_accs' = Access.AS.union ancestor_accs accs in OffsetTrie.ChildMap.iter (fun child_key child_trie -> - distribute_inner (Offset.Unit.add_offset offset (OneOffset.to_offset child_key)) child_trie ancestor_accs'' + distribute_inner (Offset.Unit.add_offset offset (OneOffset.to_offset child_key)) child_trie ~ancestor_accs:ancestor_accs' ~ancestor_outer_accs:ancestor_outer_accs' ) children; in - distribute_inner `NoOffset trie (Access.AS.empty ()) + distribute_inner `NoOffset trie ~ancestor_accs:(Access.AS.empty ()) ~ancestor_outer_accs:(Access.AS.empty ()) | `Right _ -> (* vars *) () end diff --git a/src/domains/access.ml b/src/domains/access.ml index 0e029ff128..1dc6cbafce 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -357,9 +357,9 @@ let may_race (conf,(kind: AccessKind.t),loc,e,a) (conf2,(kind2: AccessKind.t),lo else true -let group_may_race ~ancestor_accs accs = +let group_may_race ~ancestor_accs ~ancestor_outer_accs ~outer_accs accs = (* BFS to traverse one component with may_race edges *) - let rec bfs' ~ancestor_accs ~accs ~todo ~visited = + let rec bfs' ~ancestor_accs ~ancestor_outer_accs ~outer_accs ~accs ~todo ~visited = let may_race_accs ~accs ~todo = AS.fold (fun acc todo' -> AS.fold (fun acc' todo' -> @@ -372,16 +372,21 @@ let group_may_race ~ancestor_accs accs = in let accs' = AS.diff accs todo in let ancestor_accs' = AS.diff ancestor_accs todo in + let ancestor_outer_accs' = AS.diff ancestor_outer_accs todo in + let outer_accs' = AS.diff outer_accs todo in let todo_accs = may_race_accs ~accs:accs' ~todo in - let todo_ancestor_accs = may_race_accs ~accs:ancestor_accs' ~todo:(AS.diff todo ancestor_accs') in - let todo' = AS.union todo_accs todo_ancestor_accs in + let accs_todo = AS.inter todo accs in + let todo_ancestor_accs = may_race_accs ~accs:ancestor_accs' ~todo:accs_todo in + let todo_ancestor_outer_accs = may_race_accs ~accs:ancestor_outer_accs' ~todo:accs_todo in + let todo_outer_accs = may_race_accs ~accs:outer_accs' ~todo:accs_todo in + let todo' = AS.union (AS.union todo_accs todo_ancestor_accs) (AS.union todo_ancestor_outer_accs todo_outer_accs) in let visited' = AS.union visited todo in if AS.is_empty todo' then (accs', visited') else - (bfs' [@tailcall]) ~ancestor_accs:ancestor_accs' ~accs:accs' ~todo:todo' ~visited:visited' + (bfs' [@tailcall]) ~ancestor_accs:ancestor_accs' ~ancestor_outer_accs:ancestor_outer_accs' ~outer_accs:outer_accs' ~accs:accs' ~todo:todo' ~visited:visited' in - let bfs accs acc = bfs' ~ancestor_accs ~accs ~todo:(AS.singleton acc) ~visited:(AS.empty ()) in + let bfs accs acc = bfs' ~ancestor_accs ~ancestor_outer_accs ~outer_accs ~accs ~todo:(AS.singleton acc) ~visited:(AS.empty ()) in (* repeat BFS to find all components *) let rec components comps accs = if AS.is_empty accs then @@ -457,7 +462,7 @@ let print_accesses memo grouped_accs = M.msg_group Success ~category:Race "Memory location %a (safe)" Memo.pretty memo (msgs safe_accs) ) -let warn_global ~safe ~vulnerable ~unsafe ~ancestor_accs memo accs = - let grouped_accs = group_may_race ~ancestor_accs accs in (* do expensive component finding only once *) +let warn_global ~safe ~vulnerable ~unsafe ~ancestor_accs ~ancestor_outer_accs ~outer_accs memo accs = + let grouped_accs = group_may_race ~ancestor_accs ~ancestor_outer_accs ~outer_accs accs in (* do expensive component finding only once *) incr_summary ~safe ~vulnerable ~unsafe memo grouped_accs; print_accesses memo grouped_accs From 2b75972e9f60087a5558d8b8794652e7084d77d1 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Tue, 20 Jun 2023 16:13:57 +0200 Subject: [PATCH 0177/1312] added weird test case --- .../55-loop-unrolling/01-simple-cases.c | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/tests/regression/55-loop-unrolling/01-simple-cases.c b/tests/regression/55-loop-unrolling/01-simple-cases.c index 0073717187..98e8e34ff0 100644 --- a/tests/regression/55-loop-unrolling/01-simple-cases.c +++ b/tests/regression/55-loop-unrolling/01-simple-cases.c @@ -3,6 +3,27 @@ int global; +int f(int x); +int g(int x); + +int f(int x) +{ +if (x <= 0) return 0; +else return g(x) + g(x + 1); +} + +int g(int x) +{ +if (x <= 0) return 0; +else return f(x - 1) + f(x - 2); +} + +int main() { +int x = __VERIFIER_nondet_int(); +g(x); +} + +/* int main(void) { example1(); @@ -16,7 +37,7 @@ int main(void) example9(); example10(); return 0; -} +}*/ // Simple example void example1(void) @@ -30,7 +51,8 @@ void example1(void) } __goblint_check(a[0] == 0); // UNKNOWN - __goblint_check(a[3] == 3); // UNKNOWN + lab:__goblint_check(a[3] == 3); // UNKNOWN + goto lab; } // Do-while loop simple example From c6f37b2cada18c5cc8d8ca70cf0c39ef6bfda201 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Tue, 20 Jun 2023 16:20:19 +0200 Subject: [PATCH 0178/1312] added weird test case + merging --- runningGob.sh | 2 +- tests/regression/55-loop-unrolling/01-simple-cases.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/runningGob.sh b/runningGob.sh index 0e43ee45c5..fcb5417192 100755 --- a/runningGob.sh +++ b/runningGob.sh @@ -8,7 +8,7 @@ options_apron="--set ana.activated[+] apron --enable ana.int.interval --set ana. options_signs="--set "ana.activated[+]" signs --enable warn.debug" options_term="--set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra" -cfile_loops="tests/regression/00-sanity/36-strict-loop-dead.c" +cfile_loops="tests/regression/55-loop-unrolling/01-simple-cases.c" cfile_signs="tests/regression/99-tutorials/01-first.c" cfile_goto="tests/incremental/02-cfg-comparison/01-added-return-stmt.c" diff --git a/tests/regression/55-loop-unrolling/01-simple-cases.c b/tests/regression/55-loop-unrolling/01-simple-cases.c index 98e8e34ff0..9bf8d98683 100644 --- a/tests/regression/55-loop-unrolling/01-simple-cases.c +++ b/tests/regression/55-loop-unrolling/01-simple-cases.c @@ -37,7 +37,7 @@ int main(void) example9(); example10(); return 0; -}*/ +} // Simple example void example1(void) @@ -216,3 +216,4 @@ void example10(void) } return 0; } +*/ \ No newline at end of file From 566ae50925db871301039150ec3c50e3b9e8d537 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Tue, 20 Jun 2023 22:08:05 +0200 Subject: [PATCH 0179/1312] Fix indentation --- src/framework/analyses.ml | 10 +++++----- src/framework/constraints.ml | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 1d1972ac45..6f53fa098d 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -159,18 +159,18 @@ struct | `Bot -> G.bot () | `Lifted1 x -> x | _ -> failwith "GVarG.spec" - let contexts = function + let contexts = function | `Bot -> CSet.bot () | `Lifted2 x -> x | _ -> failwith "GVarG.contexts" - let create_spec spec = `Lifted1 spec - let create_contexts contexts = `Lifted2 contexts + let create_spec spec = `Lifted1 spec + let create_contexts contexts = `Lifted2 contexts - let printXml f = function + let printXml f = function | `Lifted1 x -> G.printXml f x | `Lifted2 x -> BatPrintf.fprintf f "%a" CSet.printXml x | x -> BatPrintf.fprintf f "%a" printXml x -*) + *) end exception Deadcode diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 07750122a5..d66d95d229 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -563,7 +563,7 @@ struct let side_context sideg f c = if !AnalysisState.postsolving then sideg (GVar.contexts f) (G.create_contexts (G.CSet.singleton c)) - + let common_ctx var edge prev_node pval (getl:lv -> ld) sidel getg sideg : (D.t, S.G.t, S.C.t, S.V.t) ctx * D.t list ref * (lval option * varinfo * exp list * D.t) list ref = let r = ref [] in let spawns = ref [] in @@ -1127,7 +1127,7 @@ struct | `Lifted2 d -> LH.replace l' x d (* | `Bot -> () *) (* Since Verify2 is broken and only checks existing keys, add it with local bottom value. - This works around some cases, where Verify2 would not detect a problem due to completely missing variable. *) + This works around some cases, where Verify2 would not detect a problem due to completely missing variable. *) | `Bot -> LH.replace l' x (S.D.bot ()) | `Top -> failwith "GlobConstrSolFromEqConstrSolBase.split_vars: local variable has top value" | `Lifted1 _ -> failwith "GlobConstrSolFromEqConstrSolBase.split_vars: local variable has global value" @@ -1764,10 +1764,10 @@ struct in PP.iter f h1; (* let k1 = Set.of_enum @@ PP.keys h1 in - let k2 = Set.of_enum @@ PP.keys h2 in - let o1 = Set.cardinal @@ Set.diff k1 k2 in - let o2 = Set.cardinal @@ Set.diff k2 k1 in - Printf.printf "locals: \tequal = %d\tleft = %d[%d]\tright = %d[%d]\tincomparable = %d\n" !eq !le o1 !gr o2 !uk *) + let k2 = Set.of_enum @@ PP.keys h2 in + let o1 = Set.cardinal @@ Set.diff k1 k2 in + let o2 = Set.cardinal @@ Set.diff k2 k1 in + Printf.printf "locals: \tequal = %d\tleft = %d[%d]\tright = %d[%d]\tincomparable = %d\n" !eq !le o1 !gr o2 !uk *) Printf.printf "locals: \tequal = %d\tleft = %d\tright = %d\tincomparable = %d\n" !eq !le !gr !uk let compare_locals_ctx h1 h2 = From 6cf3d129291481c6f899b9b395353b1fa7a23be1 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Tue, 20 Jun 2023 22:28:49 +0200 Subject: [PATCH 0180/1312] Small changes Direct empty list comparison instead of checking length = 0. Make loop_heads a function with argument (). --- src/analyses/termination_new.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index f062030820..a3aa3aef92 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -6,7 +6,7 @@ open TerminationPreprocessing exception PreProcessing of string -let loop_heads = +let loop_heads () = let module FileCfg = struct let file = !Cilfacade.current_file @@ -31,7 +31,7 @@ let is_loop_exit_indicator (x : varinfo) = x = !loop_exit let no_upjumping_gotos () = - List.length upjumping_gotos.contents = 0 + upjumping_gotos.contents = [] (** Checks whether a variable can be bounded *) let check_bounded ctx varinfo = From cfaf4178e38eb1247cb25d974e52b3be6683ba49 Mon Sep 17 00:00:00 2001 From: Isidor Zweckstetter Date: Tue, 20 Jun 2023 22:53:50 +0200 Subject: [PATCH 0181/1312] added location to recursion Warning --- runningGob.sh | 2 +- src/framework/constraints.ml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/runningGob.sh b/runningGob.sh index 0e43ee45c5..fcb5417192 100755 --- a/runningGob.sh +++ b/runningGob.sh @@ -8,7 +8,7 @@ options_apron="--set ana.activated[+] apron --enable ana.int.interval --set ana. options_signs="--set "ana.activated[+]" signs --enable warn.debug" options_term="--set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra" -cfile_loops="tests/regression/00-sanity/36-strict-loop-dead.c" +cfile_loops="tests/regression/55-loop-unrolling/01-simple-cases.c" cfile_signs="tests/regression/99-tutorials/01-first.c" cfile_goto="tests/incremental/02-cfg-comparison/01-added-return-stmt.c" diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 48dab4e84c..b998ab9629 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1738,7 +1738,7 @@ struct (*Cycle found*) let msgs = [ - (Pretty.dprintf "The program might not terminate! (Fundec %a is contained in a call graph cycle)\n" CilType.Fundec.pretty fundec_e, Some (M.Location.CilLocation locUnknown)); + (Pretty.dprintf "The program might not terminate! (Fundec %a is contained in a call graph cycle)\n" CilType.Fundec.pretty fundec_e, Some (M.Location.CilLocation fundec_e.svar.vdecl)); ] in M.msg_group Warning ~category:NonTerminating "Recursion cycle" msgs) else if not (LH.mem global_visited_calls call) then begin From a4991e0093f739aa1fc190d7cae40c1d93a1971d Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Wed, 21 Jun 2023 05:51:55 +0200 Subject: [PATCH 0182/1312] Adapted update_suite.rb and tests to termination output --- scripts/update_suite.rb | 71 ++++++++++--------- .../01-simple-loop-terminating.c | 4 +- .../02-simple-loop-nonterminating.c | 4 +- .../03-nested-loop-terminating.c | 6 +- .../04-nested-loop-nonterminating.c | 6 +- .../80-termination/05-for-loop-terminating.c | 4 +- .../06-for-loop-nonterminating.c | 4 +- .../07-nested-for-loop-terminating.c | 6 +- .../08-nested-for-loop-nonterminating.c | 6 +- .../09-complex-for-loop-terminating.c | 24 +++---- .../10-complex-loop-terminating.c | 24 +++---- .../80-termination/11-loopless-termination.c | 2 +- .../12-do-while-instant-terminating.c | 4 +- .../80-termination/13-do-while-terminating.c | 4 +- .../14-do-while-nonterminating.c | 4 +- .../15-complex-loop-combination-terminating.c | 34 ++++----- ...16-nested-loop-nontrivial-nonterminating.c | 6 +- .../80-termination/17-goto-terminating.c | 4 +- .../80-termination/18-goto-nonterminating.c | 4 +- .../80-termination/19-rand-terminating.c | 6 +- .../80-termination/20-rand-nonterminating.c | 6 +- .../21-no-exit-on-rand-unproofable.c | 3 +- .../22-exit-on-rand-unproofable.c | 3 +- .../23-exit-on-rand-terminating.c | 2 +- .../24-upjumping-goto-loopless-terminating.c | 2 +- .../25-leave-loop-goto-terminating.c | 2 +- .../26-enter-loop-goto-terminating.c | 2 +- .../27-upjumping-goto-nonterminating.c | 2 +- .../81-recursion/01-simple-terminating.c | 2 +- .../81-recursion/02-simple-nonterminating.c | 2 +- .../81-recursion/03-nested-terminating.c | 2 +- .../81-recursion/04-nested-nonterminating.c | 2 +- 32 files changed, 133 insertions(+), 124 deletions(-) diff --git a/scripts/update_suite.rb b/scripts/update_suite.rb index dead6cd8f1..69b3bae485 100755 --- a/scripts/update_suite.rb +++ b/scripts/update_suite.rb @@ -147,31 +147,30 @@ def collect_warnings @vars = $1 @evals = $2 end + if l =~ /\[NonTerminating\]/ then warnings[-2] = "non_local_term" end # Get NonTerminating warning next unless l =~ /(.*)\(.*?\:(\d+)(?:\:\d+)?(?:-(?:\d+)(?:\:\d+)?)?\)/ obj,i = $1,$2.to_i - ranking = ["other", "warn", "race", "norace", "deadlock", "nodeadlock", "success", "fail", "unknown", "term", "noterm"] - thiswarn = case obj - when /\(conf\. \d+\)/ then "race" - when /Deadlock/ then "deadlock" - when /lock (before|after):/ then "deadlock" - when /Assertion .* will fail/ then "fail" - when /Assertion .* will succeed/ then "success" - when /Assertion .* is unknown/ then "unknown" - when /invariant confirmed/ then "success" - when /invariant unconfirmed/ then "unknown" - when /invariant refuted/ then "fail" - when /^\[Warning\]/ then "warn" - when /^\[Error\]/ then "warn" - when /^\[Info\]/ then "warn" - when /^\[Success\]/ then "success" - when /^\[Terminating\]/ then "term" - when /^\[Nonterminating\]/ then "noterm" - when /\[Debug\]/ then next # debug "warnings" shouldn't count as other warnings (against NOWARN) - when /^ on line \d+ $/ then next # dead line warnings shouldn't count (used for unreachability with NOWARN) - when /^ on lines \d+..\d+ $/ then next # dead line warnings shouldn't count (used for unreachability with NOWARN) - else "other" - end + ranking = ["other", "warn", "local_term", "non_local_term", "race", "norace", "deadlock", "nodeadlock", "success", "fail", "unknown", "term", "noterm"] + thiswarn = case obj + when /\(conf\. \d+\)/ then "race" + when /Deadlock/ then "deadlock" + when /lock (before|after):/ then "deadlock" + when /Assertion .* will fail/ then "fail" + when /Assertion .* will succeed/ then "success" + when /Assertion .* is unknown/ then "unknown" + when /invariant confirmed/ then "success" + when /invariant unconfirmed/ then "unknown" + when /invariant refuted/ then "fail" + when /^\[Warning\]/ then "warn" + when /^\[Error\]/ then "warn" + when /^\[Info\]/ then "warn" + when /^\[Success\]/ then "success" + when /\[Debug\]/ then next # debug "warnings" shouldn't count as other warnings (against NOWARN) + when /^ on line \d+ $/ then next # dead line warnings shouldn't count (used for unreachability with NOWARN) + when /^ on lines \d+..\d+ $/ then next # dead line warnings shouldn't count (used for unreachability with NOWARN) + else "other" + end oldwarn = warnings[i] if oldwarn.nil? then warnings[i] = thiswarn @@ -189,17 +188,23 @@ def compare_warnings # full p.path is too long and p.name does not allow click to open in terminal if todo.include? idx then puts "Excellent: ignored check on #{relpath(p.path).to_s.cyan}:#{idx.to_s.blue} is now passing!" end else - if todo.include? idx then @ignored += 1 else - puts "Expected #{type.yellow}, but registered #{(warnings[idx] or "nothing").yellow} on #{p.name.cyan}:#{idx.to_s.blue}" - puts tests_line[idx].rstrip.gray - ferr = idx if ferr.nil? or idx < ferr + if todo.include? idx then + @ignored += 1 + else + if idx < 0 # When non line specific keywords were used don't print a line + puts "Expected #{type.yellow}, but registered #{(warnings[idx] or "nothing").yellow} on #{p.name.cyan}" + else + puts "Expected #{type.yellow}, but registered #{(warnings[idx] or "nothing").yellow} on #{p.name.cyan}:#{idx.to_s.blue}" + puts tests_line[idx].rstrip.gray + ferr = idx if ferr.nil? or idx < ferr + end end end } case type - when "deadlock", "race", "fail", "noterm", "unknown", "term", "warn" + when "deadlock", "race", "fail", "noterm", "unknown", "term", "warn", "non_local_term" check.call warnings[idx] == type - when "nowarn" + when "nowarn", "local_term" check.call warnings[idx].nil? when "assert", "success" check.call warnings[idx] == "success" @@ -300,10 +305,6 @@ def parse_tests (lines) tests[i] = "fail" elsif obj =~ /UNKNOWN/ then tests[i] = "unknown" - elsif obj =~ /NON?TERM/ then - tests[i] = "noterm" - elsif obj =~ /TERM/ then - tests[i] = "term" elsif obj =~ /(assert|__goblint_check).*\(/ then if obj =~ /FAIL/ then tests[i] = "fail" @@ -315,6 +316,12 @@ def parse_tests (lines) end end case lines[0] + when /NON_LOCAL_TERM/ + # covers "TERM" as keyword but a combined use of NON_LOCAL_TERM (loop termination) and TERM would be pointless + tests[-2] = "non_local_term" + when /LOCAL_TERM/ + # covers "TERM" as keyword but a combined use of NON_LOCAL_TERM (loop termination) and TERM would be pointless + tests[-2] = "local_term" when /NON?TERM/ tests[-1] = "noterm" when /TERM/ diff --git a/tests/regression/80-termination/01-simple-loop-terminating.c b/tests/regression/80-termination/01-simple-loop-terminating.c index 931b125171..a517d0d608 100644 --- a/tests/regression/80-termination/01-simple-loop-terminating.c +++ b/tests/regression/80-termination/01-simple-loop-terminating.c @@ -1,11 +1,11 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { int i = 1; - while (i <= 10) // TERM + while (i <= 10) { printf("%d\n", i); i++; diff --git a/tests/regression/80-termination/02-simple-loop-nonterminating.c b/tests/regression/80-termination/02-simple-loop-nonterminating.c index 520a4a82e0..bcb9909f80 100644 --- a/tests/regression/80-termination/02-simple-loop-nonterminating.c +++ b/tests/regression/80-termination/02-simple-loop-nonterminating.c @@ -1,9 +1,9 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { - while (1) // NOTERM + while (1) { continue; } diff --git a/tests/regression/80-termination/03-nested-loop-terminating.c b/tests/regression/80-termination/03-nested-loop-terminating.c index 172827af42..366cbaeea5 100644 --- a/tests/regression/80-termination/03-nested-loop-terminating.c +++ b/tests/regression/80-termination/03-nested-loop-terminating.c @@ -1,4 +1,4 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() @@ -9,12 +9,12 @@ int main() // Outer while loop for rows while (i <= rows) - { // TERM + { int j = 1; // Inner while loop for columns while (j <= columns) - { // TERM + { printf("(%d, %d) ", i, j); j++; } diff --git a/tests/regression/80-termination/04-nested-loop-nonterminating.c b/tests/regression/80-termination/04-nested-loop-nonterminating.c index 37af9ed6fb..ee2aa4a8c4 100644 --- a/tests/regression/80-termination/04-nested-loop-nonterminating.c +++ b/tests/regression/80-termination/04-nested-loop-nonterminating.c @@ -1,15 +1,15 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { int outerCount = 1; - while (outerCount <= 3) // NOTERM + while (outerCount <= 3) { int innerCount = 1; - while (1) // NOTERM + while (1) { printf("(%d, %d) ", outerCount, innerCount); innerCount++; diff --git a/tests/regression/80-termination/05-for-loop-terminating.c b/tests/regression/80-termination/05-for-loop-terminating.c index ab286a6dd4..2a16184f6d 100644 --- a/tests/regression/80-termination/05-for-loop-terminating.c +++ b/tests/regression/80-termination/05-for-loop-terminating.c @@ -1,11 +1,11 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { int i; - for (i = 1; i <= 10; i++) // TERM + for (i = 1; i <= 10; i++) { printf("%d\n", i); } diff --git a/tests/regression/80-termination/06-for-loop-nonterminating.c b/tests/regression/80-termination/06-for-loop-nonterminating.c index 466001e6e5..b8f30361d1 100644 --- a/tests/regression/80-termination/06-for-loop-nonterminating.c +++ b/tests/regression/80-termination/06-for-loop-nonterminating.c @@ -1,8 +1,8 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { - for (;;) { // NOTERM + for (;;) { printf("This loop does not terminate.\n"); } diff --git a/tests/regression/80-termination/07-nested-for-loop-terminating.c b/tests/regression/80-termination/07-nested-for-loop-terminating.c index eec4dda908..def0787d39 100644 --- a/tests/regression/80-termination/07-nested-for-loop-terminating.c +++ b/tests/regression/80-termination/07-nested-for-loop-terminating.c @@ -1,4 +1,4 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() @@ -7,9 +7,9 @@ int main() int columns = 4; // Nested loop to iterate over rows and columns - for (int i = 1; i <= rows; i++) // TERM + for (int i = 1; i <= rows; i++) { - for (int j = 1; j <= columns; j++) // TERM + for (int j = 1; j <= columns; j++) { printf("(%d, %d) ", i, j); } diff --git a/tests/regression/80-termination/08-nested-for-loop-nonterminating.c b/tests/regression/80-termination/08-nested-for-loop-nonterminating.c index 3f7bcb4f07..0368120b13 100644 --- a/tests/regression/80-termination/08-nested-for-loop-nonterminating.c +++ b/tests/regression/80-termination/08-nested-for-loop-nonterminating.c @@ -1,13 +1,13 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { int outerCount, innerCount; - for (outerCount = 1; outerCount <= 3; outerCount++) // NOTERM + for (outerCount = 1; outerCount <= 3; outerCount++) { - for (innerCount = 1;; innerCount++) // NOTERM + for (innerCount = 1;; innerCount++) { printf("(%d, %d) ", outerCount, innerCount); } diff --git a/tests/regression/80-termination/09-complex-for-loop-terminating.c b/tests/regression/80-termination/09-complex-for-loop-terminating.c index ed28fa9b43..ae68b11575 100644 --- a/tests/regression/80-termination/09-complex-for-loop-terminating.c +++ b/tests/regression/80-termination/09-complex-for-loop-terminating.c @@ -1,4 +1,4 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() @@ -6,17 +6,17 @@ int main() int i, j, k; // Outer loop - for (i = 1; i <= 5; i++) // TERM + for (i = 1; i <= 5; i++) { // Inner loop 1 - for (j = 1; j <= i; j++) // TERM + for (j = 1; j <= i; j++) { printf("%d ", j); } printf("\n"); // Inner loop 2 - for (k = i; k >= 1; k--) // TERM + for (k = i; k >= 1; k--) { printf("%d ", k); } @@ -24,9 +24,9 @@ int main() } // Additional loop - for (i = 5; i >= 1; i--) // TERM + for (i = 5; i >= 1; i--) { - for (j = i; j >= 1; j--) // TERM + for (j = i; j >= 1; j--) { printf("%d ", j); } @@ -34,7 +34,7 @@ int main() } // Loop with conditions - for (i = 1; i <= 10; i++) // TERM + for (i = 1; i <= 10; i++) { if (i % 2 == 0) { @@ -47,7 +47,7 @@ int main() } // Loop with nested conditions - for (i = 1; i <= 10; i++) // TERM + for (i = 1; i <= 10; i++) { printf("Number: %d - ", i); if (i < 5) @@ -65,7 +65,7 @@ int main() } // Loop with a break statement - for (i = 1; i <= 10; i++) // TERM + for (i = 1; i <= 10; i++) { printf("%d ", i); if (i == 5) @@ -76,7 +76,7 @@ int main() printf("\n"); // Loop with a continue statement - for (i = 1; i <= 10; i++) // TERM + for (i = 1; i <= 10; i++) { if (i % 2 == 0) { @@ -87,7 +87,7 @@ int main() printf("\n"); // Loop with complex conditions - for (i = 1; i <= 10; i++) // TERM + for (i = 1; i <= 10; i++) { if (i > 5 && i % 2 == 0) { @@ -98,7 +98,7 @@ int main() // Loop with multiple variables int a, b, c; - for (a = 1, b = 2, c = 3; a <= 10; a++, b += 2, c += 3) // TERM + for (a = 1, b = 2, c = 3; a <= 10; a++, b += 2, c += 3) { printf("%d %d %d\n", a, b, c); } diff --git a/tests/regression/80-termination/10-complex-loop-terminating.c b/tests/regression/80-termination/10-complex-loop-terminating.c index 3a19f17bee..eb79338078 100644 --- a/tests/regression/80-termination/10-complex-loop-terminating.c +++ b/tests/regression/80-termination/10-complex-loop-terminating.c @@ -1,4 +1,4 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() @@ -8,10 +8,10 @@ int main() int k = 5; // Outer while loop - while (i <= 5) // TERM + while (i <= 5) { // Inner while loop 1 - while (j <= i) // TERM + while (j <= i) { printf("%d ", j); j++; @@ -20,7 +20,7 @@ int main() j = 1; // Inner while loop 2 - while (k >= 1) // TERM + while (k >= 1) { printf("%d ", k); k--; @@ -33,10 +33,10 @@ int main() // Additional while loop i = 5; - while (i >= 1) // TERM + while (i >= 1) { j = i; - while (j >= 1) // TERM + while (j >= 1) { printf("%d ", j); j--; @@ -47,7 +47,7 @@ int main() // Loop with conditions i = 1; - while (i <= 10) // TERM + while (i <= 10) { if (i % 2 == 0) { @@ -62,7 +62,7 @@ int main() // Loop with nested conditions i = 1; - while (i <= 10) // TERM + while (i <= 10) { printf("Number: %d - ", i); if (i < 5) @@ -82,7 +82,7 @@ int main() // Loop with a break statement i = 1; - while (i <= 10) // TERM + while (i <= 10) { printf("%d ", i); if (i == 5) @@ -95,7 +95,7 @@ int main() // Loop with a continue statement i = 1; - while (i <= 10) // TERM + while (i <= 10) { if (i % 2 == 0) { @@ -109,7 +109,7 @@ int main() // Loop with complex conditions i = 1; - while (i <= 10) // TERM + while (i <= 10) { if (i > 5 && i % 2 == 0) { @@ -123,7 +123,7 @@ int main() int a = 1; int b = 2; int c = 3; - while (a <= 10) // TERM + while (a <= 10) { printf("%d %d %d\n", a, b, c); a++; diff --git a/tests/regression/80-termination/11-loopless-termination.c b/tests/regression/80-termination/11-loopless-termination.c index 7aeed0145d..86a300f18e 100644 --- a/tests/regression/80-termination/11-loopless-termination.c +++ b/tests/regression/80-termination/11-loopless-termination.c @@ -1,4 +1,4 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/80-termination/12-do-while-instant-terminating.c b/tests/regression/80-termination/12-do-while-instant-terminating.c index cc3cc41edc..15032b7b4f 100644 --- a/tests/regression/80-termination/12-do-while-instant-terminating.c +++ b/tests/regression/80-termination/12-do-while-instant-terminating.c @@ -1,11 +1,11 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { int i = 0; - do // TERM + do { printf("Inside the do-while loop\n"); } while (i > 0); diff --git a/tests/regression/80-termination/13-do-while-terminating.c b/tests/regression/80-termination/13-do-while-terminating.c index 05fe270f04..2e04f3e393 100644 --- a/tests/regression/80-termination/13-do-while-terminating.c +++ b/tests/regression/80-termination/13-do-while-terminating.c @@ -1,11 +1,11 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { int i = 1; - do // TERM + do { printf("Inside the do-while loop\n"); i++; diff --git a/tests/regression/80-termination/14-do-while-nonterminating.c b/tests/regression/80-termination/14-do-while-nonterminating.c index 1c70d4fc76..5ed18175e9 100644 --- a/tests/regression/80-termination/14-do-while-nonterminating.c +++ b/tests/regression/80-termination/14-do-while-nonterminating.c @@ -1,11 +1,11 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { int i = 1; - do // NOTERM + do { printf("Inside the do-while loop\n"); i++; diff --git a/tests/regression/80-termination/15-complex-loop-combination-terminating.c b/tests/regression/80-termination/15-complex-loop-combination-terminating.c index e5383aed66..f3a08f4139 100644 --- a/tests/regression/80-termination/15-complex-loop-combination-terminating.c +++ b/tests/regression/80-termination/15-complex-loop-combination-terminating.c @@ -1,4 +1,4 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() @@ -7,14 +7,14 @@ int main() int i; // for loop - for (i = 1; i <= 10; i++) // TERM + for (i = 1; i <= 10; i++) { printf("For loop iteration: %d\n", i); } // while loop int j = 1; - while (j <= 10) // TERM + while (j <= 10) { printf("While loop iteration: %d\n", j); j++; @@ -22,7 +22,7 @@ int main() // do-while loop int k = 1; - do // TERM + do { printf("Do-While loop iteration: %d\n", k); k++; @@ -32,10 +32,10 @@ int main() int a, b; // Nested for and while loop - for (a = 1; a <= 5; a++) // TERM + for (a = 1; a <= 5; a++) { int c = 1; - while (c <= a) // TERM + while (c <= a) { printf("Nested For-While loop: %d\n", c); c++; @@ -44,10 +44,10 @@ int main() // Nested while and do-while loop int x = 1; - while (x <= 5) // TERM + while (x <= 5) { int y = 1; - do // TERM + do { printf("Nested While-Do-While loop: %d\n", y); y++; @@ -57,9 +57,9 @@ int main() // Nested do-while and for loop int p = 1; - do // TERM + do { - for (int q = 1; q <= p; q++) // TERM + for (int q = 1; q <= p; q++) { printf("Nested Do-While-For loop: %d\n", q); } @@ -71,11 +71,11 @@ int main() // Nested while loop with a break statement int n = 1; - while (n <= 5) // TERM + while (n <= 5) { printf("Outer While loop iteration: %d\n", n); m = 1; - while (1) // TERM + while (1) { printf("Inner While loop iteration: %d\n", m); m++; @@ -88,7 +88,7 @@ int main() } // Loop with a continue statement - for (int r = 1; r <= 10; r++) // TERM + for (int r = 1; r <= 10; r++) { if (r % 3 == 0) { @@ -99,7 +99,7 @@ int main() // Loop with multiple conditions int s = 1; - while (s <= 10 && s % 2 == 0) // TERM + while (s <= 10 && s % 2 == 0) { printf("Loop with Multiple Conditions: %d\n", s); s++; @@ -107,13 +107,13 @@ int main() // Loop with multiple variables int t, u; - for (t = 1, u = 10; t <= 5 && u >= 5; t++, u--) // TERM + for (t = 1, u = 10; t <= 5 && u >= 5; t++, u--) { printf("Loop with Multiple Variables: %d %d\n", t, u); } // Loop with nested conditions - for (int v = 1; v <= 10; v++) // TERM + for (int v = 1; v <= 10; v++) { printf("Loop with Nested Conditions: %d - ", v); if (v < 5) @@ -137,7 +137,7 @@ int main() { printf("Loop with Label and Goto: %d\n", w); w++; - goto start; // TERM + goto start; } return 0; diff --git a/tests/regression/80-termination/16-nested-loop-nontrivial-nonterminating.c b/tests/regression/80-termination/16-nested-loop-nontrivial-nonterminating.c index 855fbd0dca..5ff890e461 100644 --- a/tests/regression/80-termination/16-nested-loop-nontrivial-nonterminating.c +++ b/tests/regression/80-termination/16-nested-loop-nontrivial-nonterminating.c @@ -1,15 +1,15 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { int outerCount = 1; - while (outerCount <= 3) // NOTERM + while (outerCount <= 3) { int innerCount = 1; - while (outerCount < 3 || innerCount > 0) // NOTERM + while (outerCount < 3 || innerCount > 0) { printf("(%d, %d) ", outerCount, innerCount); innerCount++; diff --git a/tests/regression/80-termination/17-goto-terminating.c b/tests/regression/80-termination/17-goto-terminating.c index dcf72552bc..b66fbe57ea 100644 --- a/tests/regression/80-termination/17-goto-terminating.c +++ b/tests/regression/80-termination/17-goto-terminating.c @@ -1,11 +1,11 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { int num = 1; -loop: // TERM +loop: printf("Current number: %d\n", num); num++; diff --git a/tests/regression/80-termination/18-goto-nonterminating.c b/tests/regression/80-termination/18-goto-nonterminating.c index 672128e009..cfe5ab481d 100644 --- a/tests/regression/80-termination/18-goto-nonterminating.c +++ b/tests/regression/80-termination/18-goto-nonterminating.c @@ -1,11 +1,11 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { int num = 1; -loop: // NOTERM +loop: printf("Current number: %d\n", num); num++; diff --git a/tests/regression/80-termination/19-rand-terminating.c b/tests/regression/80-termination/19-rand-terminating.c index 879ae3748a..426c5cdcca 100644 --- a/tests/regression/80-termination/19-rand-terminating.c +++ b/tests/regression/80-termination/19-rand-terminating.c @@ -1,4 +1,4 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include #include #include @@ -11,7 +11,7 @@ int main() if (rand()) { // Loop inside the if part - for (int i = 1; i <= 5; i++) // TERM + for (int i = 1; i <= 5; i++) { printf("Loop inside if part: %d\n", i); } @@ -20,7 +20,7 @@ int main() { // Loop inside the else part int j = 1; - while (j <= 5) // TERM + while (j <= 5) { printf("Loop inside else part: %d\n", j); j++; diff --git a/tests/regression/80-termination/20-rand-nonterminating.c b/tests/regression/80-termination/20-rand-nonterminating.c index 27c3f2c388..7c21538612 100644 --- a/tests/regression/80-termination/20-rand-nonterminating.c +++ b/tests/regression/80-termination/20-rand-nonterminating.c @@ -1,4 +1,4 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include #include #include @@ -11,7 +11,7 @@ int main() if (rand()) { // Loop inside the if part - for (int i = 1; i >= 0; i++) // NOTERM + for (int i = 1; i >= 0; i++) { printf("Loop inside if part: %d\n", i); } @@ -20,7 +20,7 @@ int main() { // Loop inside the else part int j = 1; - while (j > 0) // NOTERM + while (j > 0) { printf("Loop inside else part: %d\n", j); } diff --git a/tests/regression/80-termination/21-no-exit-on-rand-unproofable.c b/tests/regression/80-termination/21-no-exit-on-rand-unproofable.c index 0edafe0f65..f54af1da7c 100644 --- a/tests/regression/80-termination/21-no-exit-on-rand-unproofable.c +++ b/tests/regression/80-termination/21-no-exit-on-rand-unproofable.c @@ -1,10 +1,11 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { int forever, i = 0; +// This loop is not provable, therefore it should throw a warning while (i < 4 || forever == 1) { i++; diff --git a/tests/regression/80-termination/22-exit-on-rand-unproofable.c b/tests/regression/80-termination/22-exit-on-rand-unproofable.c index 5c270f3b2a..1bc104258d 100644 --- a/tests/regression/80-termination/22-exit-on-rand-unproofable.c +++ b/tests/regression/80-termination/22-exit-on-rand-unproofable.c @@ -1,10 +1,11 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { int forever = 1; +// This loop is not provable, therefore it should throw a warning while (forever == 1) { if (rand()) //May exit, may not diff --git a/tests/regression/80-termination/23-exit-on-rand-terminating.c b/tests/regression/80-termination/23-exit-on-rand-terminating.c index 228fc3b15e..9d000069e1 100644 --- a/tests/regression/80-termination/23-exit-on-rand-terminating.c +++ b/tests/regression/80-termination/23-exit-on-rand-terminating.c @@ -1,4 +1,4 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include #include diff --git a/tests/regression/80-termination/24-upjumping-goto-loopless-terminating.c b/tests/regression/80-termination/24-upjumping-goto-loopless-terminating.c index f4b6b8fbe2..a774f70457 100644 --- a/tests/regression/80-termination/24-upjumping-goto-loopless-terminating.c +++ b/tests/regression/80-termination/24-upjumping-goto-loopless-terminating.c @@ -1,4 +1,4 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/80-termination/25-leave-loop-goto-terminating.c b/tests/regression/80-termination/25-leave-loop-goto-terminating.c index c30e65f44b..28d8824535 100644 --- a/tests/regression/80-termination/25-leave-loop-goto-terminating.c +++ b/tests/regression/80-termination/25-leave-loop-goto-terminating.c @@ -1,4 +1,4 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/80-termination/26-enter-loop-goto-terminating.c b/tests/regression/80-termination/26-enter-loop-goto-terminating.c index 5d34e5c523..41bf44c44e 100644 --- a/tests/regression/80-termination/26-enter-loop-goto-terminating.c +++ b/tests/regression/80-termination/26-enter-loop-goto-terminating.c @@ -1,4 +1,4 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/80-termination/27-upjumping-goto-nonterminating.c b/tests/regression/80-termination/27-upjumping-goto-nonterminating.c index 6e4432dc5e..a230827356 100644 --- a/tests/regression/80-termination/27-upjumping-goto-nonterminating.c +++ b/tests/regression/80-termination/27-upjumping-goto-nonterminating.c @@ -1,4 +1,4 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/81-recursion/01-simple-terminating.c b/tests/regression/81-recursion/01-simple-terminating.c index 1c52faec68..4f09950025 100644 --- a/tests/regression/81-recursion/01-simple-terminating.c +++ b/tests/regression/81-recursion/01-simple-terminating.c @@ -1,4 +1,4 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include void recursiveFunction(int n) { diff --git a/tests/regression/81-recursion/02-simple-nonterminating.c b/tests/regression/81-recursion/02-simple-nonterminating.c index a6d6b3ab17..3ed0b75a0d 100644 --- a/tests/regression/81-recursion/02-simple-nonterminating.c +++ b/tests/regression/81-recursion/02-simple-nonterminating.c @@ -1,4 +1,4 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include void recursiveFunction(int n) { diff --git a/tests/regression/81-recursion/03-nested-terminating.c b/tests/regression/81-recursion/03-nested-terminating.c index 096a1b0121..23bedef644 100644 --- a/tests/regression/81-recursion/03-nested-terminating.c +++ b/tests/regression/81-recursion/03-nested-terminating.c @@ -1,4 +1,4 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include void innerRecursiveFunction(int n) { diff --git a/tests/regression/81-recursion/04-nested-nonterminating.c b/tests/regression/81-recursion/04-nested-nonterminating.c index ab5e35d80f..d28685e139 100644 --- a/tests/regression/81-recursion/04-nested-nonterminating.c +++ b/tests/regression/81-recursion/04-nested-nonterminating.c @@ -1,4 +1,4 @@ -// PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include void innerRecursiveFunction() { From 7a6b18cbe37d21f9cff1ca0797b2cbc5088da804 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Wed, 21 Jun 2023 14:07:23 +0200 Subject: [PATCH 0183/1312] reverted test case --- output.txt | 464 ++++++++++++++++++++++++++--------------------------- 1 file changed, 231 insertions(+), 233 deletions(-) diff --git a/output.txt b/output.txt index d751966836..37eb310726 100644 --- a/output.txt +++ b/output.txt @@ -131,7 +131,7 @@ typedef long __intptr_t; typedef unsigned int __socklen_t; #line 215 "/usr/include/x86_64-linux-gnu/bits/types.h" typedef int __sig_atomic_t; -#line 209 "/usr/lib/gcc/x86_64-linux-gnu/10/include/stddef.h" +#line 209 "/usr/lib/gcc/x86_64-linux-gnu/11/include/stddef.h" typedef unsigned long size_t; #line 10 "/usr/include/x86_64-linux-gnu/bits/types/time_t.h" typedef __time_t time_t; @@ -201,12 +201,12 @@ struct __anonstruct___value32_817613185 { unsigned int __high ; }; #line 25 "/usr/include/x86_64-linux-gnu/bits/atomic_wide_counter.h" -union __anonunion___atomic_wide_counter_1044835921 { +union __anonunion___atomic_wide_counter_316368393 { unsigned long long __value64 ; struct __anonstruct___value32_817613185 __value32 ; }; #line 25 "/usr/include/x86_64-linux-gnu/bits/atomic_wide_counter.h" -typedef union __anonunion___atomic_wide_counter_1044835921 __atomic_wide_counter; +typedef union __anonunion___atomic_wide_counter_316368393 __atomic_wide_counter; #line 51 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" struct __pthread_internal_list { struct __pthread_internal_list *__prev ; @@ -261,11 +261,11 @@ typedef unsigned int __tss_t; #line 106 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" typedef unsigned long __thrd_t; #line 108 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" -struct __anonstruct___once_flag_1044835922 { +struct __anonstruct___once_flag_826868709 { int __data ; }; #line 108 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" -typedef struct __anonstruct___once_flag_1044835922 __once_flag; +typedef struct __anonstruct___once_flag_826868709 __once_flag; #line 27 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" typedef unsigned long pthread_t; #line 32 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" @@ -441,16 +441,16 @@ struct __pthread_cleanup_frame { int __do_it ; int __cancel_type ; }; -#line 143 "/usr/lib/gcc/x86_64-linux-gnu/10/include/stddef.h" +#line 143 "/usr/lib/gcc/x86_64-linux-gnu/11/include/stddef.h" typedef long ptrdiff_t; -#line 321 "/usr/lib/gcc/x86_64-linux-gnu/10/include/stddef.h" +#line 321 "/usr/lib/gcc/x86_64-linux-gnu/11/include/stddef.h" typedef int wchar_t; -#line 415 "/usr/lib/gcc/x86_64-linux-gnu/10/include/stddef.h" +#line 415 "/usr/lib/gcc/x86_64-linux-gnu/11/include/stddef.h" struct __anonstruct_max_align_t_896270833 { long long __max_align_ll __attribute__((__aligned__(__alignof__(long long )))) ; long double __max_align_ld __attribute__((__aligned__(__alignof__(long double )))) ; }; -#line 415 "/usr/lib/gcc/x86_64-linux-gnu/10/include/stddef.h" +#line 415 "/usr/lib/gcc/x86_64-linux-gnu/11/include/stddef.h" typedef struct __anonstruct_max_align_t_896270833 max_align_t; /* compiler builtin: void __builtin_va_copy(__builtin_va_list , __builtin_va_list ) ; */ @@ -1115,496 +1115,494 @@ void example1(void) } #line 27 term27_5-file_01-simple-cases ++; +#line 27 + term_exit- = term27_5-file_01-simple-cases; #line 28 a[i] = i; -#line 29 - i ++; } while_break: /* CIL Label */ ; } -#line 27 - term_exit- = term27_5-file_01-simple-cases; } -#line 32 +#line 31 __goblint_check(a[0] == 0); -#line 33 +#line 32 __goblint_check(a[3] == 3); -#line 34 +#line 33 return; } } -#line 37 "tests/regression/55-loop-unrolling/01-simple-cases.c" +#line 36 "tests/regression/55-loop-unrolling/01-simple-cases.c" void example2(void) { int a[5] ; int i ; - int term42_5-file_01-simple-cases ; + int term41_5-file_01-simple-cases ; { -#line 40 +#line 39 i = 0; { -#line 42 - term42_5-file_01-simple-cases = 0; +#line 41 + term41_5-file_01-simple-cases = 0; { -#line 42 +#line 41 while (1) { while_continue: /* CIL Label */ ; -#line 43 +#line 42 a[i] = i; -#line 44 +#line 43 i ++; -#line 42 - term42_5-file_01-simple-cases ++; -#line 42 +#line 41 + term41_5-file_01-simple-cases ++; +#line 41 + term_exit- = term41_5-file_01-simple-cases; +#line 41 if (! (i <= 5)) { -#line 42 +#line 41 goto while_break; } } while_break: /* CIL Label */ ; } -#line 42 - term_exit- = term42_5-file_01-simple-cases; } -#line 47 +#line 46 __goblint_check(a[0] == 0); -#line 48 +#line 47 __goblint_check(a[3] == 3); -#line 49 +#line 48 return; } } -#line 52 "tests/regression/55-loop-unrolling/01-simple-cases.c" +#line 51 "tests/regression/55-loop-unrolling/01-simple-cases.c" void example3(void) { int a[10] ; int i ; - int term57_5-file_01-simple-cases ; + int term56_5-file_01-simple-cases ; { -#line 55 +#line 54 i = 0; { -#line 57 - term57_5-file_01-simple-cases = 0; +#line 56 + term56_5-file_01-simple-cases = 0; { -#line 57 +#line 56 while (1) { while_continue: /* CIL Label */ ; -#line 57 +#line 56 if (! (i < 5)) { -#line 57 +#line 56 goto while_break; } +#line 56 + term56_5-file_01-simple-cases ++; +#line 56 + term_exit- = term56_5-file_01-simple-cases; #line 57 - term57_5-file_01-simple-cases ++; -#line 58 a[i] = i; -#line 59 +#line 58 i ++; } while_break: /* CIL Label */ ; } -#line 57 - term_exit- = term57_5-file_01-simple-cases; } -#line 62 +#line 61 __goblint_check(a[0] == 0); -#line 63 +#line 62 __goblint_check(a[3] == 0); -#line 64 +#line 63 __goblint_check(a[7] == 0); -#line 65 +#line 64 return; } } -#line 68 "tests/regression/55-loop-unrolling/01-simple-cases.c" +#line 67 "tests/regression/55-loop-unrolling/01-simple-cases.c" void example4(void) { int a[10] ; int i ; int first_iteration ; - int term74_5-file_01-simple-cases ; + int term73_5-file_01-simple-cases ; { -#line 71 +#line 70 i = 0; -#line 72 +#line 71 first_iteration = 1; { -#line 74 - term74_5-file_01-simple-cases = 0; +#line 73 + term73_5-file_01-simple-cases = 0; { -#line 74 +#line 73 while (1) { while_continue: /* CIL Label */ ; -#line 74 +#line 73 if (! (i < 10)) { -#line 74 +#line 73 goto while_break; } +#line 73 + term73_5-file_01-simple-cases ++; +#line 73 + term_exit- = term73_5-file_01-simple-cases; #line 74 - term74_5-file_01-simple-cases ++; -#line 75 if (first_iteration == 1) { -#line 75 +#line 74 __goblint_check(i == 0); } else -#line 76 +#line 75 if (i > 5) { -#line 76 +#line 75 __goblint_check(i == 6); } -#line 77 +#line 76 first_iteration = 0; -#line 78 +#line 77 a[i] = 0; -#line 79 +#line 78 i ++; } while_break: /* CIL Label */ ; } -#line 74 - term_exit- = term74_5-file_01-simple-cases; } -#line 82 +#line 81 __goblint_check(a[0] == 0); -#line 83 +#line 82 __goblint_check(first_iteration == 0); -#line 84 +#line 83 return; } } -#line 89 "tests/regression/55-loop-unrolling/01-simple-cases.c" +#line 88 "tests/regression/55-loop-unrolling/01-simple-cases.c" void example5(void) { int a[4] ; int i ; int top ; - int term95_5-file_01-simple-cases ; + int term94_5-file_01-simple-cases ; { -#line 92 +#line 91 i = 0; -#line 93 +#line 92 top = 0; { -#line 95 - term95_5-file_01-simple-cases = 0; +#line 94 + term94_5-file_01-simple-cases = 0; { -#line 95 +#line 94 while (1) { while_continue: /* CIL Label */ ; -#line 95 +#line 94 if (! (i < 4)) { -#line 95 +#line 94 goto while_break; } +#line 94 + term94_5-file_01-simple-cases ++; +#line 94 + term_exit- = term94_5-file_01-simple-cases; #line 95 - term95_5-file_01-simple-cases ++; -#line 96 a[i] = 0; -#line 97 +#line 96 top += i; -#line 98 +#line 97 if (i == 2) { -#line 99 +#line 98 __goblint_check(top == 3); } else { -#line 102 +#line 101 __goblint_check(top == 3); } -#line 104 +#line 103 i ++; } while_break: /* CIL Label */ ; } -#line 95 - term_exit- = term95_5-file_01-simple-cases; } -#line 107 +#line 106 __goblint_check(a[0] == 0); -#line 108 +#line 107 __goblint_check(a[3] == 0); -#line 109 +#line 108 __goblint_check(top == 6); -#line 110 +#line 109 return; } } -#line 113 "tests/regression/55-loop-unrolling/01-simple-cases.c" +#line 112 "tests/regression/55-loop-unrolling/01-simple-cases.c" void example6(void) { int a[5] ; int i ; int top ; - int term119_5-file_01-simple-cases ; + int term118_5-file_01-simple-cases ; { -#line 116 +#line 115 i = 0; -#line 117 +#line 116 top = 0; { -#line 119 - term119_5-file_01-simple-cases = 0; +#line 118 + term118_5-file_01-simple-cases = 0; { -#line 119 +#line 118 while (1) { while_continue: /* CIL Label */ ; -#line 119 +#line 118 if (! (i < 3)) { -#line 119 +#line 118 goto while_break; } +#line 118 + term118_5-file_01-simple-cases ++; +#line 118 + term_exit- = term118_5-file_01-simple-cases; #line 119 - term119_5-file_01-simple-cases ++; -#line 120 a[i] = 0; -#line 121 +#line 120 __goblint_check(a[0] == 0); -#line 122 +#line 121 i ++; } while_break: /* CIL Label */ ; } -#line 119 - term_exit- = term119_5-file_01-simple-cases; } -#line 125 +#line 124 __goblint_check(a[0] == 0); -#line 126 +#line 125 __goblint_check(a[3] == 0); -#line 127 +#line 126 __goblint_check(top == 6); -#line 128 +#line 127 return; } } -#line 131 "tests/regression/55-loop-unrolling/01-simple-cases.c" +#line 130 "tests/regression/55-loop-unrolling/01-simple-cases.c" int update(int i ) { { -#line 132 +#line 131 if (i > 5) { -#line 133 +#line 132 return (0); } else { -#line 136 +#line 135 return (1); } } } -#line 139 "tests/regression/55-loop-unrolling/01-simple-cases.c" +#line 138 "tests/regression/55-loop-unrolling/01-simple-cases.c" void example7(void) { int a[10] ; int i ; int tmp ; - int term143_2-file_01-simple-cases ; + int term142_2-file_01-simple-cases ; { -#line 142 +#line 141 i = 0; { -#line 143 - term143_2-file_01-simple-cases = 0; +#line 142 + term142_2-file_01-simple-cases = 0; { -#line 143 +#line 142 while (1) { while_continue: /* CIL Label */ ; -#line 143 +#line 142 tmp = update(i); -#line 143 - term143_2-file_01-simple-cases ++; -#line 143 +#line 142 + term142_2-file_01-simple-cases ++; +#line 142 + term_exit- = term142_2-file_01-simple-cases; +#line 142 if (! tmp) { -#line 143 +#line 142 goto while_break; } -#line 144 +#line 143 a[i] = i; -#line 145 +#line 144 i ++; } while_break: /* CIL Label */ ; } -#line 143 - term_exit- = term143_2-file_01-simple-cases; } -#line 147 +#line 146 __goblint_check(a[0] == 0); -#line 148 +#line 147 __goblint_check(a[6] == 0); -#line 149 +#line 148 return; } } -#line 152 "tests/regression/55-loop-unrolling/01-simple-cases.c" +#line 151 "tests/regression/55-loop-unrolling/01-simple-cases.c" void example8(void) { int a[5] ; int b[5] ; int i ; int j ; - int term160_9-file_01-simple-cases ; - int term157_2-file_01-simple-cases ; + int term159_9-file_01-simple-cases ; + int term156_2-file_01-simple-cases ; { -#line 155 +#line 154 b[0] = 0; -#line 155 +#line 154 b[1] = 0; -#line 155 +#line 154 b[2] = 0; -#line 155 +#line 154 b[3] = 0; -#line 155 +#line 154 b[4] = 0; -#line 156 +#line 155 i = 0; { -#line 157 - term157_2-file_01-simple-cases = 0; +#line 156 + term156_2-file_01-simple-cases = 0; { -#line 157 +#line 156 while (1) { while_continue: /* CIL Label */ ; -#line 157 +#line 156 if (! (i < 5)) { -#line 157 +#line 156 goto while_break; } +#line 156 + term156_2-file_01-simple-cases ++; +#line 156 + term_exit- = term156_2-file_01-simple-cases; #line 157 - term157_2-file_01-simple-cases ++; -#line 158 a[i] = i; -#line 159 +#line 158 j = 0; { -#line 160 - term160_9-file_01-simple-cases = 0; +#line 159 + term159_9-file_01-simple-cases = 0; { -#line 160 +#line 159 while (1) { while_continue___0: /* CIL Label */ ; -#line 160 +#line 159 if (! (j < 5)) { -#line 160 +#line 159 goto while_break___0; } +#line 159 + term159_9-file_01-simple-cases ++; +#line 159 + term_exit- = term159_9-file_01-simple-cases; #line 160 - term160_9-file_01-simple-cases ++; -#line 161 b[j] += a[i]; -#line 162 +#line 161 j ++; } while_break___0: /* CIL Label */ ; } -#line 160 - term_exit- = term160_9-file_01-simple-cases; } -#line 164 +#line 163 i ++; } while_break: /* CIL Label */ ; } -#line 157 - term_exit- = term157_2-file_01-simple-cases; } -#line 166 +#line 165 return; } } -#line 170 "tests/regression/55-loop-unrolling/01-simple-cases.c" +#line 169 "tests/regression/55-loop-unrolling/01-simple-cases.c" void example9(void) { int a[5] ; int i ; - int term174_2-file_01-simple-cases ; + int term173_2-file_01-simple-cases ; { -#line 173 +#line 172 i = 0; { -#line 174 - term174_2-file_01-simple-cases = 0; +#line 173 + term173_2-file_01-simple-cases = 0; { -#line 174 +#line 173 while (1) { while_continue: /* CIL Label */ ; -#line 174 +#line 173 if (! 1) { -#line 174 +#line 173 goto while_break; } +#line 173 + term173_2-file_01-simple-cases ++; +#line 173 + term_exit- = term173_2-file_01-simple-cases; #line 174 - term174_2-file_01-simple-cases ++; -#line 175 a[i] = i; -#line 176 +#line 175 i ++; -#line 177 +#line 176 if (i == 5) { -#line 177 +#line 176 goto while_break; } } while_break: /* CIL Label */ ; } -#line 174 - term_exit- = term174_2-file_01-simple-cases; } -#line 179 +#line 178 return; } } -#line 183 "tests/regression/55-loop-unrolling/01-simple-cases.c" +#line 182 "tests/regression/55-loop-unrolling/01-simple-cases.c" void example10(void) { int a[5] ; int i ; - int term187_2-file_01-simple-cases ; + int term186_2-file_01-simple-cases ; { -#line 186 +#line 185 i = 0; { -#line 187 - term187_2-file_01-simple-cases = 0; +#line 186 + term186_2-file_01-simple-cases = 0; { -#line 187 +#line 186 while (1) { while_continue: /* CIL Label */ ; -#line 187 +#line 186 if (! (i < 5)) { -#line 187 +#line 186 goto while_break; } +#line 186 + term186_2-file_01-simple-cases ++; +#line 186 + term_exit- = term186_2-file_01-simple-cases; #line 187 - term187_2-file_01-simple-cases ++; -#line 188 if (i == 3) { -#line 189 +#line 188 i ++; -#line 190 +#line 189 goto while_continue; } -#line 192 +#line 191 a[i] = i; -#line 193 +#line 192 i ++; } while_break: /* CIL Label */ ; } -#line 187 - term_exit- = term187_2-file_01-simple-cases; } -#line 195 +#line 194 return; } } @@ -1683,42 +1681,40 @@ extern void ( __attribute__((__leaf__)) tzset)(void) __attribute__((__nothrow__ extern int daylight ; #line 233 extern long timezone ; -#line 246 +#line 249 extern time_t ( __attribute__((__leaf__)) timegm)(struct tm *__tp ) __attribute__((__nothrow__)) ; -#line 263 +#line 251 extern time_t ( __attribute__((__leaf__)) timelocal)(struct tm *__tp ) __attribute__((__nothrow__)) ; -#line 271 +#line 262 extern int ( __attribute__((__leaf__)) dysize)(int __year ) __attribute__((__nothrow__, __const__)) ; -#line 281 +#line 272 extern int nanosleep(struct timespec const *__requested_time , struct timespec *__remaining ) ; -#line 285 +#line 276 extern int ( __attribute__((__leaf__)) clock_getres)(clockid_t __clock_id , struct timespec *__res ) __attribute__((__nothrow__)) ; -#line 288 -extern int ( __attribute__((__nonnull__(2), __leaf__)) clock_gettime)(clockid_t __clock_id , - struct timespec *__tp ) __attribute__((__nothrow__)) ; -#line 292 -extern int ( __attribute__((__nonnull__(2), __leaf__)) clock_settime)(clockid_t __clock_id , - struct timespec const *__tp ) __attribute__((__nothrow__)) ; -#line 323 +#line 279 +extern int ( __attribute__((__leaf__)) clock_gettime)(clockid_t __clock_id , struct timespec *__tp ) __attribute__((__nothrow__)) ; +#line 282 +extern int ( __attribute__((__leaf__)) clock_settime)(clockid_t __clock_id , struct timespec const *__tp ) __attribute__((__nothrow__)) ; +#line 311 extern int clock_nanosleep(clockid_t __clock_id , int __flags , struct timespec const *__req , struct timespec *__rem ) ; -#line 338 +#line 326 extern int ( __attribute__((__leaf__)) clock_getcpuclockid)(pid_t __pid , clockid_t *__clock_id ) __attribute__((__nothrow__)) ; -#line 343 +#line 331 extern int ( __attribute__((__leaf__)) timer_create)(clockid_t __clock_id , struct sigevent * __restrict __evp , timer_t * __restrict __timerid ) __attribute__((__nothrow__)) ; -#line 348 +#line 336 extern int ( __attribute__((__leaf__)) timer_delete)(timer_t __timerid ) __attribute__((__nothrow__)) ; -#line 352 +#line 340 extern int ( __attribute__((__leaf__)) timer_settime)(timer_t __timerid , int __flags , struct itimerspec const * __restrict __value , struct itimerspec * __restrict __ovalue ) __attribute__((__nothrow__)) ; -#line 357 +#line 345 extern int ( __attribute__((__leaf__)) timer_gettime)(timer_t __timerid , struct itimerspec *__value ) __attribute__((__nothrow__)) ; -#line 376 +#line 364 extern int ( __attribute__((__leaf__)) timer_getoverrun)(timer_t __timerid ) __attribute__((__nothrow__)) ; -#line 383 +#line 371 extern int ( __attribute__((__nonnull__(1), __leaf__)) timespec_get)(struct timespec *__ts , int __base ) __attribute__((__nothrow__)) ; #line 202 "/usr/include/pthread.h" @@ -1829,8 +1825,9 @@ extern void __pthread_unregister_cancel(__pthread_unwind_buf_t *__buf ) ; #line 750 extern void __pthread_unwind_next(__pthread_unwind_buf_t *__buf ) __attribute__((__weak__, __noreturn__)) ; -#line 773 -extern int __sigsetjmp(struct __jmp_buf_tag *__env , int __savemask ) __attribute__((__nothrow__)) ; +#line 766 +extern int __sigsetjmp_cancel(struct __cancel_jmp_buf_tag *__env , int __savemask ) __asm__("__sigsetjmp") __attribute__((__returns_twice__, +__nothrow__)) ; #line 781 extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutex_init)(pthread_mutex_t *__mutex , pthread_mutexattr_t const *__mutexattr ) __attribute__((__nothrow__)) ; @@ -1995,7 +1992,8 @@ extern int ( __attribute__((__leaf__)) pthread_key_delete)(pthread_key_t __key ) extern void *( __attribute__((__leaf__)) pthread_getspecific)(pthread_key_t __key ) __attribute__((__nothrow__)) ; #line 1308 extern int ( __attribute__((__leaf__)) pthread_setspecific)(pthread_key_t __key , - void const *__pointer ) __attribute__((__nothrow__)) ; + void const *__pointer ) __attribute__((__nothrow__, +__access__(__none__,2))) ; #line 1315 extern int ( __attribute__((__nonnull__(2), __leaf__)) pthread_getcpuclockid)(pthread_t __thread_id , __clockid_t *__clock_id ) __attribute__((__nothrow__)) ; @@ -2057,6 +2055,8 @@ void qsort(void *ptr , size_t count , size_t size , int (*comp)(void const * , } #line 9 term9_3-file_stdlib ++; +#line 9 + term_exit- = term9_3-file_stdlib; #line 10 j = (size_t )0; { @@ -2073,6 +2073,8 @@ void qsort(void *ptr , size_t count , size_t size , int (*comp)(void const * , } #line 10 term10_5-file_stdlib ++; +#line 10 + term_exit- = term10_5-file_stdlib; #line 11 (*comp)((void const *)(ptr + i * size), (void const *)(ptr + j * size)); #line 10 @@ -2080,16 +2082,12 @@ void qsort(void *ptr , size_t count , size_t size , int (*comp)(void const * , } while_break___0: /* CIL Label */ ; } -#line 10 - term_exit- = term10_5-file_stdlib; } #line 9 i ++; } while_break: /* CIL Label */ ; } -#line 9 - term_exit- = term9_3-file_stdlib; } #line 16 i___0 = (size_t )0; @@ -2107,6 +2105,8 @@ void qsort(void *ptr , size_t count , size_t size , int (*comp)(void const * , } #line 16 term16_3-file_stdlib ++; +#line 16 + term_exit- = term16_3-file_stdlib; #line 17 j___0 = (size_t )0; { @@ -2123,6 +2123,8 @@ void qsort(void *ptr , size_t count , size_t size , int (*comp)(void const * , } #line 17 term17_5-file_stdlib ++; +#line 17 + term_exit- = term17_5-file_stdlib; #line 19 if (r) { #line 21 @@ -2141,6 +2143,8 @@ void qsort(void *ptr , size_t count , size_t size , int (*comp)(void const * , } #line 21 term21_9-file_stdlib ++; +#line 21 + term_exit- = term21_9-file_stdlib; #line 22 a = (char *)((ptr + i___0 * size) + k); #line 23 @@ -2156,8 +2160,6 @@ void qsort(void *ptr , size_t count , size_t size , int (*comp)(void const * , } while_break___3: /* CIL Label */ ; } -#line 21 - term_exit- = term21_9-file_stdlib; } } #line 17 @@ -2165,16 +2167,12 @@ void qsort(void *ptr , size_t count , size_t size , int (*comp)(void const * , } while_break___2: /* CIL Label */ ; } -#line 17 - term_exit- = term17_5-file_stdlib; } #line 16 i___0 ++; } while_break___1: /* CIL Label */ ; } -#line 16 - term_exit- = term16_3-file_stdlib; } #line 33 return; @@ -2212,6 +2210,8 @@ void *bsearch(void const *key , void const *ptr , size_t count , size_t size } #line 40 term40_3-file_stdlib ++; +#line 40 + term_exit- = term40_3-file_stdlib; #line 41 a = ptr + i * size; #line 42 @@ -2226,8 +2226,6 @@ void *bsearch(void const *key , void const *ptr , size_t count , size_t size } while_break: /* CIL Label */ ; } -#line 40 - term_exit- = term40_3-file_stdlib; } #line 47 return ((void *)0); From 4a402ca5373f2fe6eeb8e5dcc357368fd4f7b118 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Wed, 21 Jun 2023 14:10:44 +0200 Subject: [PATCH 0184/1312] reverted test case --- .../55-loop-unrolling/01-simple-cases.c | 24 +------------------ 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/tests/regression/55-loop-unrolling/01-simple-cases.c b/tests/regression/55-loop-unrolling/01-simple-cases.c index 9bf8d98683..3d5ce1b4e4 100644 --- a/tests/regression/55-loop-unrolling/01-simple-cases.c +++ b/tests/regression/55-loop-unrolling/01-simple-cases.c @@ -3,27 +3,6 @@ int global; -int f(int x); -int g(int x); - -int f(int x) -{ -if (x <= 0) return 0; -else return g(x) + g(x + 1); -} - -int g(int x) -{ -if (x <= 0) return 0; -else return f(x - 1) + f(x - 2); -} - -int main() { -int x = __VERIFIER_nondet_int(); -g(x); -} - -/* int main(void) { example1(); @@ -215,5 +194,4 @@ void example10(void) ++i; } return 0; -} -*/ \ No newline at end of file +} \ No newline at end of file From 79ac4ba84bdf5b02d643e73993a187f1282a8d38 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Wed, 21 Jun 2023 14:39:49 +0200 Subject: [PATCH 0185/1312] fixed error that for only one function the analysis was not executed: pulled check for result of loop analysis outside --- src/framework/constraints.ml | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index f0ae87acef..48196425f0 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1764,24 +1764,23 @@ struct ) gmap (* try all fundec + context pairs that are in the map *) with Invalid_argument _ -> () (* path ended: no cycle*) - let checkTerminating ctx v v' = - (*Check if the loops terminated*) - if ctx.ask Queries.MustTermProg - then (cycleDetection ctx v v') - else (let msgs = - [ - (Pretty.dprintf "The program might not terminate! (Loop analysis)\n", Some (M.Location.CilLocation locUnknown)); - ] in - M.msg_group Warning ~category:NonTerminating "Possibly non terminating loops" msgs) - let query ctx (type a) (q: a Queries.t): a Queries.result = match q with | WarnGlobal v -> + let ret = ctx.ask Queries.MustTermProg in + (* check result of loop analysis *) + if not ret then + (let msgs = + [ + (Pretty.dprintf "The program might not terminate! (Loop analysis)\n", Some (M.Location.CilLocation locUnknown)); + ] in + M.msg_group Warning ~category:NonTerminating "Possibly non terminating loops" msgs + ); let v: V.t = Obj.obj v in begin match v with | `Left v' -> - S.query (conv ctx) (WarnGlobal (Obj.repr v')) - | `Right v' -> checkTerminating ctx v v' + S.query (conv ctx) (WarnGlobal (Obj.repr v')) + | `Right v' -> if ret then (cycleDetection ctx v v') (* Only analyze if the recursion terminates if the loops terminated *) end | InvariantGlobal v -> let v: V.t = Obj.obj v in From 6a4105df8671cc6fc9a74e19b14257e2672b2543 Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Wed, 21 Jun 2023 14:48:04 +0200 Subject: [PATCH 0186/1312] Added SKIP/TODO functionality for loop termination --- scripts/update_suite.rb | 23 ++++++++++++++++--- .../02-simple-loop-nonterminating.c | 2 +- .../04-nested-loop-nonterminating.c | 2 +- .../06-for-loop-nonterminating.c | 2 +- .../08-nested-for-loop-nonterminating.c | 2 +- .../14-do-while-nonterminating.c | 2 +- ...16-nested-loop-nontrivial-nonterminating.c | 2 +- .../80-termination/18-goto-nonterminating.c | 2 +- .../80-termination/20-rand-nonterminating.c | 2 +- .../21-no-exit-on-rand-unproofable.c | 2 +- .../22-exit-on-rand-unproofable.c | 2 +- .../27-upjumping-goto-nonterminating.c | 2 +- .../81-recursion/02-simple-nonterminating.c | 2 +- 13 files changed, 32 insertions(+), 15 deletions(-) diff --git a/scripts/update_suite.rb b/scripts/update_suite.rb index 69b3bae485..d85acfc1d8 100755 --- a/scripts/update_suite.rb +++ b/scripts/update_suite.rb @@ -186,9 +186,15 @@ def compare_warnings if cond then @correct += 1 # full p.path is too long and p.name does not allow click to open in terminal - if todo.include? idx then puts "Excellent: ignored check on #{relpath(p.path).to_s.cyan}:#{idx.to_s.blue} is now passing!" end + if todo.include? idx + if idx < 0 + puts "Excellent: ignored check on #{relpath(p.path).to_s.cyan} for #{type.yellow} is now passing!" + else + puts "Excellent: ignored check on #{relpath(p.path).to_s.cyan}:#{idx.to_s.blue} is now passing!" + end + end else - if todo.include? idx then + if todo.include? idx @ignored += 1 else if idx < 0 # When non line specific keywords were used don't print a line @@ -202,7 +208,9 @@ def compare_warnings end } case type - when "deadlock", "race", "fail", "noterm", "unknown", "term", "warn", "non_local_term" + when "deadlock", "race", "fail", "noterm", "unknown", "term", "warn" + check.call warnings[idx] == type + when "non_local_term" check.call warnings[idx] == type when "nowarn", "local_term" check.call warnings[idx].nil? @@ -316,6 +324,15 @@ def parse_tests (lines) end end case lines[0] + when /TODO|SKIP/ + case lines[0] + when /NON_LOCAL_TERM/ + tests[-2] = "non_local_term" # Not sure if -2 is allowed or undefined in Ruby but it seems to work correctly + todo << -2 + when /LOCAL_TERM/ + tests[-2] = "local_term" + todo << -2 + end when /NON_LOCAL_TERM/ # covers "TERM" as keyword but a combined use of NON_LOCAL_TERM (loop termination) and TERM would be pointless tests[-2] = "non_local_term" diff --git a/tests/regression/80-termination/02-simple-loop-nonterminating.c b/tests/regression/80-termination/02-simple-loop-nonterminating.c index bcb9909f80..8c4c63ffab 100644 --- a/tests/regression/80-termination/02-simple-loop-nonterminating.c +++ b/tests/regression/80-termination/02-simple-loop-nonterminating.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/80-termination/04-nested-loop-nonterminating.c b/tests/regression/80-termination/04-nested-loop-nonterminating.c index ee2aa4a8c4..5a7ac43f70 100644 --- a/tests/regression/80-termination/04-nested-loop-nonterminating.c +++ b/tests/regression/80-termination/04-nested-loop-nonterminating.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/80-termination/06-for-loop-nonterminating.c b/tests/regression/80-termination/06-for-loop-nonterminating.c index b8f30361d1..73a8b8c6fd 100644 --- a/tests/regression/80-termination/06-for-loop-nonterminating.c +++ b/tests/regression/80-termination/06-for-loop-nonterminating.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/80-termination/08-nested-for-loop-nonterminating.c b/tests/regression/80-termination/08-nested-for-loop-nonterminating.c index 0368120b13..8b451e56dd 100644 --- a/tests/regression/80-termination/08-nested-for-loop-nonterminating.c +++ b/tests/regression/80-termination/08-nested-for-loop-nonterminating.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/80-termination/14-do-while-nonterminating.c b/tests/regression/80-termination/14-do-while-nonterminating.c index 5ed18175e9..c242e9a87c 100644 --- a/tests/regression/80-termination/14-do-while-nonterminating.c +++ b/tests/regression/80-termination/14-do-while-nonterminating.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/80-termination/16-nested-loop-nontrivial-nonterminating.c b/tests/regression/80-termination/16-nested-loop-nontrivial-nonterminating.c index 5ff890e461..9d51c17216 100644 --- a/tests/regression/80-termination/16-nested-loop-nontrivial-nonterminating.c +++ b/tests/regression/80-termination/16-nested-loop-nontrivial-nonterminating.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/80-termination/18-goto-nonterminating.c b/tests/regression/80-termination/18-goto-nonterminating.c index cfe5ab481d..ab85ccf8de 100644 --- a/tests/regression/80-termination/18-goto-nonterminating.c +++ b/tests/regression/80-termination/18-goto-nonterminating.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/80-termination/20-rand-nonterminating.c b/tests/regression/80-termination/20-rand-nonterminating.c index 7c21538612..fb1f336349 100644 --- a/tests/regression/80-termination/20-rand-nonterminating.c +++ b/tests/regression/80-termination/20-rand-nonterminating.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include #include #include diff --git a/tests/regression/80-termination/21-no-exit-on-rand-unproofable.c b/tests/regression/80-termination/21-no-exit-on-rand-unproofable.c index f54af1da7c..ce0fbb78ff 100644 --- a/tests/regression/80-termination/21-no-exit-on-rand-unproofable.c +++ b/tests/regression/80-termination/21-no-exit-on-rand-unproofable.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/80-termination/22-exit-on-rand-unproofable.c b/tests/regression/80-termination/22-exit-on-rand-unproofable.c index 1bc104258d..db79588945 100644 --- a/tests/regression/80-termination/22-exit-on-rand-unproofable.c +++ b/tests/regression/80-termination/22-exit-on-rand-unproofable.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/80-termination/27-upjumping-goto-nonterminating.c b/tests/regression/80-termination/27-upjumping-goto-nonterminating.c index a230827356..165a53fccf 100644 --- a/tests/regression/80-termination/27-upjumping-goto-nonterminating.c +++ b/tests/regression/80-termination/27-upjumping-goto-nonterminating.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/81-recursion/02-simple-nonterminating.c b/tests/regression/81-recursion/02-simple-nonterminating.c index 3ed0b75a0d..ef530f95d8 100644 --- a/tests/regression/81-recursion/02-simple-nonterminating.c +++ b/tests/regression/81-recursion/02-simple-nonterminating.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include void recursiveFunction(int n) { From 6f3921876d5408062d0289a33f7343431044a7fe Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Wed, 21 Jun 2023 15:09:41 +0200 Subject: [PATCH 0187/1312] Adapted TODO comments to current state --- tests/regression/80-termination/02-simple-loop-nonterminating.c | 2 +- tests/regression/80-termination/04-nested-loop-nonterminating.c | 2 +- .../regression/80-termination/09-complex-for-loop-terminating.c | 2 +- tests/regression/80-termination/10-complex-loop-terminating.c | 2 +- tests/regression/80-termination/14-do-while-nonterminating.c | 2 +- .../80-termination/15-complex-loop-combination-terminating.c | 2 +- .../80-termination/16-nested-loop-nontrivial-nonterminating.c | 2 +- tests/regression/80-termination/17-goto-terminating.c | 2 +- tests/regression/80-termination/18-goto-nonterminating.c | 2 +- tests/regression/80-termination/20-rand-nonterminating.c | 2 +- .../regression/80-termination/21-no-exit-on-rand-unproofable.c | 2 +- tests/regression/80-termination/22-exit-on-rand-unproofable.c | 2 +- tests/regression/80-termination/23-exit-on-rand-terminating.c | 2 +- .../80-termination/24-upjumping-goto-loopless-terminating.c | 2 +- .../regression/80-termination/25-leave-loop-goto-terminating.c | 2 +- .../regression/80-termination/26-enter-loop-goto-terminating.c | 2 +- .../80-termination/27-upjumping-goto-nonterminating.c | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/tests/regression/80-termination/02-simple-loop-nonterminating.c b/tests/regression/80-termination/02-simple-loop-nonterminating.c index 8c4c63ffab..bcb9909f80 100644 --- a/tests/regression/80-termination/02-simple-loop-nonterminating.c +++ b/tests/regression/80-termination/02-simple-loop-nonterminating.c @@ -1,4 +1,4 @@ -// TODO NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/80-termination/04-nested-loop-nonterminating.c b/tests/regression/80-termination/04-nested-loop-nonterminating.c index 5a7ac43f70..ee2aa4a8c4 100644 --- a/tests/regression/80-termination/04-nested-loop-nonterminating.c +++ b/tests/regression/80-termination/04-nested-loop-nonterminating.c @@ -1,4 +1,4 @@ -// TODO NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/80-termination/09-complex-for-loop-terminating.c b/tests/regression/80-termination/09-complex-for-loop-terminating.c index ae68b11575..c4cad58bff 100644 --- a/tests/regression/80-termination/09-complex-for-loop-terminating.c +++ b/tests/regression/80-termination/09-complex-for-loop-terminating.c @@ -1,4 +1,4 @@ -// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/80-termination/10-complex-loop-terminating.c b/tests/regression/80-termination/10-complex-loop-terminating.c index eb79338078..9a5a4429de 100644 --- a/tests/regression/80-termination/10-complex-loop-terminating.c +++ b/tests/regression/80-termination/10-complex-loop-terminating.c @@ -1,4 +1,4 @@ -// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/80-termination/14-do-while-nonterminating.c b/tests/regression/80-termination/14-do-while-nonterminating.c index c242e9a87c..5ed18175e9 100644 --- a/tests/regression/80-termination/14-do-while-nonterminating.c +++ b/tests/regression/80-termination/14-do-while-nonterminating.c @@ -1,4 +1,4 @@ -// TODO NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/80-termination/15-complex-loop-combination-terminating.c b/tests/regression/80-termination/15-complex-loop-combination-terminating.c index f3a08f4139..e0307cc53c 100644 --- a/tests/regression/80-termination/15-complex-loop-combination-terminating.c +++ b/tests/regression/80-termination/15-complex-loop-combination-terminating.c @@ -1,4 +1,4 @@ -// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/80-termination/16-nested-loop-nontrivial-nonterminating.c b/tests/regression/80-termination/16-nested-loop-nontrivial-nonterminating.c index 9d51c17216..5ff890e461 100644 --- a/tests/regression/80-termination/16-nested-loop-nontrivial-nonterminating.c +++ b/tests/regression/80-termination/16-nested-loop-nontrivial-nonterminating.c @@ -1,4 +1,4 @@ -// TODO NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/80-termination/17-goto-terminating.c b/tests/regression/80-termination/17-goto-terminating.c index b66fbe57ea..e988926359 100644 --- a/tests/regression/80-termination/17-goto-terminating.c +++ b/tests/regression/80-termination/17-goto-terminating.c @@ -1,4 +1,4 @@ -// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/80-termination/18-goto-nonterminating.c b/tests/regression/80-termination/18-goto-nonterminating.c index ab85ccf8de..cfe5ab481d 100644 --- a/tests/regression/80-termination/18-goto-nonterminating.c +++ b/tests/regression/80-termination/18-goto-nonterminating.c @@ -1,4 +1,4 @@ -// TODO NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/80-termination/20-rand-nonterminating.c b/tests/regression/80-termination/20-rand-nonterminating.c index fb1f336349..7c21538612 100644 --- a/tests/regression/80-termination/20-rand-nonterminating.c +++ b/tests/regression/80-termination/20-rand-nonterminating.c @@ -1,4 +1,4 @@ -// TODO NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include #include #include diff --git a/tests/regression/80-termination/21-no-exit-on-rand-unproofable.c b/tests/regression/80-termination/21-no-exit-on-rand-unproofable.c index ce0fbb78ff..f54af1da7c 100644 --- a/tests/regression/80-termination/21-no-exit-on-rand-unproofable.c +++ b/tests/regression/80-termination/21-no-exit-on-rand-unproofable.c @@ -1,4 +1,4 @@ -// TODO NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/80-termination/22-exit-on-rand-unproofable.c b/tests/regression/80-termination/22-exit-on-rand-unproofable.c index db79588945..1bc104258d 100644 --- a/tests/regression/80-termination/22-exit-on-rand-unproofable.c +++ b/tests/regression/80-termination/22-exit-on-rand-unproofable.c @@ -1,4 +1,4 @@ -// TODO NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/80-termination/23-exit-on-rand-terminating.c b/tests/regression/80-termination/23-exit-on-rand-terminating.c index 9d000069e1..253d38c5df 100644 --- a/tests/regression/80-termination/23-exit-on-rand-terminating.c +++ b/tests/regression/80-termination/23-exit-on-rand-terminating.c @@ -1,4 +1,4 @@ -// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include #include diff --git a/tests/regression/80-termination/24-upjumping-goto-loopless-terminating.c b/tests/regression/80-termination/24-upjumping-goto-loopless-terminating.c index a774f70457..01bffde383 100644 --- a/tests/regression/80-termination/24-upjumping-goto-loopless-terminating.c +++ b/tests/regression/80-termination/24-upjumping-goto-loopless-terminating.c @@ -1,4 +1,4 @@ -// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/80-termination/25-leave-loop-goto-terminating.c b/tests/regression/80-termination/25-leave-loop-goto-terminating.c index 28d8824535..fed0e218ac 100644 --- a/tests/regression/80-termination/25-leave-loop-goto-terminating.c +++ b/tests/regression/80-termination/25-leave-loop-goto-terminating.c @@ -1,4 +1,4 @@ -// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/80-termination/26-enter-loop-goto-terminating.c b/tests/regression/80-termination/26-enter-loop-goto-terminating.c index 41bf44c44e..2a43933758 100644 --- a/tests/regression/80-termination/26-enter-loop-goto-terminating.c +++ b/tests/regression/80-termination/26-enter-loop-goto-terminating.c @@ -1,4 +1,4 @@ -// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/80-termination/27-upjumping-goto-nonterminating.c b/tests/regression/80-termination/27-upjumping-goto-nonterminating.c index 165a53fccf..a230827356 100644 --- a/tests/regression/80-termination/27-upjumping-goto-nonterminating.c +++ b/tests/regression/80-termination/27-upjumping-goto-nonterminating.c @@ -1,4 +1,4 @@ -// TODO NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { From 0f3dbdc607d0f0e4f0ae20176195728b1baf2bc7 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Wed, 21 Jun 2023 15:16:13 +0200 Subject: [PATCH 0188/1312] Comment out unused loop_heads function --- src/analyses/termination_new.ml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index a3aa3aef92..7f2aa8357c 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -6,6 +6,7 @@ open TerminationPreprocessing exception PreProcessing of string +(* let loop_heads () = let module FileCfg = struct @@ -13,7 +14,8 @@ let loop_heads () = module Cfg = (val !MyCFG.current_cfg) end in let module WitnessInvariant = WitnessUtil.Invariant (FileCfg) in - WitnessInvariant.loop_heads (* TODO: Use this *) + WitnessInvariant.loop_heads (* TODO: Unused *) +*) (** Contains all loop counter variables (varinfo) and maps them to their corresponding loop statement. *) let loop_counters : stmt VarToStmt.t ref = ref VarToStmt.empty From 1a0905791df38fb6ce2c72ff02de875004d0c201 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Wed, 21 Jun 2023 15:26:31 +0200 Subject: [PATCH 0189/1312] Answer queries with false when multi-threaded --- src/analyses/termination_new.ml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index 7f2aa8357c..cd4b652f65 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -96,6 +96,11 @@ struct (* TODO: Implement check for our special loop exit indicator function *) ctx.local + (** Checks whether a new thread was spawned some time. We want to discard + * any knowledge about termination then (see query function) *) + let must_be_single_threaded_since_start ctx = + ctx.ask (Queries.MustBeSingleThreaded {since_start = true}) + (** Provides information to Goblint *) let query ctx (type a) (q: a Queries.t): a Queries.result = match q with @@ -103,9 +108,11 @@ struct (match G.find_opt (`Lifted loop_statement) (ctx.global ()) with Some b -> b | None -> false) + && must_be_single_threaded_since_start ctx | Queries.MustTermProg -> G.for_all (fun _ term_info -> term_info) (ctx.global ()) && no_upjumping_gotos () + && must_be_single_threaded_since_start ctx | _ -> Queries.Result.top q end From 524f0cb6c55f1b840198a399df65f8cca90a9096 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Wed, 21 Jun 2023 15:28:55 +0200 Subject: [PATCH 0190/1312] Change comments --- src/analyses/termination_new.ml | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/analyses/termination_new.ml b/src/analyses/termination_new.ml index cd4b652f65..7ecb11a12c 100644 --- a/src/analyses/termination_new.ml +++ b/src/analyses/termination_new.ml @@ -75,17 +75,9 @@ struct let assign ctx (lval : lval) (rval : exp) = (* Detect assignment to loop counter variable *) match lval, rval with - (* - (Var x, NoOffset), _ when is_loop_counter_var x -> - (* Assume that the following loop does not terminate *) - let loop_statement = VarToStmt.find x !loop_counters in - let () = ctx.sideg () (G.add (`Lifted loop_statement) false ctx.local) in - let () = print_endline ("Added FALSE for " ^ x.vname) in - D.add (`Lifted loop_statement) false ctx.local - *) (Var y, NoOffset), Lval (Var x, NoOffset) when is_loop_exit_indicator y -> (* Loop exit: Check whether loop counter variable is bounded *) - (* TODO: Move *) + (* TODO: Move to special *) let is_bounded = check_bounded ctx x in let loop_statement = VarToStmt.find x !loop_counters in let () = ctx.sideg () (G.add (`Lifted loop_statement) is_bounded (ctx.global ())) in From 72062a6fee57f95a814226685014288248fdd87a Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Wed, 21 Jun 2023 16:01:57 +0200 Subject: [PATCH 0191/1312] Added requested test cases --- .../28-do-while-continue-terminating.c | 21 +++++++++++ .../29-do-while-continue-nonterminating.c | 21 +++++++++++ .../30-goto-out-of-inner-loop-terminating.c | 22 ++++++++++++ ...31-goto-out-of-inner-loop-nonterminating.c | 23 ++++++++++++ .../32-multithread-terminating.c | 27 ++++++++++++++ .../33-multithread-nonterminating.c | 36 +++++++++++++++++++ 6 files changed, 150 insertions(+) create mode 100644 tests/regression/80-termination/28-do-while-continue-terminating.c create mode 100644 tests/regression/80-termination/29-do-while-continue-nonterminating.c create mode 100644 tests/regression/80-termination/30-goto-out-of-inner-loop-terminating.c create mode 100644 tests/regression/80-termination/31-goto-out-of-inner-loop-nonterminating.c create mode 100644 tests/regression/80-termination/32-multithread-terminating.c create mode 100644 tests/regression/80-termination/33-multithread-nonterminating.c diff --git a/tests/regression/80-termination/28-do-while-continue-terminating.c b/tests/regression/80-termination/28-do-while-continue-terminating.c new file mode 100644 index 0000000000..5989c61fed --- /dev/null +++ b/tests/regression/80-termination/28-do-while-continue-terminating.c @@ -0,0 +1,21 @@ +// TODO LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int i = 1; + + do + { + i++; + printf("Inside the do-while loop\n"); + if (i % 2 == 0) { + + printf("Skipping %i is even\n", i); + continue; + } + } while (i <= 5); + + printf("Exited the loop\n"); + return 0; +} diff --git a/tests/regression/80-termination/29-do-while-continue-nonterminating.c b/tests/regression/80-termination/29-do-while-continue-nonterminating.c new file mode 100644 index 0000000000..806456e887 --- /dev/null +++ b/tests/regression/80-termination/29-do-while-continue-nonterminating.c @@ -0,0 +1,21 @@ +// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int i = 1; + + do + { + printf("Inside the do-while loop\n"); + i++; + + if(i%2) { + printf("Continue as %i is odd\n", i); + continue; + } + } while (i >= 2); + + printf("Exited the loop\n"); + return 0; +} diff --git a/tests/regression/80-termination/30-goto-out-of-inner-loop-terminating.c b/tests/regression/80-termination/30-goto-out-of-inner-loop-terminating.c new file mode 100644 index 0000000000..76c272a654 --- /dev/null +++ b/tests/regression/80-termination/30-goto-out-of-inner-loop-terminating.c @@ -0,0 +1,22 @@ +// TODO LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() { + int rows = 5; + int columns = 5; + + // Outer loop for rows + for (int i = 1; i <= rows; i++) { + // Inner loop for columns + for (int j = 1; j <= columns; j++) { + if (j == 3) { + goto outer_loop; // Jump to the label "outer_loop" + } + printf("(%d, %d) ", i, j); + } + printf("\n"); + outer_loop:; // Label for the outer loop + } + + return 0; +} diff --git a/tests/regression/80-termination/31-goto-out-of-inner-loop-nonterminating.c b/tests/regression/80-termination/31-goto-out-of-inner-loop-nonterminating.c new file mode 100644 index 0000000000..c1824227d0 --- /dev/null +++ b/tests/regression/80-termination/31-goto-out-of-inner-loop-nonterminating.c @@ -0,0 +1,23 @@ +// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() { + int rows = 5; + int columns = 5; + + // Outer loop for rows + for (int i = 1; 1; i++) { + // Inner loop for columns + for (int j = 1; j <= columns; j++) { + if (j == 3) { + printf("Goto as continue for outer loop\n"); + goto outer_loop; // Jump to the label "outer_loop" + } + printf("(%d, %d) ", i, j); + } + printf("\n"); + outer_loop:; // Label for the outer loop + } + + return 0; +} diff --git a/tests/regression/80-termination/32-multithread-terminating.c b/tests/regression/80-termination/32-multithread-terminating.c new file mode 100644 index 0000000000..a08fe01398 --- /dev/null +++ b/tests/regression/80-termination/32-multithread-terminating.c @@ -0,0 +1,27 @@ +// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include +#include +#include + +// Thread function +void* printPID(void* arg) { + pid_t pid = getpid(); + pthread_t tid = pthread_self(); + printf("Thread ID: %lu, Process ID: %d\n", (unsigned long)tid, pid); + return NULL; +} + +int main() { + // Create three threads + pthread_t thread1, thread2, thread3; + pthread_create(&thread1, NULL, printPID, NULL); + pthread_create(&thread2, NULL, printPID, NULL); + pthread_create(&thread3, NULL, printPID, NULL); + + // Wait for all threads to finish + pthread_join(thread1, NULL); + pthread_join(thread2, NULL); + pthread_join(thread3, NULL); + + return 0; +} diff --git a/tests/regression/80-termination/33-multithread-nonterminating.c b/tests/regression/80-termination/33-multithread-nonterminating.c new file mode 100644 index 0000000000..77cd2aafe6 --- /dev/null +++ b/tests/regression/80-termination/33-multithread-nonterminating.c @@ -0,0 +1,36 @@ +// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include +#include +#include +#include +#include + +// Thread function +void* printPID(void* arg) { + pid_t pid = getpid(); + pthread_t tid = pthread_self(); + while(1) { + printf("Thread ID: %lu, Process ID: %d\n", (unsigned long)tid, pid); + struct timespec sleepTime; + sleepTime.tv_sec = 1; // Seconds + sleepTime.tv_nsec = 100000000 + (rand() % 200000000); // Nanoseconds (0.1 seconds + rand) + printf("Sleep for %ld nsec\n", sleepTime.tv_nsec); + nanosleep(&sleepTime, NULL); + } + return NULL; +} + +int main() { + // Create three threads + pthread_t thread1, thread2, thread3; + pthread_create(&thread1, NULL, printPID, NULL); + pthread_create(&thread2, NULL, printPID, NULL); + pthread_create(&thread3, NULL, printPID, NULL); + + // Wait for all threads to finish + pthread_join(thread1, NULL); + pthread_join(thread2, NULL); + pthread_join(thread3, NULL); + + return 0; +} From 936d7b04331ebeee920bc699a7dc64dbc217ead8 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Thu, 22 Jun 2023 12:22:45 +0300 Subject: [PATCH 0192/1312] Fix races between ancestor and outer accs Co-authored-by: Simmo Saan --- src/analyses/raceAnalysis.ml | 29 +++++++--- src/domains/access.ml | 58 +++++++++++++++---- .../04-mutex/79-type-nested-fields-deep1.t | 6 +- .../04-mutex/86-distribute-fields-3.t | 8 ++- .../04-mutex/90-distribute-fields-type-1.t | 4 +- .../04-mutex/91-distribute-fields-type-2.t | 4 +- .../04-mutex/92-distribute-fields-type-deep.t | 14 +++-- .../93-distribute-fields-type-global.t | 4 +- 8 files changed, 94 insertions(+), 33 deletions(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index 2e9a3d5b4b..e0fbf96c2f 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -52,7 +52,7 @@ struct module OffsetTrie = struct - include TrieDomain.Make (OneOffset) (Access.AS) + include TrieDomain.Make (OneOffset) (Lattice.LiftBot (Access.AS)) let rec find (offset : Offset.Unit.t) ((accs, children) : t) : value = match offset with @@ -63,8 +63,8 @@ struct let rec singleton (offset : Offset.Unit.t) (value : value) : t = match offset with | `NoOffset -> (value, ChildMap.empty ()) - | `Field (f, offset') -> (Access.AS.empty (), ChildMap.singleton (Field f) (singleton offset' value)) - | `Index ((), offset') -> (Access.AS.empty (), ChildMap.singleton Index (singleton offset' value)) + | `Field (f, offset') -> (`Bot, ChildMap.singleton (Field f) (singleton offset' value)) + | `Index ((), offset') -> (`Bot, ChildMap.singleton Index (singleton offset' value)) end module G = @@ -102,7 +102,13 @@ struct let side_access ctx (conf, w, loc, e, a) ((memoroot, offset) as memo) = if !AnalysisState.should_warn then - ctx.sideg (V.access memoroot) (G.create_access (OffsetTrie.singleton offset (Access.AS.singleton (conf, w, loc, e, a)))); + ctx.sideg (V.access memoroot) (G.create_access (OffsetTrie.singleton offset (`Lifted (Access.AS.singleton (conf, w, loc, e, a))))); + side_vars ctx memo + + let side_access0 ctx ((memoroot, offset) as memo) = + (* ignore (Pretty.printf "memo: %a\n" Access.Memo.pretty memo); *) + if !AnalysisState.should_warn then + ctx.sideg (V.access memoroot) (G.create_access (OffsetTrie.singleton offset (`Lifted (Access.AS.empty ())))); side_vars ctx memo let outer_memo ((root, offset) : Access.Memo.t) : Access.Memo.t option = @@ -114,7 +120,11 @@ struct let rec distribute_outer ctx ((root, offset) : Access.Memo.t) : Access.AS.t = let trie = G.access (ctx.global (V.access root)) in - let accs = OffsetTrie.find offset trie in + let accs = + match OffsetTrie.find offset trie with + | `Lifted accs -> accs + | `Bot -> Access.AS.empty () + in let outer_accs = match outer_memo (root, offset) with | Some outer_memo -> distribute_outer ctx outer_memo @@ -132,13 +142,18 @@ struct let trie = G.access (ctx.global g) in (** Distribute access to contained fields. *) let rec distribute_inner offset (accs, children) ~ancestor_accs ~ancestor_outer_accs = + let accs = + match accs with + | `Lifted accs -> accs + | `Bot -> Access.AS.empty () + in let outer_accs = match outer_memo (g', offset) with | Some outer_memo -> distribute_outer ctx outer_memo | None -> Access.AS.empty () in M.trace "access" "outer accs = %a" Access.AS.pretty outer_accs; - if not (Access.AS.is_empty accs) then ( + if not (Access.AS.is_empty accs && Access.AS.is_empty ancestor_accs && Access.AS.is_empty outer_accs) then ( let memo = (g', offset) in let mem_loc_str = GobPretty.sprint Access.Memo.pretty memo in Timing.wrap ~args:[("memory location", `String mem_loc_str)] "race" (Access.warn_global ~safe ~vulnerable ~unsafe ~ancestor_accs ~ancestor_outer_accs ~outer_accs memo) accs @@ -172,7 +187,7 @@ struct let loc = Option.get !Node.current_node in let add_access conf voffs = let a = part_access (Option.map fst voffs) in - Access.add (side_access octx (conf, kind, loc, e, a)) e voffs; + Access.add (side_access octx (conf, kind, loc, e, a)) (side_access0 octx) e voffs; in let add_access_struct conf ci = let a = part_access None in diff --git a/src/domains/access.ml b/src/domains/access.ml index 1dc6cbafce..40ebb289db 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -211,9 +211,30 @@ let add_one side memo: unit = if not ignorable then side memo +(** Distribute type-based access to variables and containing fields. *) +let rec add_distribute_outer side side0 (t: typ) (o: Offset.Unit.t) = + let memo = (`Type t, o) in + if M.tracing then M.tracei "access" "add_distribute_outer %a\n" Memo.pretty memo; + add_one side memo; + + (* distribute to variables of the type *) + let ts = typeSig t in + let vars = TSH.find_all typeVar ts in + List.iter (fun v -> + add_one side0 (`Var v, o) (* same offset, but on variable *) + ) vars; + + (* recursively distribute to fields containing the type *) + let fields = TSH.find_all typeIncl ts in + List.iter (fun f -> + (* prepend field and distribute to outer struct *) + add_distribute_outer side0 side0 (TComp (f.fcomp, [])) (`Field (f, o)) + ) fields; + + if M.tracing then M.traceu "access" "add_distribute_outer\n" (** Add access to known variable with offsets or unknown variable from expression. *) -let add side e voffs = +let add side side0 e voffs = begin match voffs with | Some (v, o) -> (* known variable *) if M.tracing then M.traceli "access" "add var %a%a\n" CilType.Varinfo.pretty v CilType.Offset.pretty o; @@ -228,7 +249,7 @@ let add side e voffs = in match o with | `NoOffset when not !collect_direct_arithmetic && isArithmeticType t -> () - | _ -> add_one side (`Type t, o) (* add_distribute_outer side t o (* distribute to variables and outer offsets *)*) + | _ -> add_distribute_outer side side0 t o (* distribute to variables and outer offsets *) end; if M.tracing then M.traceu "access" "add\n" @@ -379,26 +400,43 @@ let group_may_race ~ancestor_accs ~ancestor_outer_accs ~outer_accs accs = let todo_ancestor_accs = may_race_accs ~accs:ancestor_accs' ~todo:accs_todo in let todo_ancestor_outer_accs = may_race_accs ~accs:ancestor_outer_accs' ~todo:accs_todo in let todo_outer_accs = may_race_accs ~accs:outer_accs' ~todo:accs_todo in - let todo' = AS.union (AS.union todo_accs todo_ancestor_accs) (AS.union todo_ancestor_outer_accs todo_outer_accs) in + let todo_ancestor_accs_cross = may_race_accs ~accs:ancestor_accs' ~todo:(AS.inter todo outer_accs) in + let todo_outer_accs_cross = may_race_accs ~accs:outer_accs' ~todo:(AS.inter todo ancestor_accs) in + let todos = [todo_accs; todo_ancestor_accs; todo_ancestor_outer_accs; todo_outer_accs; todo_ancestor_accs_cross; todo_outer_accs_cross] in + let todo' = List.reduce AS.union todos in let visited' = AS.union visited todo in if AS.is_empty todo' then - (accs', visited') + (accs', ancestor_accs', ancestor_outer_accs', outer_accs', visited') else (bfs' [@tailcall]) ~ancestor_accs:ancestor_accs' ~ancestor_outer_accs:ancestor_outer_accs' ~outer_accs:outer_accs' ~accs:accs' ~todo:todo' ~visited:visited' in - let bfs accs acc = bfs' ~ancestor_accs ~ancestor_outer_accs ~outer_accs ~accs ~todo:(AS.singleton acc) ~visited:(AS.empty ()) in + let bfs ~ancestor_accs ~ancestor_outer_accs ~outer_accs ~accs acc = bfs' ~ancestor_accs ~ancestor_outer_accs ~outer_accs ~accs ~todo:(AS.singleton acc) ~visited:(AS.empty ()) in (* repeat BFS to find all components *) - let rec components comps accs = + let rec components comps ~ancestor_accs ~ancestor_outer_accs ~outer_accs ~accs = if AS.is_empty accs then - comps + (comps, ancestor_accs, outer_accs) else ( let acc = AS.choose accs in - let (accs', comp) = bfs accs acc in + let (accs', ancestor_accs', ancestor_outer_accs', outer_accs', comp) = bfs ~ancestor_accs ~ancestor_outer_accs ~outer_accs ~accs acc in + let comps' = comp :: comps in + components comps' ~ancestor_accs:ancestor_accs' ~ancestor_outer_accs:ancestor_outer_accs' ~outer_accs:outer_accs' ~accs:accs' + ) + in + (* ignore (Pretty.printf "ancestors0: %a outer0: %a\n" AS.pretty ancestor_accs AS.pretty outer_accs); *) + let (comps, ancestor_accs, outer_accs) = components [] ~ancestor_accs ~ancestor_outer_accs ~outer_accs ~accs in + (* ignore (Pretty.printf "ancestors: %a outer: %a\n" AS.pretty ancestor_accs AS.pretty outer_accs); *) + let rec components_cross comps ~ancestor_accs ~outer_accs = + if AS.is_empty ancestor_accs then + comps + else ( + let ancestor_acc = AS.choose ancestor_accs in + let (_, ancestor_accs', _, outer_accs', comp) = bfs ~ancestor_accs ~ancestor_outer_accs:(AS.empty ()) ~outer_accs ~accs:(AS.empty ()) ancestor_acc in + (* ignore (Pretty.printf "ancestor: %a comp: %a\n" A.pretty ancestor_acc AS.pretty comp); *) let comps' = comp :: comps in - components comps' accs' + components_cross comps' ~ancestor_accs:ancestor_accs' ~outer_accs:outer_accs' ) in - components [] accs + components_cross comps ~ancestor_accs ~outer_accs let race_conf accs = assert (not (AS.is_empty accs)); (* group_may_race should only construct non-empty components *) diff --git a/tests/regression/04-mutex/79-type-nested-fields-deep1.t b/tests/regression/04-mutex/79-type-nested-fields-deep1.t index 4075dab33b..33bc8eede4 100644 --- a/tests/regression/04-mutex/79-type-nested-fields-deep1.t +++ b/tests/regression/04-mutex/79-type-nested-fields-deep1.t @@ -23,7 +23,9 @@ write with [mhp:{tid=[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]}, thread:[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (79-type-nested-fields-deep1.c:37:3-37:20) write with [mhp:{tid=[main]; created={[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t.s.field) (79-type-nested-fields-deep1.c:44:3-44:24) [Info][Race] Memory locations race summary: - safe: 1 + safe: 2 vulnerable: 0 unsafe: 1 - total memory locations: 2 + total memory locations: 3 + +TODO: fix memory location numbers diff --git a/tests/regression/04-mutex/86-distribute-fields-3.t b/tests/regression/04-mutex/86-distribute-fields-3.t index 9651a91923..af17297db9 100644 --- a/tests/regression/04-mutex/86-distribute-fields-3.t +++ b/tests/regression/04-mutex/86-distribute-fields-3.t @@ -5,11 +5,15 @@ total lines: 8 [Success][Race] Memory location t@86-distribute-fields-3.c:15:10-15:11 (safe): write with [mhp:{tid=[main]; created={[main, t_fun@86-distribute-fields-3.c:24:3-24:40#top]}}, thread:[main]] (conf. 110) (exp: & t) (86-distribute-fields-3.c:26:3-26:9) + [Success][Race] Memory location t.s@86-distribute-fields-3.c:15:10-15:11 (safe): + write with [mhp:{tid=[main]; created={[main, t_fun@86-distribute-fields-3.c:24:3-24:40#top]}}, thread:[main]] (conf. 110) (exp: & t) (86-distribute-fields-3.c:26:3-26:9) [Warning][Race] Memory location t.s.data@86-distribute-fields-3.c:15:10-15:11 (race with conf. 110): write with [mhp:{tid=[main, t_fun@86-distribute-fields-3.c:24:3-24:40#top]}, thread:[main, t_fun@86-distribute-fields-3.c:24:3-24:40#top]] (conf. 110) (exp: & t.s.data) (86-distribute-fields-3.c:18:3-18:15) write with [mhp:{tid=[main]; created={[main, t_fun@86-distribute-fields-3.c:24:3-24:40#top]}}, thread:[main]] (conf. 110) (exp: & t) (86-distribute-fields-3.c:26:3-26:9) [Info][Race] Memory locations race summary: - safe: 1 + safe: 2 vulnerable: 0 unsafe: 1 - total memory locations: 2 + total memory locations: 3 + +TODO: fix memory location numbers diff --git a/tests/regression/04-mutex/90-distribute-fields-type-1.t b/tests/regression/04-mutex/90-distribute-fields-type-1.t index dd862fa65a..65689ff4d4 100644 --- a/tests/regression/04-mutex/90-distribute-fields-type-1.t +++ b/tests/regression/04-mutex/90-distribute-fields-type-1.t @@ -17,11 +17,11 @@ live: 7 dead: 0 total lines: 7 + [Success][Race] Memory location (struct T).s (safe): + write with [mhp:{tid=[main]; created={[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->s) (90-distribute-fields-type-1.c:40:3-40:17) [Warning][Race] Memory location (struct T).s.field (race with conf. 100): write with [mhp:{tid=[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]}, thread:[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]] (conf. 100) (exp: & tmp->field) (90-distribute-fields-type-1.c:32:3-32:20) write with [mhp:{tid=[main]; created={[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->s) (90-distribute-fields-type-1.c:40:3-40:17) - [Success][Race] Memory location (struct T).s (safe): - write with [mhp:{tid=[main]; created={[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->s) (90-distribute-fields-type-1.c:40:3-40:17) [Success][Race] Memory location (struct S).field (safe): write with [mhp:{tid=[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]}, thread:[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]] (conf. 100) (exp: & tmp->field) (90-distribute-fields-type-1.c:32:3-32:20) [Info][Race] Memory locations race summary: diff --git a/tests/regression/04-mutex/91-distribute-fields-type-2.t b/tests/regression/04-mutex/91-distribute-fields-type-2.t index 4cfb965e25..be365577f2 100644 --- a/tests/regression/04-mutex/91-distribute-fields-type-2.t +++ b/tests/regression/04-mutex/91-distribute-fields-type-2.t @@ -17,11 +17,11 @@ live: 7 dead: 0 total lines: 7 + [Success][Race] Memory location (struct T) (safe): + write with [mhp:{tid=[main]; created={[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]}}, thread:[main]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:41:3-41:17) [Warning][Race] Memory location (struct T).s (race with conf. 100): write with [mhp:{tid=[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]}, thread:[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:33:3-33:17) write with [mhp:{tid=[main]; created={[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]}}, thread:[main]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:41:3-41:17) - [Success][Race] Memory location (struct T) (safe): - write with [mhp:{tid=[main]; created={[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]}}, thread:[main]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:41:3-41:17) [Success][Race] Memory location (struct S) (safe): write with [mhp:{tid=[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]}, thread:[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:33:3-33:17) [Info][Race] Memory locations race summary: diff --git a/tests/regression/04-mutex/92-distribute-fields-type-deep.t b/tests/regression/04-mutex/92-distribute-fields-type-deep.t index 12dc5e8f52..f605c4c4cf 100644 --- a/tests/regression/04-mutex/92-distribute-fields-type-deep.t +++ b/tests/regression/04-mutex/92-distribute-fields-type-deep.t @@ -17,17 +17,19 @@ live: 7 dead: 0 total lines: 7 - [Success][Race] Memory location (struct T).s.field (safe): - write with [mhp:{tid=[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}, thread:[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (92-distribute-fields-type-deep.c:37:3-37:20) [Success][Race] Memory location (struct S).field (safe): write with [mhp:{tid=[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}, thread:[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (92-distribute-fields-type-deep.c:37:3-37:20) + [Success][Race] Memory location (struct U).t (safe): + write with [mhp:{tid=[main]; created={[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t) (92-distribute-fields-type-deep.c:45:3-45:17) + [Success][Race] Memory location (struct U).t.s (safe): + write with [mhp:{tid=[main]; created={[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t) (92-distribute-fields-type-deep.c:45:3-45:17) [Warning][Race] Memory location (struct U).t.s.field (race with conf. 100): write with [mhp:{tid=[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}, thread:[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (92-distribute-fields-type-deep.c:37:3-37:20) write with [mhp:{tid=[main]; created={[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t) (92-distribute-fields-type-deep.c:45:3-45:17) - [Success][Race] Memory location (struct U).t (safe): - write with [mhp:{tid=[main]; created={[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t) (92-distribute-fields-type-deep.c:45:3-45:17) [Info][Race] Memory locations race summary: - safe: 3 + safe: 4 vulnerable: 0 unsafe: 1 - total memory locations: 4 + total memory locations: 5 + +TODO: fix memory location numbers diff --git a/tests/regression/04-mutex/93-distribute-fields-type-global.t b/tests/regression/04-mutex/93-distribute-fields-type-global.t index 30f61cb3cc..5a00f03dce 100644 --- a/tests/regression/04-mutex/93-distribute-fields-type-global.t +++ b/tests/regression/04-mutex/93-distribute-fields-type-global.t @@ -11,11 +11,11 @@ live: 7 dead: 0 total lines: 7 + [Success][Race] Memory location s@93-distribute-fields-type-global.c:9:10-9:11 (safe): + write with [mhp:{tid=[main]; created={[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]}}, thread:[main]] (conf. 110) (exp: & s) (93-distribute-fields-type-global.c:23:3-23:9) [Warning][Race] Memory location s.field@93-distribute-fields-type-global.c:9:10-9:11 (race with conf. 110): read with [mhp:{tid=[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]}, thread:[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]] (conf. 100) (exp: & tmp->field) (93-distribute-fields-type-global.c:14:3-14:29) write with [mhp:{tid=[main]; created={[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]}}, thread:[main]] (conf. 110) (exp: & s) (93-distribute-fields-type-global.c:23:3-23:9) - [Success][Race] Memory location s@93-distribute-fields-type-global.c:9:10-9:11 (safe): - write with [mhp:{tid=[main]; created={[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]}}, thread:[main]] (conf. 110) (exp: & s) (93-distribute-fields-type-global.c:23:3-23:9) [Success][Race] Memory location (struct S).field (safe): read with [mhp:{tid=[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]}, thread:[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]] (conf. 100) (exp: & tmp->field) (93-distribute-fields-type-global.c:14:3-14:29) [Info][Race] Memory locations race summary: From edff1d3b94fdb84e26f3e98be6d0e3cf47548e2c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 22 Jun 2023 13:35:51 +0300 Subject: [PATCH 0193/1312] Fix memory location counts for cross races --- src/analyses/raceAnalysis.ml | 4 ++-- src/domains/access.ml | 7 ++++++- tests/regression/04-mutex/79-type-nested-fields-deep1.t | 6 ++---- tests/regression/04-mutex/86-distribute-fields-3.t | 8 ++------ .../regression/04-mutex/92-distribute-fields-type-deep.t | 8 ++------ 5 files changed, 14 insertions(+), 19 deletions(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index e0fbf96c2f..76b93892f6 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -153,10 +153,10 @@ struct | None -> Access.AS.empty () in M.trace "access" "outer accs = %a" Access.AS.pretty outer_accs; - if not (Access.AS.is_empty accs && Access.AS.is_empty ancestor_accs && Access.AS.is_empty outer_accs) then ( + if not (Access.AS.is_empty accs) || (not (Access.AS.is_empty ancestor_accs) && not (Access.AS.is_empty outer_accs)) then ( let memo = (g', offset) in let mem_loc_str = GobPretty.sprint Access.Memo.pretty memo in - Timing.wrap ~args:[("memory location", `String mem_loc_str)] "race" (Access.warn_global ~safe ~vulnerable ~unsafe ~ancestor_accs ~ancestor_outer_accs ~outer_accs memo) accs + Timing.wrap ~args:[("memory location", `String mem_loc_str)] "race" (Access.warn_global ~safe ~vulnerable ~unsafe ~ancestor_accs ~ancestor_outer_accs ~outer_accs memo) accs ); let ancestor_outer_accs' = Access.AS.union ancestor_outer_accs outer_accs in let ancestor_accs' = Access.AS.union ancestor_accs accs in diff --git a/src/domains/access.ml b/src/domains/access.ml index 40ebb289db..9d5147f2b0 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -432,7 +432,12 @@ let group_may_race ~ancestor_accs ~ancestor_outer_accs ~outer_accs accs = let ancestor_acc = AS.choose ancestor_accs in let (_, ancestor_accs', _, outer_accs', comp) = bfs ~ancestor_accs ~ancestor_outer_accs:(AS.empty ()) ~outer_accs ~accs:(AS.empty ()) ancestor_acc in (* ignore (Pretty.printf "ancestor: %a comp: %a\n" A.pretty ancestor_acc AS.pretty comp); *) - let comps' = comp :: comps in + let comps' = + if AS.cardinal comp > 1 then + comp :: comps + else + comps (* ignore self-race ancestor_acc component, self-race checked at ancestor's level *) + in components_cross comps' ~ancestor_accs:ancestor_accs' ~outer_accs:outer_accs' ) in diff --git a/tests/regression/04-mutex/79-type-nested-fields-deep1.t b/tests/regression/04-mutex/79-type-nested-fields-deep1.t index 33bc8eede4..4075dab33b 100644 --- a/tests/regression/04-mutex/79-type-nested-fields-deep1.t +++ b/tests/regression/04-mutex/79-type-nested-fields-deep1.t @@ -23,9 +23,7 @@ write with [mhp:{tid=[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]}, thread:[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (79-type-nested-fields-deep1.c:37:3-37:20) write with [mhp:{tid=[main]; created={[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t.s.field) (79-type-nested-fields-deep1.c:44:3-44:24) [Info][Race] Memory locations race summary: - safe: 2 + safe: 1 vulnerable: 0 unsafe: 1 - total memory locations: 3 - -TODO: fix memory location numbers + total memory locations: 2 diff --git a/tests/regression/04-mutex/86-distribute-fields-3.t b/tests/regression/04-mutex/86-distribute-fields-3.t index af17297db9..9651a91923 100644 --- a/tests/regression/04-mutex/86-distribute-fields-3.t +++ b/tests/regression/04-mutex/86-distribute-fields-3.t @@ -5,15 +5,11 @@ total lines: 8 [Success][Race] Memory location t@86-distribute-fields-3.c:15:10-15:11 (safe): write with [mhp:{tid=[main]; created={[main, t_fun@86-distribute-fields-3.c:24:3-24:40#top]}}, thread:[main]] (conf. 110) (exp: & t) (86-distribute-fields-3.c:26:3-26:9) - [Success][Race] Memory location t.s@86-distribute-fields-3.c:15:10-15:11 (safe): - write with [mhp:{tid=[main]; created={[main, t_fun@86-distribute-fields-3.c:24:3-24:40#top]}}, thread:[main]] (conf. 110) (exp: & t) (86-distribute-fields-3.c:26:3-26:9) [Warning][Race] Memory location t.s.data@86-distribute-fields-3.c:15:10-15:11 (race with conf. 110): write with [mhp:{tid=[main, t_fun@86-distribute-fields-3.c:24:3-24:40#top]}, thread:[main, t_fun@86-distribute-fields-3.c:24:3-24:40#top]] (conf. 110) (exp: & t.s.data) (86-distribute-fields-3.c:18:3-18:15) write with [mhp:{tid=[main]; created={[main, t_fun@86-distribute-fields-3.c:24:3-24:40#top]}}, thread:[main]] (conf. 110) (exp: & t) (86-distribute-fields-3.c:26:3-26:9) [Info][Race] Memory locations race summary: - safe: 2 + safe: 1 vulnerable: 0 unsafe: 1 - total memory locations: 3 - -TODO: fix memory location numbers + total memory locations: 2 diff --git a/tests/regression/04-mutex/92-distribute-fields-type-deep.t b/tests/regression/04-mutex/92-distribute-fields-type-deep.t index f605c4c4cf..c0f3beae2c 100644 --- a/tests/regression/04-mutex/92-distribute-fields-type-deep.t +++ b/tests/regression/04-mutex/92-distribute-fields-type-deep.t @@ -21,15 +21,11 @@ write with [mhp:{tid=[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}, thread:[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (92-distribute-fields-type-deep.c:37:3-37:20) [Success][Race] Memory location (struct U).t (safe): write with [mhp:{tid=[main]; created={[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t) (92-distribute-fields-type-deep.c:45:3-45:17) - [Success][Race] Memory location (struct U).t.s (safe): - write with [mhp:{tid=[main]; created={[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t) (92-distribute-fields-type-deep.c:45:3-45:17) [Warning][Race] Memory location (struct U).t.s.field (race with conf. 100): write with [mhp:{tid=[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}, thread:[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (92-distribute-fields-type-deep.c:37:3-37:20) write with [mhp:{tid=[main]; created={[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t) (92-distribute-fields-type-deep.c:45:3-45:17) [Info][Race] Memory locations race summary: - safe: 4 + safe: 2 vulnerable: 0 unsafe: 1 - total memory locations: 5 - -TODO: fix memory location numbers + total memory locations: 3 From 2f9f697f4e53e8324a62844839fed5b2f478dcef Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Thu, 22 Jun 2023 13:32:39 +0200 Subject: [PATCH 0194/1312] Skip crashing tests --- .../regression/80-termination/09-complex-for-loop-terminating.c | 2 +- tests/regression/80-termination/10-complex-loop-terminating.c | 2 +- .../80-termination/15-complex-loop-combination-terminating.c | 2 +- tests/regression/81-recursion/02-simple-nonterminating.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/regression/80-termination/09-complex-for-loop-terminating.c b/tests/regression/80-termination/09-complex-for-loop-terminating.c index c4cad58bff..8951924c06 100644 --- a/tests/regression/80-termination/09-complex-for-loop-terminating.c +++ b/tests/regression/80-termination/09-complex-for-loop-terminating.c @@ -1,4 +1,4 @@ -// TODO LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/80-termination/10-complex-loop-terminating.c b/tests/regression/80-termination/10-complex-loop-terminating.c index 9a5a4429de..dbce2a6b5b 100644 --- a/tests/regression/80-termination/10-complex-loop-terminating.c +++ b/tests/regression/80-termination/10-complex-loop-terminating.c @@ -1,4 +1,4 @@ -// TODO LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/80-termination/15-complex-loop-combination-terminating.c b/tests/regression/80-termination/15-complex-loop-combination-terminating.c index e0307cc53c..7daa9b98fe 100644 --- a/tests/regression/80-termination/15-complex-loop-combination-terminating.c +++ b/tests/regression/80-termination/15-complex-loop-combination-terminating.c @@ -1,4 +1,4 @@ -// TODO LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval #include int main() diff --git a/tests/regression/81-recursion/02-simple-nonterminating.c b/tests/regression/81-recursion/02-simple-nonterminating.c index ef530f95d8..c7902e2e7f 100644 --- a/tests/regression/81-recursion/02-simple-nonterminating.c +++ b/tests/regression/81-recursion/02-simple-nonterminating.c @@ -1,4 +1,4 @@ -// TODO NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include void recursiveFunction(int n) { From e250557c958d60fa1d8e48b1b678ad600ace27ef Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Thu, 22 Jun 2023 13:35:33 +0200 Subject: [PATCH 0195/1312] reverted test case 55 01 --- tests/regression/55-loop-unrolling/01-simple-cases.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/regression/55-loop-unrolling/01-simple-cases.c b/tests/regression/55-loop-unrolling/01-simple-cases.c index 3d5ce1b4e4..add8c6a8dd 100644 --- a/tests/regression/55-loop-unrolling/01-simple-cases.c +++ b/tests/regression/55-loop-unrolling/01-simple-cases.c @@ -30,8 +30,7 @@ void example1(void) } __goblint_check(a[0] == 0); // UNKNOWN - lab:__goblint_check(a[3] == 3); // UNKNOWN - goto lab; + __goblint_check(a[3] == 3); // UNKNOWN } // Do-while loop simple example From 4e66fd2bf3118fad23d2944400234da6fc869956 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Thu, 22 Jun 2023 16:05:13 +0200 Subject: [PATCH 0196/1312] make recursion analysis optional (depending if loop ana is activated) --- src/framework/constraints.ml | 5 ++--- src/framework/control.ml | 3 ++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 48196425f0..e57570eb35 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1767,9 +1767,8 @@ struct let query ctx (type a) (q: a Queries.t): a Queries.result = match q with | WarnGlobal v -> - let ret = ctx.ask Queries.MustTermProg in (* check result of loop analysis *) - if not ret then + if not (ctx.ask Queries.MustTermProg) then (let msgs = [ (Pretty.dprintf "The program might not terminate! (Loop analysis)\n", Some (M.Location.CilLocation locUnknown)); @@ -1780,7 +1779,7 @@ struct begin match v with | `Left v' -> S.query (conv ctx) (WarnGlobal (Obj.repr v')) - | `Right v' -> if ret then (cycleDetection ctx v v') (* Only analyze if the recursion terminates if the loops terminated *) + | `Right v' -> cycleDetection ctx v v' (* Note: to make it more efficient, one could only execute the cycle detection in case the loop analysis returns true, because otherwise the program will probably not terminate anyway*) end | InvariantGlobal v -> let v: V.t = Obj.obj v in diff --git a/src/framework/control.ml b/src/framework/control.ml index 6261329e18..b157a2e81a 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -15,6 +15,7 @@ module type S2S = functor (X : Spec) -> Spec let spec_module: (module Spec) Lazy.t = lazy ( GobConfig.building_spec := true; let arg_enabled = get_bool "ana.sv-comp.enabled" || get_bool "exp.arg" in + let arg_termination = List.mem "termination" (get_string_list "ana.activated") in (* check if loop termination analysis is enabled*) let open Batteries in (* apply functor F on module X if opt is true *) let lift opt (module F : S2S) (module X : Spec) = (module (val if opt then (module F (X)) else (module X) : Spec) : Spec) in @@ -36,7 +37,7 @@ let spec_module: (module Spec) Lazy.t = lazy ( Also must be outside of deadcode, because deadcode splits (like mutex lock event) don't pass on tokens. *) |> lift (get_bool "ana.widen.tokens") (module WideningTokens.Lifter) |> lift true (module LongjmpLifter) - |> lift true (module RecursionTermLifter)(*TODO: should we really always evaluate it???*) + |> lift arg_termination (module RecursionTermLifter) (* Always activate the recursion termination analysis, when the loop termination analysis is activated*) ) in GobConfig.building_spec := false; ControlSpecC.control_spec_c := (module S1.C); From 4b4be5b8d0bce38f15b07cfd95d4f48a6fc41d3f Mon Sep 17 00:00:00 2001 From: Isidor Zweckstetter Date: Thu, 22 Jun 2023 16:27:14 +0200 Subject: [PATCH 0197/1312] work-in-progress on svcomp integration --- src/analyses/apron/apronAnalysis.apron.ml | 2 -- src/autoTune.ml | 1 + src/framework/analysisState.ml | 3 +++ src/framework/constraints.ml | 7 ++++--- src/witness/svcomp.ml | 1 + src/witness/svcompSpec.ml | 4 ++++ src/witness/witness.ml | 17 +++++++++++++++++ .../55-loop-unrolling/01-simple-cases.c | 3 +-- 8 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/analyses/apron/apronAnalysis.apron.ml b/src/analyses/apron/apronAnalysis.apron.ml index 789701d37c..1ac88c66ee 100644 --- a/src/analyses/apron/apronAnalysis.apron.ml +++ b/src/analyses/apron/apronAnalysis.apron.ml @@ -1,7 +1,5 @@ (** Analysis using Apron for integer variables. *) open Analyses -open TerminationPreprocessing -open Cilfacade include RelationAnalysis let spec_module: (module MCPSpec) Lazy.t = diff --git a/src/autoTune.ml b/src/autoTune.ml index a267c3bf9b..4fb8a1db5e 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -216,6 +216,7 @@ let focusOnSpecification () = | NoDataRace -> (*enable all thread analyses*) print_endline @@ "Specification: NoDataRace -> enabling thread analyses \"" ^ (String.concat ", " notNeccessaryThreadAnalyses) ^ "\""; enableAnalyses notNeccessaryThreadAnalyses; + | NoTermination -> () | NoOverflow -> (*We focus on integer analysis*) set_bool "ana.int.def_exc" true; set_bool "ana.int.interval" true diff --git a/src/framework/analysisState.ml b/src/framework/analysisState.ml index 0f3a9f55bc..ddecf1752a 100644 --- a/src/framework/analysisState.ml +++ b/src/framework/analysisState.ml @@ -7,6 +7,9 @@ let should_warn = ref false (** Whether signed overflow or underflow happened *) let svcomp_may_overflow = ref false +(** TODO:**) +let svcomp_must_terminate = ref true + (** A hack to see if we are currently doing global inits *) let global_initialization = ref false diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 48196425f0..865b0405ee 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1716,7 +1716,7 @@ struct module G = GVarGSet (S.G) (S.C) (T (CilType.Fundec) (S.C)) - let name () = "RecursionTerm (" ^ S.name () ^ ")" + let name () = "termination" let conv (ctx: (_, G.t, _, V.t) ctx): (_, S.G.t, _, S.V.t) ctx = { ctx with @@ -1734,7 +1734,7 @@ struct let rec iter_call (path_visited_calls: LS.t) (call:T (CilType.Fundec) (S.C).t) = let ((fundec_e:fundec), (context_e: C.t)) = call in (*unpack tuple for later use*) if LS.mem call path_visited_calls then ( - + AnalysisState.svcomp_must_terminate := false; (*Cycle found*) let msgs = [ @@ -1770,7 +1770,8 @@ struct let ret = ctx.ask Queries.MustTermProg in (* check result of loop analysis *) if not ret then - (let msgs = + (AnalysisState.svcomp_must_terminate := false; + let msgs = [ (Pretty.dprintf "The program might not terminate! (Loop analysis)\n", Some (M.Location.CilLocation locUnknown)); ] in diff --git a/src/witness/svcomp.ml b/src/witness/svcomp.ml index a5a572d1c2..a164448210 100644 --- a/src/witness/svcomp.ml +++ b/src/witness/svcomp.ml @@ -52,6 +52,7 @@ struct | UnreachCall _ -> "unreach-call" | NoOverflow -> "no-overflow" | NoDataRace -> "no-data-race" (* not yet in SV-COMP/Benchexec *) + | NoTermination -> "no-termination" in "false(" ^ result_spec ^ ")" | Unknown -> "unknown" diff --git a/src/witness/svcompSpec.ml b/src/witness/svcompSpec.ml index 464c170251..eec667c5a6 100644 --- a/src/witness/svcompSpec.ml +++ b/src/witness/svcompSpec.ml @@ -6,6 +6,7 @@ type t = | UnreachCall of string | NoDataRace | NoOverflow + | NoTermination let of_string s = let s = String.strip s in @@ -16,6 +17,8 @@ let of_string s = NoDataRace else if global_not = "overflow" then NoOverflow + else if global_not = "termination" then (*TODO: does this even work?*) + NoTermination else let call_regex = Str.regexp "call(\\(.*\\)())" in if Str.string_match call_regex global_not 0 then @@ -42,5 +45,6 @@ let to_string spec = | UnreachCall f -> "call(" ^ f ^ "())" | NoDataRace -> "data-race" | NoOverflow -> "overflow" + | NoTermination -> "termination" in "CHECK( init(main()), LTL(G ! " ^ global_not ^ ") )" diff --git a/src/witness/witness.ml b/src/witness/witness.ml index aff9a01383..04bcb1867b 100644 --- a/src/witness/witness.ml +++ b/src/witness/witness.ml @@ -442,6 +442,23 @@ struct in (module TaskResult:WitnessTaskResult) ) + | NoTermination -> (* TODO: implement this properly*) + let module TrivialArg = + struct + include Arg + let next _ = [] + end + in + let module TaskResult = + struct + module Arg = TrivialArg + let result = Result.Unknown + let invariant _ = Invariant.none + let is_violation _ = false + let is_sink _ = false + end + in + (module TaskResult:WitnessTaskResult) | NoOverflow -> let module TrivialArg = struct diff --git a/tests/regression/55-loop-unrolling/01-simple-cases.c b/tests/regression/55-loop-unrolling/01-simple-cases.c index 3d5ce1b4e4..add8c6a8dd 100644 --- a/tests/regression/55-loop-unrolling/01-simple-cases.c +++ b/tests/regression/55-loop-unrolling/01-simple-cases.c @@ -30,8 +30,7 @@ void example1(void) } __goblint_check(a[0] == 0); // UNKNOWN - lab:__goblint_check(a[3] == 3); // UNKNOWN - goto lab; + __goblint_check(a[3] == 3); // UNKNOWN } // Do-while loop simple example From f419f50c0c26d35835235d6fb76084a7d8546fdb Mon Sep 17 00:00:00 2001 From: karoliineh Date: Thu, 22 Jun 2023 17:28:06 +0300 Subject: [PATCH 0198/1312] Replace print comments with tracing in group_may_race --- src/analyses/raceAnalysis.ml | 1 - src/domains/access.ml | 10 ++++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index 76b93892f6..c004800db6 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -152,7 +152,6 @@ struct | Some outer_memo -> distribute_outer ctx outer_memo | None -> Access.AS.empty () in - M.trace "access" "outer accs = %a" Access.AS.pretty outer_accs; if not (Access.AS.is_empty accs) || (not (Access.AS.is_empty ancestor_accs) && not (Access.AS.is_empty outer_accs)) then ( let memo = (g', offset) in let mem_loc_str = GobPretty.sprint Access.Memo.pretty memo in diff --git a/src/domains/access.ml b/src/domains/access.ml index 9d5147f2b0..b31d69baf6 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -379,6 +379,7 @@ let may_race (conf,(kind: AccessKind.t),loc,e,a) (conf2,(kind2: AccessKind.t),lo true let group_may_race ~ancestor_accs ~ancestor_outer_accs ~outer_accs accs = + if M.tracing then M.tracei "access" "group_may_race\n\tancestors_accs: %a\n\touter_accs: %a\n" AS.pretty ancestor_accs AS.pretty outer_accs; (* BFS to traverse one component with may_race edges *) let rec bfs' ~ancestor_accs ~ancestor_outer_accs ~outer_accs ~accs ~todo ~visited = let may_race_accs ~accs ~todo = @@ -422,16 +423,15 @@ let group_may_race ~ancestor_accs ~ancestor_outer_accs ~outer_accs accs = components comps' ~ancestor_accs:ancestor_accs' ~ancestor_outer_accs:ancestor_outer_accs' ~outer_accs:outer_accs' ~accs:accs' ) in - (* ignore (Pretty.printf "ancestors0: %a outer0: %a\n" AS.pretty ancestor_accs AS.pretty outer_accs); *) let (comps, ancestor_accs, outer_accs) = components [] ~ancestor_accs ~ancestor_outer_accs ~outer_accs ~accs in - (* ignore (Pretty.printf "ancestors: %a outer: %a\n" AS.pretty ancestor_accs AS.pretty outer_accs); *) + if M.tracing then M.trace "access" "components\n\tancestors_accs: %a\n\touter_accs: %a\n" AS.pretty ancestor_accs AS.pretty outer_accs; let rec components_cross comps ~ancestor_accs ~outer_accs = if AS.is_empty ancestor_accs then comps else ( let ancestor_acc = AS.choose ancestor_accs in let (_, ancestor_accs', _, outer_accs', comp) = bfs ~ancestor_accs ~ancestor_outer_accs:(AS.empty ()) ~outer_accs ~accs:(AS.empty ()) ancestor_acc in - (* ignore (Pretty.printf "ancestor: %a comp: %a\n" A.pretty ancestor_acc AS.pretty comp); *) + if M.tracing then M.trace "access" "components_cross\n\tancestors_accs: %a\n\touter_accs: %a\n" AS.pretty ancestor_accs' AS.pretty outer_accs'; let comps' = if AS.cardinal comp > 1 then comp :: comps @@ -441,7 +441,9 @@ let group_may_race ~ancestor_accs ~ancestor_outer_accs ~outer_accs accs = components_cross comps' ~ancestor_accs:ancestor_accs' ~outer_accs:outer_accs' ) in - components_cross comps ~ancestor_accs ~outer_accs + let components_cross = components_cross comps ~ancestor_accs ~outer_accs in + if M.tracing then M.traceu "access" "group_may_race\n"; + components_cross let race_conf accs = assert (not (AS.is_empty accs)); (* group_may_race should only construct non-empty components *) From 62d260fc915aefe9155a952d4a326b974c9d9fb8 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Thu, 22 Jun 2023 16:30:04 +0200 Subject: [PATCH 0199/1312] handled warnings during make --- output.txt | 2233 --------------------- src/analyses/apron/apronAnalysis.apron.ml | 2 - 2 files changed, 2235 deletions(-) delete mode 100644 output.txt diff --git a/output.txt b/output.txt deleted file mode 100644 index 37eb310726..0000000000 --- a/output.txt +++ /dev/null @@ -1,2233 +0,0 @@ -/* Generated by CIL v. 2.0.1-48-g4df989f */ -/* print_CIL_Input is true */ - -#line 31 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned char __u_char; -#line 32 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned short __u_short; -#line 33 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned int __u_int; -#line 34 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __u_long; -#line 37 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef signed char __int8_t; -#line 38 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned char __uint8_t; -#line 39 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef short __int16_t; -#line 40 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned short __uint16_t; -#line 41 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef int __int32_t; -#line 42 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned int __uint32_t; -#line 44 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __int64_t; -#line 45 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __uint64_t; -#line 52 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef __int8_t __int_least8_t; -#line 53 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef __uint8_t __uint_least8_t; -#line 54 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef __int16_t __int_least16_t; -#line 55 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef __uint16_t __uint_least16_t; -#line 56 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef __int32_t __int_least32_t; -#line 57 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef __uint32_t __uint_least32_t; -#line 58 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef __int64_t __int_least64_t; -#line 59 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef __uint64_t __uint_least64_t; -#line 63 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __quad_t; -#line 64 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __u_quad_t; -#line 72 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __intmax_t; -#line 73 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __uintmax_t; -#line 145 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __dev_t; -#line 146 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned int __uid_t; -#line 147 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned int __gid_t; -#line 148 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __ino_t; -#line 149 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __ino64_t; -#line 150 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned int __mode_t; -#line 151 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __nlink_t; -#line 152 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __off_t; -#line 153 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __off64_t; -#line 154 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef int __pid_t; -#line 155 "/usr/include/x86_64-linux-gnu/bits/types.h" -struct __anonstruct___fsid_t_109580352 { - int __val[2] ; -}; -#line 155 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef struct __anonstruct___fsid_t_109580352 __fsid_t; -#line 156 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __clock_t; -#line 157 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __rlim_t; -#line 158 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __rlim64_t; -#line 159 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned int __id_t; -#line 160 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __time_t; -#line 161 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned int __useconds_t; -#line 162 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __suseconds_t; -#line 163 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __suseconds64_t; -#line 165 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef int __daddr_t; -#line 166 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef int __key_t; -#line 169 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef int __clockid_t; -#line 172 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef void *__timer_t; -#line 175 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __blksize_t; -#line 180 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __blkcnt_t; -#line 181 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __blkcnt64_t; -#line 184 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __fsblkcnt_t; -#line 185 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __fsblkcnt64_t; -#line 188 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __fsfilcnt_t; -#line 189 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __fsfilcnt64_t; -#line 192 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __fsword_t; -#line 194 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __ssize_t; -#line 197 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __syscall_slong_t; -#line 199 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned long __syscall_ulong_t; -#line 203 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef __off64_t __loff_t; -#line 204 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef char *__caddr_t; -#line 207 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef long __intptr_t; -#line 210 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef unsigned int __socklen_t; -#line 215 "/usr/include/x86_64-linux-gnu/bits/types.h" -typedef int __sig_atomic_t; -#line 209 "/usr/lib/gcc/x86_64-linux-gnu/11/include/stddef.h" -typedef unsigned long size_t; -#line 10 "/usr/include/x86_64-linux-gnu/bits/types/time_t.h" -typedef __time_t time_t; -#line 11 "/usr/include/x86_64-linux-gnu/bits/types/struct_timespec.h" -struct timespec { - __time_t tv_sec ; - __syscall_slong_t tv_nsec ; -}; -#line 38 "/usr/include/sched.h" -typedef __pid_t pid_t; -#line 23 "/usr/include/x86_64-linux-gnu/bits/types/struct_sched_param.h" -struct sched_param { - int sched_priority ; -}; -#line 32 "/usr/include/x86_64-linux-gnu/bits/cpu-set.h" -typedef unsigned long __cpu_mask; -#line 39 "/usr/include/x86_64-linux-gnu/bits/cpu-set.h" -struct __anonstruct_cpu_set_t_826868708 { - __cpu_mask __bits[1024UL / (8UL * sizeof(__cpu_mask ))] ; -}; -#line 39 "/usr/include/x86_64-linux-gnu/bits/cpu-set.h" -typedef struct __anonstruct_cpu_set_t_826868708 cpu_set_t; -#line 7 "/usr/include/x86_64-linux-gnu/bits/types/clock_t.h" -typedef __clock_t clock_t; -#line 7 "/usr/include/x86_64-linux-gnu/bits/types/struct_tm.h" -struct tm { - int tm_sec ; - int tm_min ; - int tm_hour ; - int tm_mday ; - int tm_mon ; - int tm_year ; - int tm_wday ; - int tm_yday ; - int tm_isdst ; - long tm_gmtoff ; - char const *tm_zone ; -}; -#line 7 "/usr/include/x86_64-linux-gnu/bits/types/clockid_t.h" -typedef __clockid_t clockid_t; -#line 7 "/usr/include/x86_64-linux-gnu/bits/types/timer_t.h" -typedef __timer_t timer_t; -#line 8 "/usr/include/x86_64-linux-gnu/bits/types/struct_itimerspec.h" -struct itimerspec { - struct timespec it_interval ; - struct timespec it_value ; -}; -#line 49 "/usr/include/time.h" -struct sigevent ; -#line 27 "/usr/include/x86_64-linux-gnu/bits/types/__locale_t.h" -struct __locale_data ; -#line 27 "/usr/include/x86_64-linux-gnu/bits/types/__locale_t.h" -struct __locale_struct { - struct __locale_data *__locales[13] ; - unsigned short const *__ctype_b ; - int const *__ctype_tolower ; - int const *__ctype_toupper ; - char const *__names[13] ; -}; -#line 41 "/usr/include/x86_64-linux-gnu/bits/types/__locale_t.h" -typedef struct __locale_struct *__locale_t; -#line 24 "/usr/include/x86_64-linux-gnu/bits/types/locale_t.h" -typedef __locale_t locale_t; -#line 25 "/usr/include/x86_64-linux-gnu/bits/atomic_wide_counter.h" -struct __anonstruct___value32_817613185 { - unsigned int __low ; - unsigned int __high ; -}; -#line 25 "/usr/include/x86_64-linux-gnu/bits/atomic_wide_counter.h" -union __anonunion___atomic_wide_counter_316368393 { - unsigned long long __value64 ; - struct __anonstruct___value32_817613185 __value32 ; -}; -#line 25 "/usr/include/x86_64-linux-gnu/bits/atomic_wide_counter.h" -typedef union __anonunion___atomic_wide_counter_316368393 __atomic_wide_counter; -#line 51 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" -struct __pthread_internal_list { - struct __pthread_internal_list *__prev ; - struct __pthread_internal_list *__next ; -}; -#line 51 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" -typedef struct __pthread_internal_list __pthread_list_t; -#line 57 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" -struct __pthread_internal_slist { - struct __pthread_internal_slist *__next ; -}; -#line 57 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" -typedef struct __pthread_internal_slist __pthread_slist_t; -#line 22 "/usr/include/x86_64-linux-gnu/bits/struct_mutex.h" -struct __pthread_mutex_s { - int __lock ; - unsigned int __count ; - int __owner ; - unsigned int __nusers ; - int __kind ; - short __spins ; - short __elision ; - __pthread_list_t __list ; -}; -#line 23 "/usr/include/x86_64-linux-gnu/bits/struct_rwlock.h" -struct __pthread_rwlock_arch_t { - unsigned int __readers ; - unsigned int __writers ; - unsigned int __wrphase_futex ; - unsigned int __writers_futex ; - unsigned int __pad3 ; - unsigned int __pad4 ; - int __cur_writer ; - int __shared ; - signed char __rwelision ; - unsigned char __pad1[7] ; - unsigned long __pad2 ; - unsigned int __flags ; -}; -#line 94 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" -struct __pthread_cond_s { - __atomic_wide_counter __wseq ; - __atomic_wide_counter __g1_start ; - unsigned int __g_refs[2] ; - unsigned int __g_size[2] ; - unsigned int __g1_orig_size ; - unsigned int __wrefs ; - unsigned int __g_signals[2] ; -}; -#line 105 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" -typedef unsigned int __tss_t; -#line 106 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" -typedef unsigned long __thrd_t; -#line 108 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" -struct __anonstruct___once_flag_826868709 { - int __data ; -}; -#line 108 "/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h" -typedef struct __anonstruct___once_flag_826868709 __once_flag; -#line 27 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -typedef unsigned long pthread_t; -#line 32 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -union __anonunion_pthread_mutexattr_t_488594144 { - char __size[4] ; - int __align ; -}; -#line 32 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -typedef union __anonunion_pthread_mutexattr_t_488594144 pthread_mutexattr_t; -#line 41 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -union __anonunion_pthread_condattr_t_488594145 { - char __size[4] ; - int __align ; -}; -#line 41 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -typedef union __anonunion_pthread_condattr_t_488594145 pthread_condattr_t; -#line 49 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -typedef unsigned int pthread_key_t; -#line 53 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -typedef int pthread_once_t; -#line 56 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -union pthread_attr_t { - char __size[56] ; - long __align ; -}; -#line 62 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -typedef union pthread_attr_t pthread_attr_t; -#line 67 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -union __anonunion_pthread_mutex_t_335460617 { - struct __pthread_mutex_s __data ; - char __size[40] ; - long __align ; -}; -#line 67 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -typedef union __anonunion_pthread_mutex_t_335460617 pthread_mutex_t; -#line 75 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -union __anonunion_pthread_cond_t_951761805 { - struct __pthread_cond_s __data ; - char __size[48] ; - long long __align ; -}; -#line 75 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -typedef union __anonunion_pthread_cond_t_951761805 pthread_cond_t; -#line 86 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -union __anonunion_pthread_rwlock_t_656928968 { - struct __pthread_rwlock_arch_t __data ; - char __size[56] ; - long __align ; -}; -#line 86 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -typedef union __anonunion_pthread_rwlock_t_656928968 pthread_rwlock_t; -#line 93 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -union __anonunion_pthread_rwlockattr_t_145707745 { - char __size[8] ; - long __align ; -}; -#line 93 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -typedef union __anonunion_pthread_rwlockattr_t_145707745 pthread_rwlockattr_t; -#line 103 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -typedef int volatile pthread_spinlock_t; -#line 108 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -union __anonunion_pthread_barrier_t_145707746 { - char __size[32] ; - long __align ; -}; -#line 108 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -typedef union __anonunion_pthread_barrier_t_145707746 pthread_barrier_t; -#line 114 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -union __anonunion_pthread_barrierattr_t_951761806 { - char __size[4] ; - int __align ; -}; -#line 114 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h" -typedef union __anonunion_pthread_barrierattr_t_951761806 pthread_barrierattr_t; -#line 31 "/usr/include/x86_64-linux-gnu/bits/setjmp.h" -typedef long __jmp_buf[8]; -#line 5 "/usr/include/x86_64-linux-gnu/bits/types/__sigset_t.h" -struct __anonstruct___sigset_t_764561023 { - unsigned long __val[1024UL / (8UL * sizeof(unsigned long ))] ; -}; -#line 5 "/usr/include/x86_64-linux-gnu/bits/types/__sigset_t.h" -typedef struct __anonstruct___sigset_t_764561023 __sigset_t; -#line 26 "/usr/include/x86_64-linux-gnu/bits/types/struct___jmp_buf_tag.h" -struct __jmp_buf_tag { - __jmp_buf __jmpbuf ; - int __mask_was_saved ; - __sigset_t __saved_mask ; -}; -#line 37 "/usr/include/pthread.h" -enum __anonenum_34415463 { - PTHREAD_CREATE_JOINABLE = 0, - PTHREAD_CREATE_DETACHED = 1 -} ; -#line 47 -enum __anonenum_508643754 { - PTHREAD_MUTEX_TIMED_NP = 0, - PTHREAD_MUTEX_RECURSIVE_NP = 1, - PTHREAD_MUTEX_ERRORCHECK_NP = 2, - PTHREAD_MUTEX_ADAPTIVE_NP = 3, - PTHREAD_MUTEX_NORMAL = 0, - PTHREAD_MUTEX_RECURSIVE = 1, - PTHREAD_MUTEX_ERRORCHECK = 2, - PTHREAD_MUTEX_DEFAULT = 0 -} ; -#line 69 -enum __anonenum_931900394 { - PTHREAD_MUTEX_STALLED = 0, - PTHREAD_MUTEX_STALLED_NP = 0, - PTHREAD_MUTEX_ROBUST = 1, - PTHREAD_MUTEX_ROBUST_NP = 1 -} ; -#line 81 -enum __anonenum_205214487 { - PTHREAD_PRIO_NONE = 0, - PTHREAD_PRIO_INHERIT = 1, - PTHREAD_PRIO_PROTECT = 2 -} ; -#line 104 -enum __anonenum_25043950 { - PTHREAD_RWLOCK_PREFER_READER_NP = 0, - PTHREAD_RWLOCK_PREFER_WRITER_NP = 1, - PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP = 2, - PTHREAD_RWLOCK_DEFAULT_NP = 0 -} ; -#line 124 -enum __anonenum_436439511 { - PTHREAD_INHERIT_SCHED = 0, - PTHREAD_EXPLICIT_SCHED = 1 -} ; -#line 134 -enum __anonenum_998661166 { - PTHREAD_SCOPE_SYSTEM = 0, - PTHREAD_SCOPE_PROCESS = 1 -} ; -#line 144 -enum __anonenum_146137331 { - PTHREAD_PROCESS_PRIVATE = 0, - PTHREAD_PROCESS_SHARED = 1 -} ; -#line 159 "/usr/include/pthread.h" -struct _pthread_cleanup_buffer { - void (*__routine)(void * ) ; - void *__arg ; - int __canceltype ; - struct _pthread_cleanup_buffer *__prev ; -}; -#line 168 -enum __anonenum_53396917 { - PTHREAD_CANCEL_ENABLE = 0, - PTHREAD_CANCEL_DISABLE = 1 -} ; -#line 175 -enum __anonenum_904563783 { - PTHREAD_CANCEL_DEFERRED = 0, - PTHREAD_CANCEL_ASYNCHRONOUS = 1 -} ; -#line 538 "/usr/include/pthread.h" -struct __cancel_jmp_buf_tag { - __jmp_buf __cancel_jmp_buf ; - int __mask_was_saved ; -}; -#line 544 "/usr/include/pthread.h" -struct __anonstruct___pthread_unwind_buf_t_530692248 { - struct __cancel_jmp_buf_tag __cancel_jmp_buf[1] ; - void *__pad[4] ; -}; -#line 544 "/usr/include/pthread.h" -typedef struct __anonstruct___pthread_unwind_buf_t_530692248 __attribute__((__aligned__)) __pthread_unwind_buf_t; -#line 557 "/usr/include/pthread.h" -struct __pthread_cleanup_frame { - void (*__cancel_routine)(void * ) ; - void *__cancel_arg ; - int __do_it ; - int __cancel_type ; -}; -#line 143 "/usr/lib/gcc/x86_64-linux-gnu/11/include/stddef.h" -typedef long ptrdiff_t; -#line 321 "/usr/lib/gcc/x86_64-linux-gnu/11/include/stddef.h" -typedef int wchar_t; -#line 415 "/usr/lib/gcc/x86_64-linux-gnu/11/include/stddef.h" -struct __anonstruct_max_align_t_896270833 { - long long __max_align_ll __attribute__((__aligned__(__alignof__(long long )))) ; - long double __max_align_ld __attribute__((__aligned__(__alignof__(long double )))) ; -}; -#line 415 "/usr/lib/gcc/x86_64-linux-gnu/11/include/stddef.h" -typedef struct __anonstruct_max_align_t_896270833 max_align_t; -/* compiler builtin: - void __builtin_va_copy(__builtin_va_list , __builtin_va_list ) ; */ -/* compiler builtin: - void *__builtin_frob_return_address(void * ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_and_and_fetch(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_fetch_and_or(...) ; */ -/* compiler builtin: - int __builtin_popcountll(unsigned long long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_or_fetch(...) ; */ -/* compiler builtin: - float __builtin_atanf(float ) ; */ -/* compiler builtin: - float __attribute__((____vector_size____(16))) __builtin_ia32_addps(float __attribute__((____vector_size____(16))) , - float __attribute__((____vector_size____(16))) ) ; */ -/* compiler builtin: - unsigned long __builtin_strcspn(char const * , char const * ) ; */ -/* compiler builtin: - float __builtin_asinf(float ) ; */ -/* compiler builtin: - float __attribute__((____vector_size____(16))) __builtin_ia32_maxps(float __attribute__((____vector_size____(16))) , - float __attribute__((____vector_size____(16))) ) ; */ -/* compiler builtin: - float __attribute__((____vector_size____(16))) __builtin_ia32_unpckhps(float __attribute__((____vector_size____(16))) , - float __attribute__((____vector_size____(16))) ) ; */ -/* compiler builtin: - double __builtin_acos(double ) ; */ -/* compiler builtin: - int __builtin___sprintf_chk(char * , int , unsigned long , char const * , ...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_nand_fetch_8(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_and(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_xor_fetch_16(...) ; */ -/* compiler builtin: - double __builtin_cosh(double ) ; */ -/* compiler builtin: - float __builtin_tanhf(float ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_nand_16(...) ; */ -/* compiler builtin: - void *__builtin_mempcpy(void * , void const * , unsigned long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_sub_fetch_1(...) ; */ -/* compiler builtin: - long double __builtin_sqrtl(long double ) ; */ -/* compiler builtin: - int __builtin_parity(unsigned int ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_or(...) ; */ -/* compiler builtin: - long double __builtin_coshl(long double ) ; */ -/* compiler builtin: - long double __builtin_cosl(long double ) ; */ -/* compiler builtin: - float __builtin_cosf(float ) ; */ -/* compiler builtin: - void __sync_synchronize(...) ; */ -/* compiler builtin: - long double __builtin_acosl(long double ) ; */ -/* compiler builtin: - void *__builtin___mempcpy_chk(void * , void const * , unsigned long , unsigned long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_or_and_fetch(...) ; */ -/* compiler builtin: - int __builtin_clz(unsigned int ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_nand_fetch_4(...) ; */ -/* compiler builtin: - double __builtin_log10(double ) ; */ -/* compiler builtin: - char *__builtin___strcat_chk(char * , char const * , unsigned long ) ; */ -/* compiler builtin: - float __builtin_modff(float , float * ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_sub_fetch_4(...) ; */ -/* compiler builtin: - _Bool __atomic_compare_exchange_n(...) ; */ -/* compiler builtin: - double __builtin_sin(double ) ; */ -/* compiler builtin: - double __builtin_frexp(double , int * ) ; */ -/* compiler builtin: - float __builtin_acosf(float ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_add_and_fetch(...) ; */ -/* compiler builtin: - long double __builtin_sinhl(long double ) ; */ -/* compiler builtin: - char *__builtin___stpcpy_chk(char * , char const * , unsigned long ) ; */ -/* compiler builtin: - void __atomic_signal_fence(int ) ; */ -/* compiler builtin: - double __builtin_fabs(double ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_nand_1(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_and_fetch_16(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_fetch_and_nand(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_xor_fetch_8(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_xor_fetch_2(...) ; */ -/* compiler builtin: - void __atomic_thread_fence(int ) ; */ -/* compiler builtin: - void __atomic_store_16(...) ; */ -/* compiler builtin: - void __builtin_va_start(__builtin_va_list ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_and_8(...) ; */ -/* compiler builtin: - short __builtin_bswap16(short ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_and_fetch_2(...) ; */ -/* compiler builtin: - _Bool __atomic_test_and_set(void * , int ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_add_8(...) ; */ -/* compiler builtin: - int __builtin_ctz(unsigned int ) ; */ -/* compiler builtin: - char *__builtin_strpbrk(char const * , char const * ) ; */ -/* compiler builtin: - char *__builtin_strcpy(char * , char const * ) ; */ -/* compiler builtin: - double __builtin_sqrt(double ) ; */ -/* compiler builtin: - __builtin_va_list __builtin_next_arg(void) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_nand(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_exchange_16(...) ; */ -/* compiler builtin: - void __atomic_clear(_Bool * , int ) ; */ -/* compiler builtin: - void __atomic_store(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_add_fetch_2(...) ; */ -/* compiler builtin: - float __builtin_log10f(float ) ; */ -/* compiler builtin: - long double __builtin_fabsl(long double ) ; */ -/* compiler builtin: - long double __builtin_floorl(long double ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_xor_1(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_and_fetch(...) ; */ -/* compiler builtin: - float __builtin_floorf(float ) ; */ -/* compiler builtin: - _Bool __atomic_compare_exchange_4(...) ; */ -/* compiler builtin: - void *__builtin_memcpy(void * , void const * , unsigned long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_sub_and_fetch(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_nand_and_fetch(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_load_16(...) ; */ -/* compiler builtin: - float __attribute__((____vector_size____(16))) __builtin_ia32_subps(float __attribute__((____vector_size____(16))) , - float __attribute__((____vector_size____(16))) ) ; */ -/* compiler builtin: - int __builtin_parityll(unsigned long long ) ; */ -/* compiler builtin: - void __builtin_va_end(__builtin_va_list ) ; */ -/* compiler builtin: - void __builtin_bzero(void * , unsigned long ) ; */ -/* compiler builtin: - _Bool __atomic_always_lock_free(unsigned long , void * ) ; */ -/* compiler builtin: - int __builtin_strncmp(char const * , char const * , unsigned long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_nand_fetch_16(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_xor_and_fetch(...) ; */ -/* compiler builtin: - int __builtin___vsprintf_chk(char * , int , unsigned long , char const * , - __builtin_va_list ) ; */ -/* compiler builtin: - float __builtin_sqrtf(float ) ; */ -/* compiler builtin: - double __builtin_nans(char const * ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_xor_8(...) ; */ -/* compiler builtin: - double __builtin_exp(double ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_exchange_8(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_exchange_1(...) ; */ -/* compiler builtin: - int __builtin_strcmp(char const * , char const * ) ; */ -/* compiler builtin: - float __builtin_ldexpf(float , int ) ; */ -/* compiler builtin: - float __builtin_powif(float , int ) ; */ -/* compiler builtin: - long double __builtin_log10l(long double ) ; */ -/* compiler builtin: - void *__builtin___memmove_chk(void * , void const * , unsigned long , unsigned long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_fetch_and_and(...) ; */ -/* compiler builtin: - void *__builtin_return_address(unsigned int ) ; */ -/* compiler builtin: - void __atomic_feraiseexcept(int ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_and_fetch_4(...) ; */ -/* compiler builtin: - float __builtin_fabsf(float ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_or_fetch_8(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_or_fetch_4(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_or_fetch_1(...) ; */ -/* compiler builtin: - unsigned long __builtin_object_size(void * , int ) ; */ -/* compiler builtin: - void *__builtin_alloca(unsigned long ) ; */ -/* compiler builtin: - int __builtin_va_arg_pack_len(void) ; */ -/* compiler builtin: - long double __builtin_tanl(long double ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_and_2(...) ; */ -/* compiler builtin: - void __sync_lock_release(...) ; */ -/* compiler builtin: - long double __builtin_modfl(long double , long double * ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_nand_8(...) ; */ -/* compiler builtin: - char *__builtin_stpcpy(char * , char const * ) ; */ -/* compiler builtin: - long double __builtin_sinl(long double ) ; */ -/* compiler builtin: - double __builtin_asin(double ) ; */ -/* compiler builtin: - float __builtin_sinhf(float ) ; */ -/* compiler builtin: - int __builtin_ctzl(unsigned long ) ; */ -/* compiler builtin: - long double __builtin_tanhl(long double ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_add(...) ; */ -/* compiler builtin: - long __builtin_bswap64(long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_nand_fetch_1(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_nand_2(...) ; */ -/* compiler builtin: - float __attribute__((____vector_size____(16))) __builtin_ia32_mulps(float __attribute__((____vector_size____(16))) , - float __attribute__((____vector_size____(16))) ) ; */ -/* compiler builtin: - double __builtin_tan(double ) ; */ -/* compiler builtin: - char *__builtin_strncpy(char * , char const * , unsigned long ) ; */ -/* compiler builtin: - float __builtin_inff(void) ; */ -/* compiler builtin: - void *__builtin___memset_chk(void * , int , unsigned long , unsigned long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_load_n(...) ; */ -/* compiler builtin: - double __builtin_huge_val(void) ; */ -/* compiler builtin: - int __builtin_clzl(unsigned long ) ; */ -/* compiler builtin: - _Bool __atomic_compare_exchange_16(...) ; */ -/* compiler builtin: - float __builtin_frexpf(float , int * ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_exchange_n(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_or_1(...) ; */ -/* compiler builtin: - long double __builtin_fmodl(long double ) ; */ -/* compiler builtin: - double __builtin_atan(double ) ; */ -/* compiler builtin: - int __builtin___fprintf_chk(void * , int , char const * , ...) ; */ -/* compiler builtin: - float __builtin_ceilf(float ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_or_8(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_add_1(...) ; */ -/* compiler builtin: - void __builtin_return(void const * ) ; */ -/* compiler builtin: - long double __builtin_asinl(long double ) ; */ -/* compiler builtin: - int __builtin_ffsll(unsigned long long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_sub_1(...) ; */ -/* compiler builtin: - int __builtin_va_arg_pack(void) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_or_4(...) ; */ -/* compiler builtin: - char *__builtin___strncpy_chk(char * , char const * , unsigned long , unsigned long ) ; */ -/* compiler builtin: - double __builtin_powi(double , int ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_load_2(...) ; */ -/* compiler builtin: - char *__builtin_strchr(char * , int ) ; */ -/* compiler builtin: - char *__builtin___strncat_chk(char * , char const * , unsigned long , unsigned long ) ; */ -/* compiler builtin: - void __atomic_store_2(...) ; */ -/* compiler builtin: - long double __builtin_huge_vall(void) ; */ -/* compiler builtin: - int __builtin_ffsl(unsigned long ) ; */ -/* compiler builtin: - int __builtin___vprintf_chk(int , char const * , __builtin_va_list ) ; */ -/* compiler builtin: - float __attribute__((____vector_size____(16))) __builtin_ia32_unpcklps(float __attribute__((____vector_size____(16))) , - float __attribute__((____vector_size____(16))) ) ; */ -/* compiler builtin: - char *__builtin_strncat(char * , char const * , unsigned long ) ; */ -/* compiler builtin: - int __builtin_ctzll(unsigned long long ) ; */ -/* compiler builtin: - void __builtin_stdarg_start(__builtin_va_list ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_exchange_4(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_fetch_and_xor(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_sub_4(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_and_4(...) ; */ -/* compiler builtin: - long double __builtin_frexpl(long double , int * ) ; */ -/* compiler builtin: - _Bool __atomic_compare_exchange(...) ; */ -/* compiler builtin: - float __builtin_tanf(float ) ; */ -/* compiler builtin: - long double __builtin_logl(long double ) ; */ -/* compiler builtin: - void __builtin_va_arg(__builtin_va_list , unsigned long , void * ) ; */ -/* compiler builtin: - long __builtin_expect(long , long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_load_1(...) ; */ -/* compiler builtin: - _Bool __atomic_compare_exchange_1(...) ; */ -/* compiler builtin: - int __builtin___printf_chk(int , char const * , ...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_xor_2(...) ; */ -/* compiler builtin: - int __builtin___vfprintf_chk(void * , int , char const * , __builtin_va_list ) ; */ -/* compiler builtin: - void __builtin_prefetch(void const * , ...) ; */ -/* compiler builtin: - long double __builtin_nansl(char const * ) ; */ -/* compiler builtin: - double __builtin_fmod(double ) ; */ -/* compiler builtin: - void __atomic_load(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_or_fetch_16(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_add_fetch_16(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_val_compare_and_swap(...) ; */ -/* compiler builtin: - void __atomic_store_4(...) ; */ -/* compiler builtin: - double __builtin_tanh(double ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_and_1(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_nand_4(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_add_16(...) ; */ -/* compiler builtin: - void __builtin_unreachable(void) ; */ -/* compiler builtin: - _Bool __atomic_compare_exchange_2(...) ; */ -/* compiler builtin: - long double __builtin_ldexpl(long double , int ) ; */ -/* compiler builtin: - void *__builtin_apply(void (*)() , void * , unsigned long ) ; */ -/* compiler builtin: - float __builtin_sinf(float ) ; */ -/* compiler builtin: - double __builtin_ceil(double ) ; */ -/* compiler builtin: - void __atomic_exchange(...) ; */ -/* compiler builtin: - long double __builtin_powil(long double , int ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_sub_fetch_8(...) ; */ -/* compiler builtin: - long double __builtin_expl(long double ) ; */ -/* compiler builtin: - int __builtin_constant_p(int ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_sub_16(...) ; */ -/* compiler builtin: - double __builtin_log(double ) ; */ -/* compiler builtin: - float __builtin_expf(float ) ; */ -/* compiler builtin: - int __builtin_types_compatible_p(unsigned long , unsigned long ) ; */ -/* compiler builtin: - long double __builtin_atan2l(long double , long double ) ; */ -/* compiler builtin: - void *__builtin_apply_args(void) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_exchange_2(...) ; */ -/* compiler builtin: - float __builtin_logf(float ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_sub_fetch_2(...) ; */ -/* compiler builtin: - unsigned long __builtin_strlen(char const * ) ; */ -/* compiler builtin: - int __builtin_ffs(unsigned int ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_xor_16(...) ; */ -/* compiler builtin: - double __builtin_inf(void) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_or_16(...) ; */ -/* compiler builtin: - void *__builtin___memcpy_chk(void * , void const * , unsigned long , unsigned long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_load_4(...) ; */ -/* compiler builtin: - void __atomic_store_n(...) ; */ -/* compiler builtin: - void __builtin_trap(void) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_add_4(...) ; */ -/* compiler builtin: - int __builtin_parityl(unsigned long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_nand_fetch_2(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_lock_test_and_set(...) ; */ -/* compiler builtin: - unsigned long __builtin_strspn(char const * , char const * ) ; */ -/* compiler builtin: - void __builtin_varargs_start(__builtin_va_list ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_sub_fetch(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_and_16(...) ; */ -/* compiler builtin: - _Bool __atomic_compare_exchange_8(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_nand_fetch(...) ; */ -/* compiler builtin: - double __builtin_nan(char const * ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_load_8(...) ; */ -/* compiler builtin: - int __builtin___snprintf_chk(char * , unsigned long , int , unsigned long , - char const * , ...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_xor_fetch_4(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_sub_2(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_add_fetch(...) ; */ -/* compiler builtin: - long double __builtin_atanl(long double ) ; */ -/* compiler builtin: - int __builtin_clzll(unsigned long long ) ; */ -/* compiler builtin: - float __builtin_huge_valf(void) ; */ -/* compiler builtin: - float __builtin_coshf(float ) ; */ -/* compiler builtin: - float __builtin_nansf(char const * ) ; */ -/* compiler builtin: - void __atomic_store_8(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_xor_fetch(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_fetch_and_add(...) ; */ -/* compiler builtin: - int __builtin___vsnprintf_chk(char * , unsigned long , int , unsigned long , - char const * , __builtin_va_list ) ; */ -/* compiler builtin: - float __builtin_nanf(char const * ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_or_fetch_2(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_add_fetch_1(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_xor(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_add_fetch_8(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_xor_4(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_sub_8(...) ; */ -/* compiler builtin: - _Bool __sync_bool_compare_and_swap(...) ; */ -/* compiler builtin: - double __builtin_atan2(double , double ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __builtin_tgmath(...) ; */ -/* compiler builtin: - int __builtin_popcountl(unsigned long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_and_fetch_1(...) ; */ -/* compiler builtin: - long double __builtin_ceill(long double ) ; */ -/* compiler builtin: - void __atomic_store_1(...) ; */ -/* compiler builtin: - char *__builtin___strcpy_chk(char * , char const * , unsigned long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_xor_fetch_1(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_or_2(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_sub_fetch_16(...) ; */ -/* compiler builtin: - double __builtin_floor(double ) ; */ -/* compiler builtin: - double __builtin_cos(double ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __sync_fetch_and_sub(...) ; */ -/* compiler builtin: - void *__builtin_memset(void * , int , int ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_add_2(...) ; */ -/* compiler builtin: - long double __builtin_nanl(char const * ) ; */ -/* compiler builtin: - float __builtin_atan2f(float , float ) ; */ -/* compiler builtin: - _Bool __atomic_is_lock_free(unsigned long , void * ) ; */ -/* compiler builtin: - int __builtin_popcount(unsigned int ) ; */ -/* compiler builtin: - double __builtin_sinh(double ) ; */ -/* compiler builtin: - void __builtin_bcopy(void const * , void * , unsigned long ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_and_fetch_8(...) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_fetch_sub(...) ; */ -/* compiler builtin: - void *__builtin_extract_return_addr(void * ) ; */ -/* compiler builtin: - int __builtin_bswap32(int ) ; */ -/* compiler builtin: - double __builtin_ldexp(double , int ) ; */ -/* compiler builtin: - long double __builtin_infl(void) ; */ -/* compiler builtin: - float __builtin_fmodf(float ) ; */ -/* compiler builtin: - void __attribute__((__overloaded__)) __atomic_add_fetch_4(...) ; */ -/* compiler builtin: - void *__builtin_frame_address(unsigned int ) ; */ -#line 1 "lib/goblint/runtime/include/goblint.h" -extern void __goblint_check(int exp ) ; -#line 2 -extern void __goblint_assume(int exp ) ; -#line 3 -extern void __goblint_assert(int exp ) ; -#line 5 -extern void __goblint_assume_join() ; -#line 7 -extern void __goblint_split_begin(int exp ) ; -#line 8 -extern void __goblint_split_end(int exp ) ; -#line 4 "tests/regression/55-loop-unrolling/01-simple-cases.c" -int global ; -#line 8 -void example1(void) ; -#line 9 -void example2(void) ; -#line 10 -void example3(void) ; -#line 11 -void example4(void) ; -#line 12 -void example5(void) ; -#line 13 -void example6(void) ; -#line 14 -void example7(void) ; -#line 15 -void example8(void) ; -#line 16 -void example9(void) ; -#line 17 -void example10(void) ; -#line 6 "tests/regression/55-loop-unrolling/01-simple-cases.c" -int main(void) -{ - - - { -#line 8 - example1(); -#line 9 - example2(); -#line 10 - example3(); -#line 11 - example4(); -#line 12 - example5(); -#line 13 - example6(); -#line 14 - example7(); -#line 15 - example8(); -#line 16 - example9(); -#line 17 - example10(); -#line 18 - return (0); -} -} -#line 22 "tests/regression/55-loop-unrolling/01-simple-cases.c" -void example1(void) -{ - int a[5] ; - int i ; - int term27_5-file_01-simple-cases ; - - { -#line 25 - i = 0; - { -#line 27 - term27_5-file_01-simple-cases = 0; - { -#line 27 - while (1) { - while_continue: /* CIL Label */ ; -#line 27 - if (! (i < 5)) { -#line 27 - goto while_break; - } -#line 27 - term27_5-file_01-simple-cases ++; -#line 27 - term_exit- = term27_5-file_01-simple-cases; -#line 28 - a[i] = i; - } - while_break: /* CIL Label */ ; - } - } -#line 31 - __goblint_check(a[0] == 0); -#line 32 - __goblint_check(a[3] == 3); -#line 33 - return; -} -} -#line 36 "tests/regression/55-loop-unrolling/01-simple-cases.c" -void example2(void) -{ - int a[5] ; - int i ; - int term41_5-file_01-simple-cases ; - - { -#line 39 - i = 0; - { -#line 41 - term41_5-file_01-simple-cases = 0; - { -#line 41 - while (1) { - while_continue: /* CIL Label */ ; -#line 42 - a[i] = i; -#line 43 - i ++; -#line 41 - term41_5-file_01-simple-cases ++; -#line 41 - term_exit- = term41_5-file_01-simple-cases; -#line 41 - if (! (i <= 5)) { -#line 41 - goto while_break; - } - } - while_break: /* CIL Label */ ; - } - } -#line 46 - __goblint_check(a[0] == 0); -#line 47 - __goblint_check(a[3] == 3); -#line 48 - return; -} -} -#line 51 "tests/regression/55-loop-unrolling/01-simple-cases.c" -void example3(void) -{ - int a[10] ; - int i ; - int term56_5-file_01-simple-cases ; - - { -#line 54 - i = 0; - { -#line 56 - term56_5-file_01-simple-cases = 0; - { -#line 56 - while (1) { - while_continue: /* CIL Label */ ; -#line 56 - if (! (i < 5)) { -#line 56 - goto while_break; - } -#line 56 - term56_5-file_01-simple-cases ++; -#line 56 - term_exit- = term56_5-file_01-simple-cases; -#line 57 - a[i] = i; -#line 58 - i ++; - } - while_break: /* CIL Label */ ; - } - } -#line 61 - __goblint_check(a[0] == 0); -#line 62 - __goblint_check(a[3] == 0); -#line 63 - __goblint_check(a[7] == 0); -#line 64 - return; -} -} -#line 67 "tests/regression/55-loop-unrolling/01-simple-cases.c" -void example4(void) -{ - int a[10] ; - int i ; - int first_iteration ; - int term73_5-file_01-simple-cases ; - - { -#line 70 - i = 0; -#line 71 - first_iteration = 1; - { -#line 73 - term73_5-file_01-simple-cases = 0; - { -#line 73 - while (1) { - while_continue: /* CIL Label */ ; -#line 73 - if (! (i < 10)) { -#line 73 - goto while_break; - } -#line 73 - term73_5-file_01-simple-cases ++; -#line 73 - term_exit- = term73_5-file_01-simple-cases; -#line 74 - if (first_iteration == 1) { -#line 74 - __goblint_check(i == 0); - } else -#line 75 - if (i > 5) { -#line 75 - __goblint_check(i == 6); - } -#line 76 - first_iteration = 0; -#line 77 - a[i] = 0; -#line 78 - i ++; - } - while_break: /* CIL Label */ ; - } - } -#line 81 - __goblint_check(a[0] == 0); -#line 82 - __goblint_check(first_iteration == 0); -#line 83 - return; -} -} -#line 88 "tests/regression/55-loop-unrolling/01-simple-cases.c" -void example5(void) -{ - int a[4] ; - int i ; - int top ; - int term94_5-file_01-simple-cases ; - - { -#line 91 - i = 0; -#line 92 - top = 0; - { -#line 94 - term94_5-file_01-simple-cases = 0; - { -#line 94 - while (1) { - while_continue: /* CIL Label */ ; -#line 94 - if (! (i < 4)) { -#line 94 - goto while_break; - } -#line 94 - term94_5-file_01-simple-cases ++; -#line 94 - term_exit- = term94_5-file_01-simple-cases; -#line 95 - a[i] = 0; -#line 96 - top += i; -#line 97 - if (i == 2) { -#line 98 - __goblint_check(top == 3); - } else { -#line 101 - __goblint_check(top == 3); - } -#line 103 - i ++; - } - while_break: /* CIL Label */ ; - } - } -#line 106 - __goblint_check(a[0] == 0); -#line 107 - __goblint_check(a[3] == 0); -#line 108 - __goblint_check(top == 6); -#line 109 - return; -} -} -#line 112 "tests/regression/55-loop-unrolling/01-simple-cases.c" -void example6(void) -{ - int a[5] ; - int i ; - int top ; - int term118_5-file_01-simple-cases ; - - { -#line 115 - i = 0; -#line 116 - top = 0; - { -#line 118 - term118_5-file_01-simple-cases = 0; - { -#line 118 - while (1) { - while_continue: /* CIL Label */ ; -#line 118 - if (! (i < 3)) { -#line 118 - goto while_break; - } -#line 118 - term118_5-file_01-simple-cases ++; -#line 118 - term_exit- = term118_5-file_01-simple-cases; -#line 119 - a[i] = 0; -#line 120 - __goblint_check(a[0] == 0); -#line 121 - i ++; - } - while_break: /* CIL Label */ ; - } - } -#line 124 - __goblint_check(a[0] == 0); -#line 125 - __goblint_check(a[3] == 0); -#line 126 - __goblint_check(top == 6); -#line 127 - return; -} -} -#line 130 "tests/regression/55-loop-unrolling/01-simple-cases.c" -int update(int i ) -{ - - - { -#line 131 - if (i > 5) { -#line 132 - return (0); - } else { -#line 135 - return (1); - } -} -} -#line 138 "tests/regression/55-loop-unrolling/01-simple-cases.c" -void example7(void) -{ - int a[10] ; - int i ; - int tmp ; - int term142_2-file_01-simple-cases ; - - { -#line 141 - i = 0; - { -#line 142 - term142_2-file_01-simple-cases = 0; - { -#line 142 - while (1) { - while_continue: /* CIL Label */ ; -#line 142 - tmp = update(i); -#line 142 - term142_2-file_01-simple-cases ++; -#line 142 - term_exit- = term142_2-file_01-simple-cases; -#line 142 - if (! tmp) { -#line 142 - goto while_break; - } -#line 143 - a[i] = i; -#line 144 - i ++; - } - while_break: /* CIL Label */ ; - } - } -#line 146 - __goblint_check(a[0] == 0); -#line 147 - __goblint_check(a[6] == 0); -#line 148 - return; -} -} -#line 151 "tests/regression/55-loop-unrolling/01-simple-cases.c" -void example8(void) -{ - int a[5] ; - int b[5] ; - int i ; - int j ; - int term159_9-file_01-simple-cases ; - int term156_2-file_01-simple-cases ; - - { -#line 154 - b[0] = 0; -#line 154 - b[1] = 0; -#line 154 - b[2] = 0; -#line 154 - b[3] = 0; -#line 154 - b[4] = 0; -#line 155 - i = 0; - { -#line 156 - term156_2-file_01-simple-cases = 0; - { -#line 156 - while (1) { - while_continue: /* CIL Label */ ; -#line 156 - if (! (i < 5)) { -#line 156 - goto while_break; - } -#line 156 - term156_2-file_01-simple-cases ++; -#line 156 - term_exit- = term156_2-file_01-simple-cases; -#line 157 - a[i] = i; -#line 158 - j = 0; - { -#line 159 - term159_9-file_01-simple-cases = 0; - { -#line 159 - while (1) { - while_continue___0: /* CIL Label */ ; -#line 159 - if (! (j < 5)) { -#line 159 - goto while_break___0; - } -#line 159 - term159_9-file_01-simple-cases ++; -#line 159 - term_exit- = term159_9-file_01-simple-cases; -#line 160 - b[j] += a[i]; -#line 161 - j ++; - } - while_break___0: /* CIL Label */ ; - } - } -#line 163 - i ++; - } - while_break: /* CIL Label */ ; - } - } -#line 165 - return; -} -} -#line 169 "tests/regression/55-loop-unrolling/01-simple-cases.c" -void example9(void) -{ - int a[5] ; - int i ; - int term173_2-file_01-simple-cases ; - - { -#line 172 - i = 0; - { -#line 173 - term173_2-file_01-simple-cases = 0; - { -#line 173 - while (1) { - while_continue: /* CIL Label */ ; -#line 173 - if (! 1) { -#line 173 - goto while_break; - } -#line 173 - term173_2-file_01-simple-cases ++; -#line 173 - term_exit- = term173_2-file_01-simple-cases; -#line 174 - a[i] = i; -#line 175 - i ++; -#line 176 - if (i == 5) { -#line 176 - goto while_break; - } - } - while_break: /* CIL Label */ ; - } - } -#line 178 - return; -} -} -#line 182 "tests/regression/55-loop-unrolling/01-simple-cases.c" -void example10(void) -{ - int a[5] ; - int i ; - int term186_2-file_01-simple-cases ; - - { -#line 185 - i = 0; - { -#line 186 - term186_2-file_01-simple-cases = 0; - { -#line 186 - while (1) { - while_continue: /* CIL Label */ ; -#line 186 - if (! (i < 5)) { -#line 186 - goto while_break; - } -#line 186 - term186_2-file_01-simple-cases ++; -#line 186 - term_exit- = term186_2-file_01-simple-cases; -#line 187 - if (i == 3) { -#line 188 - i ++; -#line 189 - goto while_continue; - } -#line 191 - a[i] = i; -#line 192 - i ++; - } - while_break: /* CIL Label */ ; - } - } -#line 194 - return; -} -} -#line 117 "/usr/include/x86_64-linux-gnu/bits/cpu-set.h" -extern int ( __attribute__((__leaf__)) __sched_cpucount)(size_t __setsize , cpu_set_t const *__setp ) __attribute__((__nothrow__)) ; -#line 119 -extern cpu_set_t *( __attribute__((__leaf__)) __sched_cpualloc)(size_t __count ) __attribute__((__nothrow__)) ; -#line 120 -extern void ( __attribute__((__leaf__)) __sched_cpufree)(cpu_set_t *__set ) __attribute__((__nothrow__)) ; -#line 54 "/usr/include/sched.h" -extern int ( __attribute__((__leaf__)) sched_setparam)(__pid_t __pid , struct sched_param const *__param ) __attribute__((__nothrow__)) ; -#line 58 -extern int ( __attribute__((__leaf__)) sched_getparam)(__pid_t __pid , struct sched_param *__param ) __attribute__((__nothrow__)) ; -#line 61 -extern int ( __attribute__((__leaf__)) sched_setscheduler)(__pid_t __pid , int __policy , - struct sched_param const *__param ) __attribute__((__nothrow__)) ; -#line 65 -extern int ( __attribute__((__leaf__)) sched_getscheduler)(__pid_t __pid ) __attribute__((__nothrow__)) ; -#line 68 -extern int ( __attribute__((__leaf__)) sched_yield)(void) __attribute__((__nothrow__)) ; -#line 71 -extern int ( __attribute__((__leaf__)) sched_get_priority_max)(int __algorithm ) __attribute__((__nothrow__)) ; -#line 74 -extern int ( __attribute__((__leaf__)) sched_get_priority_min)(int __algorithm ) __attribute__((__nothrow__)) ; -#line 78 -extern int ( __attribute__((__leaf__)) sched_rr_get_interval)(__pid_t __pid , struct timespec *__t ) __attribute__((__nothrow__)) ; -#line 72 "/usr/include/time.h" -extern clock_t ( __attribute__((__leaf__)) clock)(void) __attribute__((__nothrow__)) ; -#line 76 -extern time_t ( __attribute__((__leaf__)) time)(time_t *__timer ) __attribute__((__nothrow__)) ; -#line 79 -extern double ( __attribute__((__leaf__)) difftime)(time_t __time1 , time_t __time0 ) __attribute__((__nothrow__, -__const__)) ; -#line 83 -extern time_t ( __attribute__((__leaf__)) mktime)(struct tm *__tp ) __attribute__((__nothrow__)) ; -#line 100 -extern size_t ( __attribute__((__leaf__)) strftime)(char * __restrict __s , size_t __maxsize , - char const * __restrict __format , - struct tm const * __restrict __tp ) __attribute__((__nothrow__)) ; -#line 116 -extern size_t ( __attribute__((__leaf__)) strftime_l)(char * __restrict __s , size_t __maxsize , - char const * __restrict __format , - struct tm const * __restrict __tp , - locale_t __loc ) __attribute__((__nothrow__)) ; -#line 132 -extern struct tm *( __attribute__((__leaf__)) gmtime)(time_t const *__timer ) __attribute__((__nothrow__)) ; -#line 136 -extern struct tm *( __attribute__((__leaf__)) localtime)(time_t const *__timer ) __attribute__((__nothrow__)) ; -#line 154 -extern struct tm *( __attribute__((__leaf__)) gmtime_r)(time_t const * __restrict __timer , - struct tm * __restrict __tp ) __attribute__((__nothrow__)) ; -#line 159 -extern struct tm *( __attribute__((__leaf__)) localtime_r)(time_t const * __restrict __timer , - struct tm * __restrict __tp ) __attribute__((__nothrow__)) ; -#line 179 -extern char *( __attribute__((__leaf__)) asctime)(struct tm const *__tp ) __attribute__((__nothrow__)) ; -#line 183 -extern char *( __attribute__((__leaf__)) ctime)(time_t const *__timer ) __attribute__((__nothrow__)) ; -#line 197 -extern char *( __attribute__((__leaf__)) asctime_r)(struct tm const * __restrict __tp , - char * __restrict __buf ) __attribute__((__nothrow__)) ; -#line 202 -extern char *( __attribute__((__leaf__)) ctime_r)(time_t const * __restrict __timer , - char * __restrict __buf ) __attribute__((__nothrow__)) ; -#line 217 -extern char *__tzname[2] ; -#line 218 -extern int __daylight ; -#line 219 -extern long __timezone ; -#line 224 -extern char *tzname[2] ; -#line 228 -extern void ( __attribute__((__leaf__)) tzset)(void) __attribute__((__nothrow__)) ; -#line 232 -extern int daylight ; -#line 233 -extern long timezone ; -#line 249 -extern time_t ( __attribute__((__leaf__)) timegm)(struct tm *__tp ) __attribute__((__nothrow__)) ; -#line 251 -extern time_t ( __attribute__((__leaf__)) timelocal)(struct tm *__tp ) __attribute__((__nothrow__)) ; -#line 262 -extern int ( __attribute__((__leaf__)) dysize)(int __year ) __attribute__((__nothrow__, -__const__)) ; -#line 272 -extern int nanosleep(struct timespec const *__requested_time , struct timespec *__remaining ) ; -#line 276 -extern int ( __attribute__((__leaf__)) clock_getres)(clockid_t __clock_id , struct timespec *__res ) __attribute__((__nothrow__)) ; -#line 279 -extern int ( __attribute__((__leaf__)) clock_gettime)(clockid_t __clock_id , struct timespec *__tp ) __attribute__((__nothrow__)) ; -#line 282 -extern int ( __attribute__((__leaf__)) clock_settime)(clockid_t __clock_id , struct timespec const *__tp ) __attribute__((__nothrow__)) ; -#line 311 -extern int clock_nanosleep(clockid_t __clock_id , int __flags , struct timespec const *__req , - struct timespec *__rem ) ; -#line 326 -extern int ( __attribute__((__leaf__)) clock_getcpuclockid)(pid_t __pid , clockid_t *__clock_id ) __attribute__((__nothrow__)) ; -#line 331 -extern int ( __attribute__((__leaf__)) timer_create)(clockid_t __clock_id , struct sigevent * __restrict __evp , - timer_t * __restrict __timerid ) __attribute__((__nothrow__)) ; -#line 336 -extern int ( __attribute__((__leaf__)) timer_delete)(timer_t __timerid ) __attribute__((__nothrow__)) ; -#line 340 -extern int ( __attribute__((__leaf__)) timer_settime)(timer_t __timerid , int __flags , - struct itimerspec const * __restrict __value , - struct itimerspec * __restrict __ovalue ) __attribute__((__nothrow__)) ; -#line 345 -extern int ( __attribute__((__leaf__)) timer_gettime)(timer_t __timerid , struct itimerspec *__value ) __attribute__((__nothrow__)) ; -#line 364 -extern int ( __attribute__((__leaf__)) timer_getoverrun)(timer_t __timerid ) __attribute__((__nothrow__)) ; -#line 371 -extern int ( __attribute__((__nonnull__(1), __leaf__)) timespec_get)(struct timespec *__ts , - int __base ) __attribute__((__nothrow__)) ; -#line 202 "/usr/include/pthread.h" -extern int ( __attribute__((__nonnull__(1,3))) pthread_create)(pthread_t * __restrict __newthread , - pthread_attr_t const * __restrict __attr , - void *(*__start_routine)(void * ) , - void * __restrict __arg ) __attribute__((__nothrow__)) ; -#line 211 -extern void pthread_exit(void *__retval ) __attribute__((__noreturn__)) ; -#line 219 -extern int pthread_join(pthread_t __th , void **__thread_return ) ; -#line 269 -extern int ( __attribute__((__leaf__)) pthread_detach)(pthread_t __th ) __attribute__((__nothrow__)) ; -#line 273 -extern pthread_t ( __attribute__((__leaf__)) pthread_self)(void) __attribute__((__nothrow__, -__const__)) ; -#line 276 -extern int ( __attribute__((__leaf__)) pthread_equal)(pthread_t __thread1 , pthread_t __thread2 ) __attribute__((__nothrow__, -__const__)) ; -#line 285 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_attr_init)(pthread_attr_t *__attr ) __attribute__((__nothrow__)) ; -#line 288 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_attr_destroy)(pthread_attr_t *__attr ) __attribute__((__nothrow__)) ; -#line 292 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_attr_getdetachstate)(pthread_attr_t const *__attr , - int *__detachstate ) __attribute__((__nothrow__)) ; -#line 297 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_attr_setdetachstate)(pthread_attr_t *__attr , - int __detachstate ) __attribute__((__nothrow__)) ; -#line 303 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_attr_getguardsize)(pthread_attr_t const *__attr , - size_t *__guardsize ) __attribute__((__nothrow__)) ; -#line 308 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_attr_setguardsize)(pthread_attr_t *__attr , - size_t __guardsize ) __attribute__((__nothrow__)) ; -#line 314 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_attr_getschedparam)(pthread_attr_t const * __restrict __attr , - struct sched_param * __restrict __param ) __attribute__((__nothrow__)) ; -#line 319 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_attr_setschedparam)(pthread_attr_t * __restrict __attr , - struct sched_param const * __restrict __param ) __attribute__((__nothrow__)) ; -#line 324 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_attr_getschedpolicy)(pthread_attr_t const * __restrict __attr , - int * __restrict __policy ) __attribute__((__nothrow__)) ; -#line 329 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_attr_setschedpolicy)(pthread_attr_t *__attr , - int __policy ) __attribute__((__nothrow__)) ; -#line 333 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_attr_getinheritsched)(pthread_attr_t const * __restrict __attr , - int * __restrict __inherit ) __attribute__((__nothrow__)) ; -#line 338 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_attr_setinheritsched)(pthread_attr_t *__attr , - int __inherit ) __attribute__((__nothrow__)) ; -#line 344 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_attr_getscope)(pthread_attr_t const * __restrict __attr , - int * __restrict __scope ) __attribute__((__nothrow__)) ; -#line 349 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_attr_setscope)(pthread_attr_t *__attr , - int __scope ) __attribute__((__nothrow__)) ; -#line 353 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_attr_getstackaddr)(pthread_attr_t const * __restrict __attr , - void ** __restrict __stackaddr ) __attribute__((__nothrow__, -__deprecated__)) ; -#line 361 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_attr_setstackaddr)(pthread_attr_t *__attr , - void *__stackaddr ) __attribute__((__nothrow__, -__deprecated__)) ; -#line 366 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_attr_getstacksize)(pthread_attr_t const * __restrict __attr , - size_t * __restrict __stacksize ) __attribute__((__nothrow__)) ; -#line 373 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_attr_setstacksize)(pthread_attr_t *__attr , - size_t __stacksize ) __attribute__((__nothrow__)) ; -#line 379 -extern int ( __attribute__((__nonnull__(1,2,3), __leaf__)) pthread_attr_getstack)(pthread_attr_t const * __restrict __attr , - void ** __restrict __stackaddr , - size_t * __restrict __stacksize ) __attribute__((__nothrow__)) ; -#line 387 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_attr_setstack)(pthread_attr_t *__attr , - void *__stackaddr , - size_t __stacksize ) __attribute__((__nothrow__)) ; -#line 441 -extern int ( __attribute__((__nonnull__(3), __leaf__)) pthread_setschedparam)(pthread_t __target_thread , - int __policy , - struct sched_param const *__param ) __attribute__((__nothrow__)) ; -#line 446 -extern int ( __attribute__((__nonnull__(2,3), __leaf__)) pthread_getschedparam)(pthread_t __target_thread , - int * __restrict __policy , - struct sched_param * __restrict __param ) __attribute__((__nothrow__)) ; -#line 452 -extern int ( __attribute__((__leaf__)) pthread_setschedprio)(pthread_t __target_thread , - int __prio ) __attribute__((__nothrow__)) ; -#line 509 -int ( __attribute__((__nonnull__(1,2))) pthread_once)(pthread_once_t *once_control , - void (*init_routine)(void) ) __attribute__((__goblint_stub__)) ; -#line 521 -extern int pthread_setcancelstate(int __state , int *__oldstate ) ; -#line 525 -extern int pthread_setcanceltype(int __type , int *__oldtype ) ; -#line 528 -extern int pthread_cancel(pthread_t __th ) ; -#line 533 -extern void pthread_testcancel(void) ; -#line 697 -extern void __pthread_register_cancel(__pthread_unwind_buf_t *__buf ) ; -#line 709 -extern void __pthread_unregister_cancel(__pthread_unwind_buf_t *__buf ) ; -#line 750 -extern void __pthread_unwind_next(__pthread_unwind_buf_t *__buf ) __attribute__((__weak__, -__noreturn__)) ; -#line 766 -extern int __sigsetjmp_cancel(struct __cancel_jmp_buf_tag *__env , int __savemask ) __asm__("__sigsetjmp") __attribute__((__returns_twice__, -__nothrow__)) ; -#line 781 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutex_init)(pthread_mutex_t *__mutex , - pthread_mutexattr_t const *__mutexattr ) __attribute__((__nothrow__)) ; -#line 786 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutex_destroy)(pthread_mutex_t *__mutex ) __attribute__((__nothrow__)) ; -#line 790 -extern int ( __attribute__((__nonnull__(1))) pthread_mutex_trylock)(pthread_mutex_t *__mutex ) __attribute__((__nothrow__)) ; -#line 794 -extern int ( __attribute__((__nonnull__(1))) pthread_mutex_lock)(pthread_mutex_t *__mutex ) __attribute__((__nothrow__)) ; -#line 800 -extern int ( __attribute__((__nonnull__(1,2))) pthread_mutex_timedlock)(pthread_mutex_t * __restrict __mutex , - struct timespec const * __restrict __abstime ) __attribute__((__nothrow__)) ; -#line 835 -extern int ( __attribute__((__nonnull__(1))) pthread_mutex_unlock)(pthread_mutex_t *__mutex ) __attribute__((__nothrow__)) ; -#line 840 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_mutex_getprioceiling)(pthread_mutex_t const * __restrict __mutex , - int * __restrict __prioceiling ) __attribute__((__nothrow__)) ; -#line 847 -extern int ( __attribute__((__nonnull__(1,3), __leaf__)) pthread_mutex_setprioceiling)(pthread_mutex_t * __restrict __mutex , - int __prioceiling , - int * __restrict __old_ceiling ) __attribute__((__nothrow__)) ; -#line 855 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutex_consistent)(pthread_mutex_t *__mutex ) __attribute__((__nothrow__)) ; -#line 874 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutexattr_init)(pthread_mutexattr_t *__attr ) __attribute__((__nothrow__)) ; -#line 878 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutexattr_destroy)(pthread_mutexattr_t *__attr ) __attribute__((__nothrow__)) ; -#line 882 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_mutexattr_getpshared)(pthread_mutexattr_t const * __restrict __attr , - int * __restrict __pshared ) __attribute__((__nothrow__)) ; -#line 888 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutexattr_setpshared)(pthread_mutexattr_t *__attr , - int __pshared ) __attribute__((__nothrow__)) ; -#line 894 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_mutexattr_gettype)(pthread_mutexattr_t const * __restrict __attr , - int * __restrict __kind ) __attribute__((__nothrow__)) ; -#line 901 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutexattr_settype)(pthread_mutexattr_t *__attr , - int __kind ) __attribute__((__nothrow__)) ; -#line 906 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_mutexattr_getprotocol)(pthread_mutexattr_t const * __restrict __attr , - int * __restrict __protocol ) __attribute__((__nothrow__)) ; -#line 913 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutexattr_setprotocol)(pthread_mutexattr_t *__attr , - int __protocol ) __attribute__((__nothrow__)) ; -#line 918 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_mutexattr_getprioceiling)(pthread_mutexattr_t const * __restrict __attr , - int * __restrict __prioceiling ) __attribute__((__nothrow__)) ; -#line 924 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutexattr_setprioceiling)(pthread_mutexattr_t *__attr , - int __prioceiling ) __attribute__((__nothrow__)) ; -#line 930 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_mutexattr_getrobust)(pthread_mutexattr_t const *__attr , - int *__robustness ) __attribute__((__nothrow__)) ; -#line 946 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_mutexattr_setrobust)(pthread_mutexattr_t *__attr , - int __robustness ) __attribute__((__nothrow__)) ; -#line 967 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_rwlock_init)(pthread_rwlock_t * __restrict __rwlock , - pthread_rwlockattr_t const * __restrict __attr ) __attribute__((__nothrow__)) ; -#line 972 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_rwlock_destroy)(pthread_rwlock_t *__rwlock ) __attribute__((__nothrow__)) ; -#line 976 -extern int ( __attribute__((__nonnull__(1))) pthread_rwlock_rdlock)(pthread_rwlock_t *__rwlock ) __attribute__((__nothrow__)) ; -#line 980 -extern int ( __attribute__((__nonnull__(1))) pthread_rwlock_tryrdlock)(pthread_rwlock_t *__rwlock ) __attribute__((__nothrow__)) ; -#line 986 -extern int ( __attribute__((__nonnull__(1,2))) pthread_rwlock_timedrdlock)(pthread_rwlock_t * __restrict __rwlock , - struct timespec const * __restrict __abstime ) __attribute__((__nothrow__)) ; -#line 1023 -extern int ( __attribute__((__nonnull__(1))) pthread_rwlock_wrlock)(pthread_rwlock_t *__rwlock ) __attribute__((__nothrow__)) ; -#line 1027 -extern int ( __attribute__((__nonnull__(1))) pthread_rwlock_trywrlock)(pthread_rwlock_t *__rwlock ) __attribute__((__nothrow__)) ; -#line 1033 -extern int ( __attribute__((__nonnull__(1,2))) pthread_rwlock_timedwrlock)(pthread_rwlock_t * __restrict __rwlock , - struct timespec const * __restrict __abstime ) __attribute__((__nothrow__)) ; -#line 1071 -extern int ( __attribute__((__nonnull__(1))) pthread_rwlock_unlock)(pthread_rwlock_t *__rwlock ) __attribute__((__nothrow__)) ; -#line 1078 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_rwlockattr_init)(pthread_rwlockattr_t *__attr ) __attribute__((__nothrow__)) ; -#line 1082 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_rwlockattr_destroy)(pthread_rwlockattr_t *__attr ) __attribute__((__nothrow__)) ; -#line 1086 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_rwlockattr_getpshared)(pthread_rwlockattr_t const * __restrict __attr , - int * __restrict __pshared ) __attribute__((__nothrow__)) ; -#line 1092 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_rwlockattr_setpshared)(pthread_rwlockattr_t *__attr , - int __pshared ) __attribute__((__nothrow__)) ; -#line 1097 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_rwlockattr_getkind_np)(pthread_rwlockattr_t const * __restrict __attr , - int * __restrict __pref ) __attribute__((__nothrow__)) ; -#line 1103 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_rwlockattr_setkind_np)(pthread_rwlockattr_t *__attr , - int __pref ) __attribute__((__nothrow__)) ; -#line 1112 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_cond_init)(pthread_cond_t * __restrict __cond , - pthread_condattr_t const * __restrict __cond_attr ) __attribute__((__nothrow__)) ; -#line 1117 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_cond_destroy)(pthread_cond_t *__cond ) __attribute__((__nothrow__)) ; -#line 1121 -extern int ( __attribute__((__nonnull__(1))) pthread_cond_signal)(pthread_cond_t *__cond ) __attribute__((__nothrow__)) ; -#line 1125 -extern int ( __attribute__((__nonnull__(1))) pthread_cond_broadcast)(pthread_cond_t *__cond ) __attribute__((__nothrow__)) ; -#line 1133 -extern int ( __attribute__((__nonnull__(1,2))) pthread_cond_wait)(pthread_cond_t * __restrict __cond , - pthread_mutex_t * __restrict __mutex ) ; -#line 1145 -extern int ( __attribute__((__nonnull__(1,2,3))) pthread_cond_timedwait)(pthread_cond_t * __restrict __cond , - pthread_mutex_t * __restrict __mutex , - struct timespec const * __restrict __abstime ) ; -#line 1194 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_condattr_init)(pthread_condattr_t *__attr ) __attribute__((__nothrow__)) ; -#line 1198 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_condattr_destroy)(pthread_condattr_t *__attr ) __attribute__((__nothrow__)) ; -#line 1202 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_condattr_getpshared)(pthread_condattr_t const * __restrict __attr , - int * __restrict __pshared ) __attribute__((__nothrow__)) ; -#line 1208 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_condattr_setpshared)(pthread_condattr_t *__attr , - int __pshared ) __attribute__((__nothrow__)) ; -#line 1213 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_condattr_getclock)(pthread_condattr_t const * __restrict __attr , - __clockid_t * __restrict __clock_id ) __attribute__((__nothrow__)) ; -#line 1219 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_condattr_setclock)(pthread_condattr_t *__attr , - __clockid_t __clock_id ) __attribute__((__nothrow__)) ; -#line 1230 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_spin_init)(pthread_spinlock_t *__lock , - int __pshared ) __attribute__((__nothrow__)) ; -#line 1234 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_spin_destroy)(pthread_spinlock_t *__lock ) __attribute__((__nothrow__)) ; -#line 1238 -extern int ( __attribute__((__nonnull__(1))) pthread_spin_lock)(pthread_spinlock_t *__lock ) __attribute__((__nothrow__)) ; -#line 1242 -extern int ( __attribute__((__nonnull__(1))) pthread_spin_trylock)(pthread_spinlock_t *__lock ) __attribute__((__nothrow__)) ; -#line 1246 -extern int ( __attribute__((__nonnull__(1))) pthread_spin_unlock)(pthread_spinlock_t *__lock ) __attribute__((__nothrow__)) ; -#line 1254 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_barrier_init)(pthread_barrier_t * __restrict __barrier , - pthread_barrierattr_t const * __restrict __attr , - unsigned int __count ) __attribute__((__nothrow__)) ; -#line 1260 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_barrier_destroy)(pthread_barrier_t *__barrier ) __attribute__((__nothrow__)) ; -#line 1264 -extern int ( __attribute__((__nonnull__(1))) pthread_barrier_wait)(pthread_barrier_t *__barrier ) __attribute__((__nothrow__)) ; -#line 1269 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_barrierattr_init)(pthread_barrierattr_t *__attr ) __attribute__((__nothrow__)) ; -#line 1273 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_barrierattr_destroy)(pthread_barrierattr_t *__attr ) __attribute__((__nothrow__)) ; -#line 1277 -extern int ( __attribute__((__nonnull__(1,2), __leaf__)) pthread_barrierattr_getpshared)(pthread_barrierattr_t const * __restrict __attr , - int * __restrict __pshared ) __attribute__((__nothrow__)) ; -#line 1283 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_barrierattr_setpshared)(pthread_barrierattr_t *__attr , - int __pshared ) __attribute__((__nothrow__)) ; -#line 1297 -extern int ( __attribute__((__nonnull__(1), __leaf__)) pthread_key_create)(pthread_key_t *__key , - void (*__destr_function)(void * ) ) __attribute__((__nothrow__)) ; -#line 1302 -extern int ( __attribute__((__leaf__)) pthread_key_delete)(pthread_key_t __key ) __attribute__((__nothrow__)) ; -#line 1305 -extern void *( __attribute__((__leaf__)) pthread_getspecific)(pthread_key_t __key ) __attribute__((__nothrow__)) ; -#line 1308 -extern int ( __attribute__((__leaf__)) pthread_setspecific)(pthread_key_t __key , - void const *__pointer ) __attribute__((__nothrow__, -__access__(__none__,2))) ; -#line 1315 -extern int ( __attribute__((__nonnull__(2), __leaf__)) pthread_getcpuclockid)(pthread_t __thread_id , - __clockid_t *__clock_id ) __attribute__((__nothrow__)) ; -#line 1332 -extern int ( __attribute__((__leaf__)) pthread_atfork)(void (*__prepare)(void) , void (*__parent)(void) , - void (*__child)(void) ) __attribute__((__nothrow__)) ; -#line 5 "lib/libc/stub/src/pthread.c" -int ( __attribute__((__nonnull__(1,2))) pthread_once)(pthread_once_t *once_control , - void (*init_routine)(void) ) __attribute__((__goblint_stub__)) ; -#line 5 "lib/libc/stub/src/pthread.c" -int ( __attribute__((__nonnull__(1,2))) pthread_once)(pthread_once_t *once_control , - void (*init_routine)(void) ) -{ - int top ; - - { -#line 8 - (*init_routine)(); -#line 9 - return (top); -} -} -#line 6 "lib/libc/stub/src/stdlib.c" -void qsort(void *ptr , size_t count , size_t size , int (*comp)(void const * , void const * ) ) __attribute__((__goblint_stub__)) ; -#line 7 -void qsort(void *ptr , size_t count , size_t size , int (*comp)(void const * , void const * ) ) __attribute__((__goblint_stub__)) ; -#line 7 "lib/libc/stub/src/stdlib.c" -void qsort(void *ptr , size_t count , size_t size , int (*comp)(void const * , void const * ) ) -{ - size_t i ; - size_t j ; - size_t i___0 ; - size_t j___0 ; - int r ; - size_t k ; - char *a ; - char *b ; - char c ; - int term10_5-file_stdlib ; - int term9_3-file_stdlib ; - int term21_9-file_stdlib ; - int term17_5-file_stdlib ; - int term16_3-file_stdlib ; - - { -#line 9 - i = (size_t )0; - { -#line 9 - term9_3-file_stdlib = 0; - { -#line 9 - while (1) { - while_continue: /* CIL Label */ ; -#line 9 - if (! (i < count)) { -#line 9 - goto while_break; - } -#line 9 - term9_3-file_stdlib ++; -#line 9 - term_exit- = term9_3-file_stdlib; -#line 10 - j = (size_t )0; - { -#line 10 - term10_5-file_stdlib = 0; - { -#line 10 - while (1) { - while_continue___0: /* CIL Label */ ; -#line 10 - if (! (j < count)) { -#line 10 - goto while_break___0; - } -#line 10 - term10_5-file_stdlib ++; -#line 10 - term_exit- = term10_5-file_stdlib; -#line 11 - (*comp)((void const *)(ptr + i * size), (void const *)(ptr + j * size)); -#line 10 - j ++; - } - while_break___0: /* CIL Label */ ; - } - } -#line 9 - i ++; - } - while_break: /* CIL Label */ ; - } - } -#line 16 - i___0 = (size_t )0; - { -#line 16 - term16_3-file_stdlib = 0; - { -#line 16 - while (1) { - while_continue___1: /* CIL Label */ ; -#line 16 - if (! (i___0 < count)) { -#line 16 - goto while_break___1; - } -#line 16 - term16_3-file_stdlib ++; -#line 16 - term_exit- = term16_3-file_stdlib; -#line 17 - j___0 = (size_t )0; - { -#line 17 - term17_5-file_stdlib = 0; - { -#line 17 - while (1) { - while_continue___2: /* CIL Label */ ; -#line 17 - if (! (j___0 < count)) { -#line 17 - goto while_break___2; - } -#line 17 - term17_5-file_stdlib ++; -#line 17 - term_exit- = term17_5-file_stdlib; -#line 19 - if (r) { -#line 21 - k = (size_t )0; - { -#line 21 - term21_9-file_stdlib = 0; - { -#line 21 - while (1) { - while_continue___3: /* CIL Label */ ; -#line 21 - if (! (k < size)) { -#line 21 - goto while_break___3; - } -#line 21 - term21_9-file_stdlib ++; -#line 21 - term_exit- = term21_9-file_stdlib; -#line 22 - a = (char *)((ptr + i___0 * size) + k); -#line 23 - b = (char *)((ptr + j___0 * size) + k); -#line 24 - c = *a; -#line 25 - *a = *b; -#line 26 - *b = c; -#line 21 - k ++; - } - while_break___3: /* CIL Label */ ; - } - } - } -#line 17 - j___0 ++; - } - while_break___2: /* CIL Label */ ; - } - } -#line 16 - i___0 ++; - } - while_break___1: /* CIL Label */ ; - } - } -#line 33 - return; -} -} -#line 37 -void *bsearch(void const *key , void const *ptr , size_t count , size_t size , - int (*comp)(void const * , void const * ) ) __attribute__((__goblint_stub__)) ; -#line 38 -void *bsearch(void const *key , void const *ptr , size_t count , size_t size , - int (*comp)(void const * , void const * ) ) __attribute__((__goblint_stub__)) ; -#line 38 "lib/libc/stub/src/stdlib.c" -void *bsearch(void const *key , void const *ptr , size_t count , size_t size , - int (*comp)(void const * , void const * ) ) -{ - size_t i ; - void const *a ; - int tmp ; - int term40_3-file_stdlib ; - - { -#line 40 - i = (size_t )0; - { -#line 40 - term40_3-file_stdlib = 0; - { -#line 40 - while (1) { - while_continue: /* CIL Label */ ; -#line 40 - if (! (i < count)) { -#line 40 - goto while_break; - } -#line 40 - term40_3-file_stdlib ++; -#line 40 - term_exit- = term40_3-file_stdlib; -#line 41 - a = ptr + i * size; -#line 42 - tmp = (*comp)(key, a); -#line 42 - if (tmp == 0) { -#line 43 - return ((void *)a); - } -#line 40 - i ++; - } - while_break: /* CIL Label */ ; - } - } -#line 47 - return ((void *)0); -} -} diff --git a/src/analyses/apron/apronAnalysis.apron.ml b/src/analyses/apron/apronAnalysis.apron.ml index 789701d37c..1ac88c66ee 100644 --- a/src/analyses/apron/apronAnalysis.apron.ml +++ b/src/analyses/apron/apronAnalysis.apron.ml @@ -1,7 +1,5 @@ (** Analysis using Apron for integer variables. *) open Analyses -open TerminationPreprocessing -open Cilfacade include RelationAnalysis let spec_module: (module MCPSpec) Lazy.t = From 3707652189843f261fb6d12a8cb2abf59e6d107a Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Thu, 22 Jun 2023 16:47:16 +0200 Subject: [PATCH 0200/1312] indentation --- src/analyses/apron/apronAnalysis.apron.ml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/analyses/apron/apronAnalysis.apron.ml b/src/analyses/apron/apronAnalysis.apron.ml index 1ac88c66ee..1c31e975f2 100644 --- a/src/analyses/apron/apronAnalysis.apron.ml +++ b/src/analyses/apron/apronAnalysis.apron.ml @@ -1,5 +1,7 @@ (** Analysis using Apron for integer variables. *) + open Analyses + include RelationAnalysis let spec_module: (module MCPSpec) Lazy.t = From f7dab71875d61ae7c24f3221fb5643b7b372e345 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Thu, 22 Jun 2023 16:56:26 +0200 Subject: [PATCH 0201/1312] indentation --- src/analyses/apron/apronAnalysis.apron.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/analyses/apron/apronAnalysis.apron.ml b/src/analyses/apron/apronAnalysis.apron.ml index 1c31e975f2..62ae96f400 100644 --- a/src/analyses/apron/apronAnalysis.apron.ml +++ b/src/analyses/apron/apronAnalysis.apron.ml @@ -1,9 +1,10 @@ -(** Analysis using Apron for integer variables. *) +(** {{!RelationAnalysis} Relational integer value analysis} using {!Apron} domains ([apron]). *) open Analyses include RelationAnalysis + let spec_module: (module MCPSpec) Lazy.t = lazy ( let module Man = (val ApronDomain.get_manager ()) in From 5b0d72ff32fd06809751696b5713a5a5aab897dc Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Thu, 22 Jun 2023 18:16:03 +0200 Subject: [PATCH 0202/1312] Indentation with ocp-indent --- src/analyses/apron/apronAnalysis.apron.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/apron/apronAnalysis.apron.ml b/src/analyses/apron/apronAnalysis.apron.ml index 62ae96f400..bec0c4ec57 100644 --- a/src/analyses/apron/apronAnalysis.apron.ml +++ b/src/analyses/apron/apronAnalysis.apron.ml @@ -36,7 +36,7 @@ let after_config () = let module Spec = (val get_spec ()) in MCP.register_analysis (module Spec : MCPSpec); GobConfig.set_string "ana.path_sens[+]" (Spec.name ()) - + let _ = AfterConfig.register after_config From 8a320eb3a9f5f8cd26fe4414ec5d26f95b4e16f6 Mon Sep 17 00:00:00 2001 From: Isidor Zweckstetter Date: Thu, 22 Jun 2023 21:08:00 +0200 Subject: [PATCH 0203/1312] added sv-comp compatability --- src/framework/analysisState.ml | 2 +- src/framework/constraints.ml | 4 ++-- src/util/options.schema.json | 4 ++-- src/witness/svcompSpec.ml | 2 +- src/witness/witness.ml | 33 +++++++++++++++++++++++---------- 5 files changed, 29 insertions(+), 16 deletions(-) diff --git a/src/framework/analysisState.ml b/src/framework/analysisState.ml index ddecf1752a..3f577d79f4 100644 --- a/src/framework/analysisState.ml +++ b/src/framework/analysisState.ml @@ -8,7 +8,7 @@ let should_warn = ref false let svcomp_may_overflow = ref false (** TODO:**) -let svcomp_must_terminate = ref true +let svcomp_may_not_terminate = ref false (** A hack to see if we are currently doing global inits *) let global_initialization = ref false diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 865b0405ee..f6d920d74f 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1734,7 +1734,7 @@ struct let rec iter_call (path_visited_calls: LS.t) (call:T (CilType.Fundec) (S.C).t) = let ((fundec_e:fundec), (context_e: C.t)) = call in (*unpack tuple for later use*) if LS.mem call path_visited_calls then ( - AnalysisState.svcomp_must_terminate := false; + AnalysisState.svcomp_may_not_terminate := true; (*Cycle found*) let msgs = [ @@ -1770,7 +1770,7 @@ struct let ret = ctx.ask Queries.MustTermProg in (* check result of loop analysis *) if not ret then - (AnalysisState.svcomp_must_terminate := false; + (AnalysisState.svcomp_may_not_terminate := true; let msgs = [ (Pretty.dprintf "The program might not terminate! (Loop analysis)\n", Some (M.Location.CilLocation locUnknown)); diff --git a/src/util/options.schema.json b/src/util/options.schema.json index 3efaef2e83..8ec398b3ad 100644 --- a/src/util/options.schema.json +++ b/src/util/options.schema.json @@ -566,7 +566,7 @@ "title": "ana.sv-comp.enabled", "description": "SV-COMP mode", "type": "boolean", - "default": false + "default": true }, "functions": { "title": "ana.sv-comp.functions", @@ -581,7 +581,7 @@ "title": "ana.specification", "description": "SV-COMP specification (path or string)", "type": "string", - "default": "" + "default": "/home/isidor/goblint/goblint-analyzer/tests/sv-comp/no-termination.prp" }, "wp": { "title": "ana.wp", diff --git a/src/witness/svcompSpec.ml b/src/witness/svcompSpec.ml index eec667c5a6..f8791d065e 100644 --- a/src/witness/svcompSpec.ml +++ b/src/witness/svcompSpec.ml @@ -17,7 +17,7 @@ let of_string s = NoDataRace else if global_not = "overflow" then NoOverflow - else if global_not = "termination" then (*TODO: does this even work?*) + else if global_not = "termination" then NoTermination else let call_regex = Str.regexp "call(\\(.*\\)())" in diff --git a/src/witness/witness.ml b/src/witness/witness.ml index 04bcb1867b..f0e2f0f799 100644 --- a/src/witness/witness.ml +++ b/src/witness/witness.ml @@ -449,16 +449,29 @@ struct let next _ = [] end in - let module TaskResult = - struct - module Arg = TrivialArg - let result = Result.Unknown - let invariant _ = Invariant.none - let is_violation _ = false - let is_sink _ = false - end - in - (module TaskResult:WitnessTaskResult) + if not !AnalysisState.svcomp_may_not_terminate then + let module TaskResult = + struct + module Arg = Arg + let result = Result.True + let invariant = find_invariant + let is_violation _ = false + let is_sink _ = false + end + in + (module TaskResult:WitnessTaskResult) + else ( + let module TaskResult = + struct + module Arg = TrivialArg + let result = Result.Unknown + let invariant _ = Invariant.none + let is_violation _ = false + let is_sink _ = false + end + in + (module TaskResult:WitnessTaskResult) + ) | NoOverflow -> let module TrivialArg = struct From 5d3f25cba243f67a14e8446fc6211faec2500116 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Thu, 22 Jun 2023 22:01:03 +0200 Subject: [PATCH 0204/1312] Fix indentation --- src/framework/constraints.ml | 78 ++++++++++++++++++------------------ src/witness/witness.ml | 44 ++++++++++---------- 2 files changed, 61 insertions(+), 61 deletions(-) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index ab6931bf70..ce3ecadc1e 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1698,19 +1698,19 @@ end (** Add cycle detection in the function call graph to a analysis *) module RecursionTermLifter (S: Spec) : Spec with module D = S.D - and module C = S.C + and module C = S.C = (*global invariants: - - V -> G - - fundec -> Map (S.C) (Set (fundec * S.C)) + - V -> G + - fundec -> Map (S.C) (Set (fundec * S.C)) Therefore: g -> {c' -> {(f, c)}} in case f, c --> g, c' *) struct include S - module V = - struct + module V = + struct include GVarF(S.V) end @@ -1718,17 +1718,17 @@ struct let name () = "termination" - let conv (ctx: (_, G.t, _, V.t) ctx): (_, S.G.t, _, S.V.t) ctx = + let conv (ctx: (_, G.t, _, V.t) ctx): (_, S.G.t, _, S.V.t) ctx = { ctx with global = (fun v -> G.s (ctx.global (V.spec v))); sideg = (fun v g -> ctx.sideg (V.spec v) (G.create_s g)); } - let cycleDetection ctx v v' = - let module LH = Hashtbl.Make (T (CilType.Fundec) (S.C)) in - let module LS = Set.Make (T (CilType.Fundec) (S.C)) in + let cycleDetection ctx v v' = + let module LH = Hashtbl.Make (T (CilType.Fundec) (S.C)) in + let module LS = Set.Make (T (CilType.Fundec) (S.C)) in (* find all cycles/SCCs *) - let global_visited_calls = LH.create 100 in + let global_visited_calls = LH.create 100 in (* DFS *) let rec iter_call (path_visited_calls: LS.t) (call:T (CilType.Fundec) (S.C).t) = @@ -1736,7 +1736,7 @@ struct if LS.mem call path_visited_calls then ( AnalysisState.svcomp_may_not_terminate := true; (*Cycle found*) - let msgs = + let msgs = [ (Pretty.dprintf "The program might not terminate! (Fundec %a is contained in a call graph cycle)\n" CilType.Fundec.pretty fundec_e, Some (M.Location.CilLocation fundec_e.svar.vdecl)); ] in @@ -1744,25 +1744,25 @@ struct else if not (LH.mem global_visited_calls call) then begin try LH.replace global_visited_calls call (); - let new_path_visited_calls = LS.add call path_visited_calls in - let fundec_e_typeV: V.t = V.relift (`Right fundec_e) in - let gmap_opt = G.base2 (ctx.global (fundec_e_typeV)) in + let new_path_visited_calls = LS.add call path_visited_calls in + let fundec_e_typeV: V.t = V.relift (`Right fundec_e) in + let gmap_opt = G.base2 (ctx.global (fundec_e_typeV)) in let gmap = Option.get (gmap_opt) in (*might be empty*) - let callers: G.CSet.t = G.CMap.find (context_e) gmap in + let callers: G.CSet.t = G.CMap.find (context_e) gmap in G.CSet.iter (fun to_call -> iter_call new_path_visited_calls to_call ) callers; with Invalid_argument _ -> () (* path ended: no cycle*) - end + end in - try - let gmap_opt = G.base2 (ctx.global (v)) in - let gmap = Option.get (gmap_opt) in - G.CMap.iter(fun key value -> - let call = (v', key) in - iter_call LS.empty call - ) gmap (* try all fundec + context pairs that are in the map *) - with Invalid_argument _ -> () (* path ended: no cycle*) + try + let gmap_opt = G.base2 (ctx.global (v)) in + let gmap = Option.get (gmap_opt) in + G.CMap.iter(fun key value -> + let call = (v', key) in + iter_call LS.empty call + ) gmap (* try all fundec + context pairs that are in the map *) + with Invalid_argument _ -> () (* path ended: no cycle*) let query ctx (type a) (q: a Queries.t): a Queries.result = match q with @@ -1770,18 +1770,18 @@ struct (* check result of loop analysis *) if not (ctx.ask Queries.MustTermProg) then (AnalysisState.svcomp_may_not_terminate := true; - let msgs = - [ - (Pretty.dprintf "The program might not terminate! (Loop analysis)\n", Some (M.Location.CilLocation locUnknown)); - ] in - M.msg_group Warning ~category:NonTerminating "Possibly non terminating loops" msgs - ); + let msgs = + [ + (Pretty.dprintf "The program might not terminate! (Loop analysis)\n", Some (M.Location.CilLocation locUnknown)); + ] in + M.msg_group Warning ~category:NonTerminating "Possibly non terminating loops" msgs + ); let v: V.t = Obj.obj v in begin match v with | `Left v' -> - S.query (conv ctx) (WarnGlobal (Obj.repr v')) + S.query (conv ctx) (WarnGlobal (Obj.repr v')) | `Right v' -> cycleDetection ctx v v' (* Note: to make it more efficient, one could only execute the cycle detection in case the loop analysis returns true, because otherwise the program will probably not terminate anyway*) - end + end | InvariantGlobal v -> let v: V.t = Obj.obj v in begin match v with @@ -1795,30 +1795,30 @@ struct let branch ctx = S.branch (conv ctx) let assign ctx = S.assign (conv ctx) let vdecl ctx = S.vdecl (conv ctx) - + (* c = context t = set of tuples (fundec * context) *) let side_context sideg f c t = if !AnalysisState.postsolving then sideg (V.contexts f) (G.create_contexts (G.CMap.singleton (c) (t))) - - let enter ctx = S.enter (conv ctx) + + let enter ctx = S.enter (conv ctx) let paths_as_set ctx = S.paths_as_set (conv ctx) let body ctx = S.body (conv ctx) let return ctx = S.return (conv ctx) - let combine_env ctx r fe f args fc es f_ask = + let combine_env ctx r fe f args fc es f_ask = if !AnalysisState.postsolving then let c_r: S.C.t = ctx.context () in (*Caller context*) let nodeF = ctx.node in let fd_r : fundec = Node.find_fundec nodeF in (*Caller fundec*) let c_e: S.C.t = Option.get fc in (*Callee context*) let fd_e : fundec = f in (*Callee fundec*) - let tup: (fundec * S.C.t) = (fd_r, c_r) in - let t = G.CSet.singleton (tup) in + let tup: (fundec * S.C.t) = (fd_r, c_r) in + let t = G.CSet.singleton (tup) in side_context ctx.sideg fd_e (c_e) t; S.combine_env (conv ctx) r fe f args fc es f_ask - else + else S.combine_env (conv ctx) r fe f args fc es f_ask let combine_assign ctx = S.combine_assign (conv ctx) let special ctx = S.special (conv ctx) diff --git a/src/witness/witness.ml b/src/witness/witness.ml index f0e2f0f799..b62b2b54cd 100644 --- a/src/witness/witness.ml +++ b/src/witness/witness.ml @@ -23,7 +23,7 @@ let write_file filename (module Task:Task) (module TaskResult:WitnessTaskResult) let module IsInteresting = struct (* type node = N.t - type edge = TaskResult.Arg.Edge.t *) + type edge = TaskResult.Arg.Edge.t *) let minwitness = get_bool "witness.minimize" let is_interesting_real from_node edge to_node = (* TODO: don't duplicate this logic with write_node, write_edge *) @@ -59,11 +59,11 @@ let write_file filename (module Task:Task) (module TaskResult:WitnessTaskResult) let module GML = XmlGraphMlWriter in let module GML = (val match get_string "witness.id" with - | "node" -> - (module ArgNodeGraphMlWriter (N) (GML) : GraphMlWriter with type node = N.t) - | "enumerate" -> - (module EnumerateNodeGraphMlWriter (N) (GML)) - | _ -> failwith "witness.id: illegal value" + | "node" -> + (module ArgNodeGraphMlWriter (N) (GML) : GraphMlWriter with type node = N.t) + | "enumerate" -> + (module EnumerateNodeGraphMlWriter (N) (GML)) + | _ -> failwith "witness.id: illegal value" ) in let module GML = DeDupGraphMlWriter (N) (GML) in @@ -107,16 +107,16 @@ let write_file filename (module Task:Task) (module TaskResult:WitnessTaskResult) GML.write_key g "edge" "goblintLine" "string" None; (* TODO: remove *) (* GML.write_key g "edge" "enterFunction2" "string" None; - GML.write_key g "edge" "returnFromFunction2" "string" None; *) + GML.write_key g "edge" "returnFromFunction2" "string" None; *) GML.start_graph g; GML.write_metadata g "witness-type" ( - match TaskResult.result with - | Result.True -> "correctness_witness" - | Result.False _ -> "violation_witness" - | Result.Unknown -> "unknown_witness" - ); + match TaskResult.result with + | Result.True -> "correctness_witness" + | Result.False _ -> "violation_witness" + | Result.Unknown -> "unknown_witness" + ); GML.write_metadata g "sourcecodelang" "C"; GML.write_metadata g "producer" (Printf.sprintf "Goblint (%s)" Version.goblint); GML.write_metadata g "specification" (Svcomp.Specification.to_string Task.specification); @@ -141,7 +141,7 @@ let write_file filename (module Task:Task) (module TaskResult:WitnessTaskResult) | Statement _, `Lifted i -> let i = InvariantCil.exp_replace_original_name i in [("invariant", CilType.Exp.show i); - ("invariant.scope", (Node.find_fundec cfgnode).svar.vname)] + ("invariant.scope", (Node.find_fundec cfgnode).svar.vname)] | _ -> (* ignore entry and return invariants, variables of wrong scopes *) (* TODO: don't? fix scopes? *) @@ -150,10 +150,10 @@ let write_file filename (module Task:Task) (module TaskResult:WitnessTaskResult) [] end; (* begin match cfgnode with - | Statement s -> + | Statement s -> [("sourcecode", GobPretty.sprint Basetype.CilStmt.pretty s)] (* TODO: sourcecode not official? especially on node? *) - | _ -> [] - end; *) + | _ -> [] + end; *) (* violation actually only allowed in violation witness *) (* maybe should appear on from_node of entry edge instead *) begin if TaskResult.is_violation node then @@ -170,7 +170,7 @@ let write_file filename (module Task:Task) (module TaskResult:WitnessTaskResult) | Statement stmt -> Printf.sprintf "s%d" stmt.sid | Function f -> Printf.sprintf "ret%d%s" f.vid f.vname | FunctionEntry f -> Printf.sprintf "fun%d%s" f.vid f.vname - )] *) + )] *) (* [("goblintNode", N.to_string node)] *) ]) in @@ -213,9 +213,9 @@ let write_file filename (module Task:Task) (module TaskResult:WitnessTaskResult) (* enter and return on other side of nodes, more correct loc (startline) but had some scope problem? *) (* | MyARG.CFGEdge (Entry f) -> - [("enterFunction2", f.svar.vname)] - | MyARG.CFGEdge (Ret (_, f)) -> - [("returnFromFunction2", f.svar.vname)] *) + [("enterFunction2", f.svar.vname)] + | MyARG.CFGEdge (Ret (_, f)) -> + [("returnFromFunction2", f.svar.vname)] *) | _ -> [] end; [("goblintEdge", Arg.Edge.to_string edge)] @@ -394,12 +394,12 @@ struct struct let path = observer_path end - ) + ) in MCP.register_analysis (module Spec); (* TODO: don't modify JSON but have ref vars for these instead *) (* GobConfig.set_list "ana.activated" (Json.Build.string (Spec.name ()) :: GobConfig.get_list "ana.activated"); - GobConfig.set_list "ana.path_sens" (Json.Build.string (Spec.name ()) :: GobConfig.get_list "ana.path_sens"); *) + GobConfig.set_list "ana.path_sens" (Json.Build.string (Spec.name ()) :: GobConfig.get_list "ana.path_sens"); *) (* TODO: don't append to end; currently done to get observer order to be nice *) GobConfig.set_list "ana.activated" (GobConfig.get_list "ana.activated" @ [`String (Spec.name ())]); GobConfig.set_list "ana.path_sens" (GobConfig.get_list "ana.path_sens" @ [`String (Spec.name ())]); From 7bb5697b51950921c51e638aaebefe4b80ce3a4c Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 27 Jun 2023 16:23:52 +0200 Subject: [PATCH 0205/1312] Add first rough (semi-working) memory OOB analysis --- src/analyses/memOutOfBounds.ml | 194 +++++++++++++++++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 src/analyses/memOutOfBounds.ml diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml new file mode 100644 index 0000000000..db4066d442 --- /dev/null +++ b/src/analyses/memOutOfBounds.ml @@ -0,0 +1,194 @@ +open GoblintCil +open Analyses + +module Spec = +struct + include Analyses.IdentitySpec + + module D = Lattice.Unit(*ValueDomain.AddrSetDomain*) + module C = Lattice.Unit + + (* TODO: Do this later *) + let context _ _ = () + + let name () = "memOutOfBounds" + + (* HELPER FUNCTIONS *) + + let exp_points_to_heap ctx (exp:exp) = + match ctx.ask (Queries.MayPointTo exp) with + | a when not (Queries.LS.is_top a) && not (Queries.LS.mem (dummyFunDec.svar, `NoOffset) a) -> + Queries.LS.elements a + |> List.map fst + |> List.exists (fun x -> ctx.ask (Queries.IsHeapVar x)) + | _ -> false + + let lval_points_to_heap ctx (lval:lval) = exp_points_to_heap ctx (mkAddrOf lval) + + let rec exp_contains_a_ptr (exp:exp) = + match exp with + | Const _ + | SizeOf _ + | SizeOfStr _ + | AlignOf _ + | AddrOfLabel _ -> false + | Real e + | Imag e + | SizeOfE e + | AlignOfE e + | UnOp (_, e, _) + | CastE (_, e) -> exp_contains_a_ptr e + | BinOp (_, e1, e2, _) -> + exp_contains_a_ptr e1 || exp_contains_a_ptr e2 + | Question (e1, e2, e3, _) -> + exp_contains_a_ptr e1 || exp_contains_a_ptr e2 || exp_contains_a_ptr e3 + | Lval lval + | AddrOf lval + | StartOf lval -> lval_contains_a_ptr lval + + and lval_contains_a_ptr (lval:lval) = + let lval_is_of_ptr_type lval = + match typeOfLval lval with + | TPtr _ -> true + | _ -> false + in + let (host, offset) = lval in + let host_contains_a_ptr h = + match h with + | Var v -> isPointerType v.vtype + | Mem e -> exp_contains_a_ptr e + in + let rec offset_contains_a_ptr o = + match o with + | NoOffset -> false + | Index (e, o) -> exp_contains_a_ptr e || offset_contains_a_ptr o + | Field (f, o) -> isPointerType f.ftype || offset_contains_a_ptr o + in + lval_is_of_ptr_type lval || host_contains_a_ptr host || offset_contains_a_ptr offset + + let calc_lval_type_size (lval:lval) = + begin try + let t = typeOfLval lval in + match sizeOf t with + | Const (CInt(i, _, _)) -> Some (cilint_to_int i) + | _ -> None + with _ -> None + end + + let get_ptr_size_for_lval ctx (lval:lval) = + match lval_points_to_heap ctx lval with + | true -> (* We're dealing with a ptr that points to the heap *) + begin match ctx.ask (Queries.BlobSize (mkAddrOf lval)) with + | a when not (Queries.ID.is_top a) -> + begin match Queries.ID.to_int a with + | Some i -> Some (IntOps.BigIntOps.to_int i) + | None -> None + end + | _ -> None + end + (* Assumption here is that if it's not a heap ptr, then it's a stack ptr and the ptr size would be the lval type's size *) + | false -> calc_lval_type_size lval + + let rec get_offset_size ctx offset = + match offset with + | NoOffset -> Some 0 + | Index (e, o) -> + begin match ctx.ask (Queries.EvalInt e) with + | a when not (Queries.ID.is_top a) -> + begin match Queries.ID.to_int a with + | Some i -> + begin match get_offset_size ctx o with + | Some os -> Some (IntOps.BigIntOps.to_int i + os) + | None -> None + end + | None -> None + end + | _ -> None + end + | Field (f, o) -> + let f_size = + begin match sizeOf f.ftype with + | Const (CInt (i, _, _)) -> Some (cilint_to_int i) + | _ -> None + end + in + begin match f_size, get_offset_size ctx o with + | Some fs, Some os -> Some (fs + os) + | _ -> None + end + + let check_lval_for_oob_access ctx (lval:lval) = + match lval_contains_a_ptr lval with + | false -> () + | true -> + let (_, offset) = lval in + begin match get_ptr_size_for_lval ctx lval, get_offset_size ctx offset with + | _, None -> M.warn "Offset unknown, potential out-of-bounds access for lval %a" CilType.Lval.pretty lval + | None, _ -> M.warn "Pointer size unknown, potential out-of-bounds access for lval %a" CilType.Lval.pretty lval + | Some pi, Some oi -> + if oi > pi then + M.warn "Must out-of-bounds memory access: Offset size (%d) is larger than pointer's memory size (%d) for lval %a" oi pi CilType.Lval.pretty lval + end + + let rec check_exp_for_oob_access ctx (exp:exp) = + match exp with + | Const _ + | SizeOf _ + | SizeOfStr _ + | AlignOf _ + | AddrOfLabel _ -> () + | Real e + | Imag e + | SizeOfE e + | AlignOfE e + | UnOp (_, e, _) + | CastE (_, e) -> check_exp_for_oob_access ctx e + | BinOp (_, e1, e2, _) -> + check_exp_for_oob_access ctx e1; + check_exp_for_oob_access ctx e2 + | Question (e1, e2, e3, _) -> + check_exp_for_oob_access ctx e1; + check_exp_for_oob_access ctx e2; + check_exp_for_oob_access ctx e3 + | Lval lval + | StartOf lval + | AddrOf lval -> check_lval_for_oob_access ctx lval + + + (* TRANSFER FUNCTIONS *) + + let assign ctx (lval:lval) (rval:exp) : D.t = + check_lval_for_oob_access ctx lval; + check_exp_for_oob_access ctx rval; + ctx.local + + let branch ctx (exp:exp) (tv:bool) : D.t = + check_exp_for_oob_access ctx exp; + ctx.local + + let return ctx (exp:exp option) (f:fundec) : D.t = + Option.iter (fun x -> check_exp_for_oob_access ctx x) exp; + ctx.local + + let special ctx (lval:lval option) (f:varinfo) (arglist:exp list) : D.t = + Option.iter (fun x -> check_lval_for_oob_access ctx x) lval; + List.iter (fun arg -> check_exp_for_oob_access ctx arg) arglist; + ctx.local + + let enter ctx (lval:lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = + [ctx.local, ctx.local] + + let combine_env ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (callee_local:D.t) (f_ask:Queries.ask) : D.t = + ctx.local + + let combine_assign ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (callee_local:D.t) (f_ask:Queries.ask) : D.t = + Option.iter (fun x -> check_lval_for_oob_access ctx x) lval; + List.iter (fun arg -> check_exp_for_oob_access ctx arg) args; + ctx.local + + let startstate v = (*D.empty*) () + let exitstate v = (*D.empty*) () +end + +let _ = + MCP.register_analysis (module Spec : MCPSpec) \ No newline at end of file From a5c693efd417720c2baccfd19782cc8592f1f262 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 27 Jun 2023 16:24:21 +0200 Subject: [PATCH 0206/1312] Add 2 simple regression tests for memory OOB --- tests/regression/73-mem-oob/01-oob-heap-simple.c | 14 ++++++++++++++ tests/regression/73-mem-oob/02-oob-stack-simple.c | 12 ++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 tests/regression/73-mem-oob/01-oob-heap-simple.c create mode 100644 tests/regression/73-mem-oob/02-oob-stack-simple.c diff --git a/tests/regression/73-mem-oob/01-oob-heap-simple.c b/tests/regression/73-mem-oob/01-oob-heap-simple.c new file mode 100644 index 0000000000..91d9c7605a --- /dev/null +++ b/tests/regression/73-mem-oob/01-oob-heap-simple.c @@ -0,0 +1,14 @@ +// PARAM: --set ana.activated[+] memOutOfBounds +#include + +int main(int argc, char const *argv[]) { + char *ptr = malloc(5 * sizeof(char)); + + *ptr = 'a';//NOWARN + *(ptr + 1) = 'b';//NOWARN + *(ptr + 10) = 'c';//WARN + + free(ptr); + + return 0; +} diff --git a/tests/regression/73-mem-oob/02-oob-stack-simple.c b/tests/regression/73-mem-oob/02-oob-stack-simple.c new file mode 100644 index 0000000000..deedd52781 --- /dev/null +++ b/tests/regression/73-mem-oob/02-oob-stack-simple.c @@ -0,0 +1,12 @@ +// PARAM: --set ana.activated[+] memOutOfBounds +#include + +int main(int argc, char const *argv[]) { + int i = 42; + int *ptr = &i; + + *ptr = 5;//NOWARN + *(ptr + 10) = 55;//WARN + + return 0; +} From 511fd366c0181999023f57e0e8a592d5f745f319 Mon Sep 17 00:00:00 2001 From: FelixKrayer Date: Tue, 27 Jun 2023 16:32:03 +0200 Subject: [PATCH 0207/1312] changed the invalidation of equivalences to use VarEq.may_change --- src/analyses/tmpSpecial.ml | 92 ++++++++++---------------------------- 1 file changed, 24 insertions(+), 68 deletions(-) diff --git a/src/analyses/tmpSpecial.ml b/src/analyses/tmpSpecial.ml index a9b65189b2..9b88ab0317 100644 --- a/src/analyses/tmpSpecial.ml +++ b/src/analyses/tmpSpecial.ml @@ -1,5 +1,7 @@ (* Analysis that tracks which variables hold the results of calls to math library functions. - For each equivalence a set of lvals is tracked, that contains all lvals on which the arguments of the corresponding call depend, so an equivalence can be removed if one of the lvals is written.*) + For each equivalence a set of expressions is tracked, that contains the arguments of the corresponding call as well as the Lval it is assigned to, so an equivalence can be removed if one of these expressions may be changed.*) + +module VarEq = VarEq.Spec open GoblintCil open Analyses @@ -10,9 +12,9 @@ struct let name () = "tmpSpecial" module ML = LibraryDesc.MathLifted - module LS = SetDomain.ToppedSet(Lval.CilLval) (struct let topname = "All" end) - module MlLsProd = Lattice.Prod (ML) (LS) - module D = MapDomain.MapBot (Lval.CilLval) (MlLsProd) + module Deps = SetDomain.Reverse (SetDomain.ToppedSet (CilType.Exp) (struct let topname = "All" end)) + module MLDeps = Lattice.Prod (ML) (Deps) + module D = MapDomain.MapBot (Lval.CilLval) (MLDeps) module C = Lattice.Unit let rec resolve (offs : offset) : (CilType.Fieldinfo.t, Basetype.CilExp.t) Lval.offs = @@ -20,40 +22,17 @@ struct | NoOffset -> `NoOffset | Field (f_info, f_offs) -> `Field (f_info, (resolve f_offs)) | Index (i_exp, i_offs) -> `Index (i_exp, (resolve i_offs)) - - let ls_of_lv ctx (lval:lval) : LS.t = - match lval with - | (Var v, offs) -> LS.of_list [(v, resolve offs)] - | (Mem e, _) -> (ctx.ask (Queries.MayPointTo e)) - - let rec ls_of_exp ctx (exp:exp) : LS.t = - match exp with - | Const _ -> LS.empty () - | Lval lv -> ls_of_lv ctx lv - | SizeOf _ -> LS.empty () - | Real e -> ls_of_exp ctx e - | Imag e -> ls_of_exp ctx e - | SizeOfE e -> ls_of_exp ctx e - | SizeOfStr _ -> LS.empty () - | AlignOf _ -> LS.empty () - | AlignOfE e -> ls_of_exp ctx e - | UnOp (_,e,_) -> ls_of_exp ctx e - | BinOp (_,e1,e2,_) -> LS.union (ls_of_exp ctx e1) (ls_of_exp ctx e2) - | Question (q,e1,e2,_) -> LS.union (ls_of_exp ctx q) (LS.union (ls_of_exp ctx e1) (ls_of_exp ctx e2)) - | CastE (_,e) -> ls_of_exp ctx e - | AddrOf _ -> ctx.ask (Queries.MayPointTo exp) - | AddrOfLabel _ -> LS.empty () - | StartOf _ -> ctx.ask (Queries.MayPointTo exp) - + + let invalidate ask exp_w st = + D.filter (fun _ (ml, deps) -> (Deps.for_all (fun arg -> not (VarEq.may_change ask exp_w arg)) deps)) st let context _ _ = () (* transfer functions *) let assign ctx (lval:lval) (rval:exp) : D.t = - (* get the set of all lvals written by the assign. Then remove all entrys from the map where the dependencies overlap with the set of written lvals *) - let lvalsWritten = ls_of_lv ctx lval in - if M.tracing then M.trace "tmpSpecial" "lvalsWritten %a\n" LS.pretty lvalsWritten; - D.filter (fun _ (ml, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) ctx.local + if M.tracing then M.tracel "tmpSpecial" "assignment of %a\n" d_lval lval; + (* Invalidate all entrys from the map that are possibly written by the assignment *) + invalidate (Analyses.ask_of_ctx ctx) (mkAddrOf lval) ctx.local let branch ctx (exp:exp) (tv:bool) : D.t = ctx.local @@ -74,6 +53,7 @@ struct let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = let d = ctx.local in + let ask = Analyses.ask_of_ctx ctx in (* Just dbg prints *) (if M.tracing then @@ -83,55 +63,31 @@ struct let desc = LibraryFunctions.find f in + (* remove entrys, dependent on lvals that were possibly written by the special function *) - let shallow_addrs = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = false } arglist in - let deep_addrs = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = true } arglist in - let deep_addrs = - if List.mem LibraryDesc.InvalidateGlobals desc.attrs then ( - foldGlobals !Cilfacade.current_file (fun acc global -> - match global with - | GVar (vi, _, _) when not (BaseUtil.is_static vi) -> - mkAddrOf (Var vi, NoOffset) :: acc - (* TODO: what about GVarDecl? (see "base.ml -> special_unknown_invalidate")*) - | _ -> acc - ) deep_addrs - ) - else - deep_addrs - in - let d = List.fold_left (fun accD addr -> - let lvalsWritten = ctx.ask (Queries.MayPointTo addr) in - D.filter (fun _ (_, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) accD) d shallow_addrs - in - let d = List.fold_left (fun accD addr -> - let lvalsWritten = ctx.ask (Queries.ReachableFrom addr) in - D.filter (fun _ (_, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) accD) d deep_addrs - in + let write_args = LibraryDesc.Accesses.find_kind desc.accs Write arglist in + (* TODO similar to symbLocks->Spec->special: why doesn't invalidate involve any reachable for deep write? *) + let d = List.fold_left (fun d e -> invalidate ask e d) d write_args in (* same for lval assignment of the call*) let d = match lval with - | Some lv -> ( - (* get the set of all lvals written by the assign. Then remove all entrys from the map where the dependencies overlap with the set of written lvals *) - let lvalsWritten = ls_of_lv ctx lv in - D.filter (fun _ (ml, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) d - ) + | Some lv -> invalidate ask (mkAddrOf lv) ctx.local | None -> d in (* add new math fun desc*) let d = match lval, desc.special arglist with - | Some (Var v, offs), (Math { fun_args; }) -> - let argsDep = List.fold_left LS.union (LS.empty ()) (List.map (ls_of_exp ctx) arglist) in - let lvalsWritten = ls_of_lv ctx (Var v, offs) in - (* only add descriptor, if the set of lvals contained in the args is known and none is written by the assignment *) + | Some ((Var v, offs) as lv), (Math { fun_args; }) -> + (* only add descriptor, if none of the args is written by the assignment, invalidating the equivalence *) (* actually it would be necessary to check here, if one of the arguments is written by the call. However this is not the case for any of the math functions and no other functions are covered so far *) - if LS.is_top argsDep || not (LS.is_empty (LS.meet argsDep lvalsWritten)) then + if List.exists (fun arg -> VarEq.may_change ask (mkAddrOf lv) arg) arglist then d - else - D.add (v, resolve offs) ((ML.lift fun_args, LS.union argsDep lvalsWritten)) d + else + D.add (v, resolve offs) ((ML.lift fun_args, Deps.of_list ((Lval lv)::arglist))) d | _ -> d + in if M.tracing then M.tracel "tmpSpecial" "Result: %a\n\n" D.pretty d; From 86031779609876e6b713ac2d5a4640831b3dcc15 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Wed, 28 Jun 2023 11:27:10 +0200 Subject: [PATCH 0208/1312] Fix indentation --- src/framework/control.ml | 118 +++++++++++++++++++-------------------- src/util/cilfacade.ml | 14 ++--- 2 files changed, 66 insertions(+), 66 deletions(-) diff --git a/src/framework/control.ml b/src/framework/control.ml index 6a25283687..9a717e2f67 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -20,25 +20,25 @@ let spec_module: (module Spec) Lazy.t = lazy ( (* apply functor F on module X if opt is true *) let lift opt (module F : S2S) (module X : Spec) = (module (val if opt then (module F (X)) else (module X) : Spec) : Spec) in let module S1 = (val - (module MCP.MCP2 : Spec) - |> lift true (module WidenContextLifterSide) (* option checked in functor *) - (* hashcons before witness to reduce duplicates, because witness re-uses contexts in domain and requires tag for PathSensitive3 *) - |> lift (get_bool "ana.opt.hashcons" || arg_enabled) (module HashconsContextLifter) - |> lift arg_enabled (module HashconsLifter) - |> lift arg_enabled (module WitnessConstraints.PathSensitive3) - |> lift (not arg_enabled) (module PathSensitive2) - |> lift (get_bool "ana.dead-code.branches") (module DeadBranchLifter) - |> lift true (module DeadCodeLifter) - |> lift (get_bool "dbg.slice.on") (module LevelSliceLifter) - |> lift (get_int "dbg.limit.widen" > 0) (module LimitLifter) - |> lift (get_bool "ana.opt.equal" && not (get_bool "ana.opt.hashcons")) (module OptEqual) - |> lift (get_bool "ana.opt.hashcons") (module HashconsLifter) - (* Widening tokens must be outside of hashcons, because widening token domain ignores token sets for identity, so hashcons doesn't allow adding tokens. - Also must be outside of deadcode, because deadcode splits (like mutex lock event) don't pass on tokens. *) - |> lift (get_bool "ana.widen.tokens") (module WideningTokens.Lifter) - |> lift true (module LongjmpLifter) - |> lift arg_termination (module RecursionTermLifter) (* Always activate the recursion termination analysis, when the loop termination analysis is activated*) - ) in + (module MCP.MCP2 : Spec) + |> lift true (module WidenContextLifterSide) (* option checked in functor *) + (* hashcons before witness to reduce duplicates, because witness re-uses contexts in domain and requires tag for PathSensitive3 *) + |> lift (get_bool "ana.opt.hashcons" || arg_enabled) (module HashconsContextLifter) + |> lift arg_enabled (module HashconsLifter) + |> lift arg_enabled (module WitnessConstraints.PathSensitive3) + |> lift (not arg_enabled) (module PathSensitive2) + |> lift (get_bool "ana.dead-code.branches") (module DeadBranchLifter) + |> lift true (module DeadCodeLifter) + |> lift (get_bool "dbg.slice.on") (module LevelSliceLifter) + |> lift (get_int "dbg.limit.widen" > 0) (module LimitLifter) + |> lift (get_bool "ana.opt.equal" && not (get_bool "ana.opt.hashcons")) (module OptEqual) + |> lift (get_bool "ana.opt.hashcons") (module HashconsLifter) + (* Widening tokens must be outside of hashcons, because widening token domain ignores token sets for identity, so hashcons doesn't allow adding tokens. + Also must be outside of deadcode, because deadcode splits (like mutex lock event) don't pass on tokens. *) + |> lift (get_bool "ana.widen.tokens") (module WideningTokens.Lifter) + |> lift true (module LongjmpLifter) + |> lift arg_termination (module RecursionTermLifter) (* Always activate the recursion termination analysis, when the loop termination analysis is activated*) + ) in GobConfig.building_spec := false; ControlSpecC.control_spec_c := (module S1.C); (module S1) @@ -303,10 +303,10 @@ struct | MyCFG.Assign (lval,exp) -> if M.tracing then M.trace "global_inits" "Assign %a = %a\n" d_lval lval d_exp exp; (match lval, exp with - | (Var v,o), (AddrOf (Var f,NoOffset)) - when v.vstorage <> Static && isFunctionType f.vtype -> - (try funs := Cilfacade.find_varinfo_fundec f :: !funs with Not_found -> ()) - | _ -> () + | (Var v,o), (AddrOf (Var f,NoOffset)) + when v.vstorage <> Static && isFunctionType f.vtype -> + (try funs := Cilfacade.find_varinfo_fundec f :: !funs with Not_found -> ()) + | _ -> () ); let res = Spec.assign {ctx with local = st} lval exp in (* Needed for privatizations (e.g. None) that do not side immediately *) @@ -530,9 +530,9 @@ struct GobSys.mkdir_or_exists save_run; GobConfig.write_file config; let module Meta = struct - type t = { command : string; version: string; timestamp : float; localtime : string } [@@deriving to_yojson] - let json = to_yojson { command = GobSys.command_line; version = Version.goblint; timestamp = Unix.time (); localtime = GobUnix.localtime () } - end + type t = { command : string; version: string; timestamp : float; localtime : string } [@@deriving to_yojson] + let json = to_yojson { command = GobSys.command_line; version = Version.goblint; timestamp = Unix.time (); localtime = GobUnix.localtime () } + end in (* Yojson.Safe.to_file meta Meta.json; *) Yojson.Safe.pretty_to_channel (Stdlib.open_out (Fpath.to_string meta)) Meta.json; (* the above is compact, this is pretty-printed *) @@ -584,10 +584,10 @@ struct in let print_and_calculate_uncalled = function | GFun (fn, loc) when is_bad_uncalled fn.svar loc-> - let cnt = Cilfacade.countLoc fn in - uncalled_dead := !uncalled_dead + cnt; - if get_bool "ana.dead-code.functions" then - M.warn ~loc:(CilLocation loc) ~category:Deadcode "Function '%a' is uncalled: %d LLoC" CilType.Fundec.pretty fn cnt (* CilLocation is fine because always printed from scratch *) + let cnt = Cilfacade.countLoc fn in + uncalled_dead := !uncalled_dead + cnt; + if get_bool "ana.dead-code.functions" then + M.warn ~loc:(CilLocation loc) ~category:Deadcode "Function '%a' is uncalled: %d LLoC" CilType.Fundec.pretty fn cnt (* CilLocation is fine because always printed from scratch *) | _ -> () in List.iter print_and_calculate_uncalled file.globals; @@ -619,35 +619,35 @@ struct NodeH.modify_opt node join by_node; ); by_loc, by_node - in - - let ask ?(node = MyCFG.dummy_node) loc = - let f (type a) (q : a Queries.t) : a = - match Hashtbl.find_option joined_by_loc loc with - | None -> Queries.Result.bot q - | Some local -> Query.ask_local_node gh node local q - in - ({ f } : Queries.ask) - in - - (* A node is dead when its abstract value is bottom in all contexts; - it holds that: bottom in all contexts iff. bottom in the join of all contexts. - Therefore, we just answer whether the (stored) join is bottom. *) - let must_be_dead node = - NodeH.find_option joined_by_node node - (* nodes that didn't make it into the result are definitely dead (hence for_all) *) - |> GobOption.for_all Spec.D.is_bot - in - - let must_be_uncalled fd = not @@ BatSet.Int.mem fd.svar.vid calledFuns in - - let skipped_statements from_node edge to_node = - CfgTools.CfgEdgeH.find_default skippedByEdge (from_node, edge, to_node) [] - in - - Transform.run_transformations file active_transformations - { ask ; must_be_dead ; must_be_uncalled ; - cfg_forward = Cfg.next ; cfg_backward = Cfg.prev ; skipped_statements }; + in + + let ask ?(node = MyCFG.dummy_node) loc = + let f (type a) (q : a Queries.t) : a = + match Hashtbl.find_option joined_by_loc loc with + | None -> Queries.Result.bot q + | Some local -> Query.ask_local_node gh node local q + in + ({ f } : Queries.ask) + in + + (* A node is dead when its abstract value is bottom in all contexts; + it holds that: bottom in all contexts iff. bottom in the join of all contexts. + Therefore, we just answer whether the (stored) join is bottom. *) + let must_be_dead node = + NodeH.find_option joined_by_node node + (* nodes that didn't make it into the result are definitely dead (hence for_all) *) + |> GobOption.for_all Spec.D.is_bot + in + + let must_be_uncalled fd = not @@ BatSet.Int.mem fd.svar.vid calledFuns in + + let skipped_statements from_node edge to_node = + CfgTools.CfgEdgeH.find_default skippedByEdge (from_node, edge, to_node) [] + in + + Transform.run_transformations file active_transformations + { ask ; must_be_dead ; must_be_uncalled ; + cfg_forward = Cfg.next ; cfg_backward = Cfg.prev ; skipped_statements }; ); lh, gh diff --git a/src/util/cilfacade.ml b/src/util/cilfacade.ml index 2043a94ed0..2a81444e41 100644 --- a/src/util/cilfacade.ml +++ b/src/util/cilfacade.ml @@ -89,14 +89,14 @@ let visitors_cil = ref [] (* does exactly the same as register_preprocess but it is executed earlier, before the CFG is created*) let register_preprocess_cil name visitor_fun = visitors_cil := !visitors_cil @ [name, visitor_fun] - + let do_preprocess_cil ast = - let f fd (name, visitor_fun) = - (* this has to be done here, since the settings aren't available when register_preprocess is called *) - if List.mem name (get_string_list "ana.activated") then - ignore @@ visitCilFunction (visitor_fun fd) fd - in - iterGlobals ast (function GFun (fd,_) -> List.iter (f fd) !visitors_cil | _ -> ()) + let f fd (name, visitor_fun) = + (* this has to be done here, since the settings aren't available when register_preprocess is called *) + if List.mem name (get_string_list "ana.activated") then + ignore @@ visitCilFunction (visitor_fun fd) fd + in + iterGlobals ast (function GFun (fd,_) -> List.iter (f fd) !visitors_cil | _ -> ()) (** @raise GoblintCil.FrontC.ParseError @raise GoblintCil.Errormsg.Error *) From d987f1a151e481a7dcd9fd50b1dc0b5a53b51f19 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Wed, 28 Jun 2023 11:34:44 +0200 Subject: [PATCH 0209/1312] Restore signs analysis tutorial --- src/analyses/tutorials/signs.ml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/analyses/tutorials/signs.ml b/src/analyses/tutorials/signs.ml index 9ae48f8626..31168df86a 100644 --- a/src/analyses/tutorials/signs.ml +++ b/src/analyses/tutorials/signs.ml @@ -23,12 +23,12 @@ struct (* TODO: An attempt to abstract integers, but it's just a little wrong... *) let of_int i = - if Z.compare i Z.zero < 0 then Neg - else if Z.compare i Z.zero > 0 then Pos + if Z.compare i Z.zero < 0 then Zero + else if Z.compare i Z.zero > 0 then Zero else Zero let lt x y = match x, y with - | Neg, Pos | Neg, Zero | Pos, Zero -> true (* TODO: Maybe something missing? *) + | Neg, Pos | Neg, Zero -> true (* TODO: Maybe something missing? *) | _ -> false end @@ -59,7 +59,7 @@ struct (* This should now evaluate expressions. *) let eval (d: D.t) (exp: exp): SL.t = match exp with - | Const (CInt (i, _, _)) -> SL.of_int i (* TODO: Fix me! *) + | Const (CInt (i, _, _)) -> SL.top () (* TODO: Fix me! *) | Lval (Var x, NoOffset) -> D.find x d | _ -> SL.top () From b1930abaf634e8a9a6c4f34186b41a5bdf5a9206 Mon Sep 17 00:00:00 2001 From: serenita <33780257+serenita@users.noreply.github.com> Date: Wed, 28 Jun 2023 11:39:48 +0200 Subject: [PATCH 0210/1312] Simplify src/framework/analyses.ml Co-authored-by: Julian Erhard --- src/framework/analyses.ml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 20727aaf23..cae9d6f782 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -154,11 +154,7 @@ module GVarGSet (G: Lattice.S) (C: Printable.S) (Base: Printable.S) = struct module CSet = struct - include SetDomain.Make ( - struct - include (Base) (* Set of Tuples*) - end - ) + include SetDomain.Make (Base) (* Set of Tuples*) let name () = "contexts" let printXml f a = BatPrintf.fprintf f "\n"; From a3fd8d0977a9217838037972d4bb6337422a0961 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Wed, 28 Jun 2023 11:55:10 +0200 Subject: [PATCH 0211/1312] Fix indentation --- src/framework/analyses.ml | 40 +++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index cae9d6f782..1c3d596cc2 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -119,19 +119,19 @@ struct end (* Tuple of fundec and S.C*) -module T (Base1: Printable.S) (Base2: Printable.S) = -struct +module T (Base1: Printable.S) (Base2: Printable.S) = +struct include Printable.Std type t = (Base1.t * Base2.t) let fundec (a,_) = a let context (_,b) = b let equal (a1, b1) (a2, b2) = if (Base1.equal a1 a2 && Base2.equal b1 b2) then true else false - let show (a,b) = (Base1.show a) ^ (Base2.show b) + let show (a,b) = (Base1.show a) ^ (Base2.show b) let name () = "Tuple" let to_yojson x = `String (show x) let relift (a,b) = (a,b) (*Todo: is this correct?*) - let printXml f (a,b) = + let printXml f (a,b) = BatPrintf.fprintf f "\n Tuple:\n\n caller_fundec\n%a\n\n @@ -139,13 +139,13 @@ struct \n" Base1.printXml a Base2.printXml b let compare (a1,b1) (a2,b2) = (*Todo: is this ok?*) - if equal (a1, b1) (a2, b2) then 0 + if equal (a1, b1) (a2, b2) then 0 else( let val_a a = if (a > 0) then 1 else -1 in let val_b b = if (b > 0) then 3 else -3 in val_a (Base1.compare a1 a2) + val_b (Base2.compare b1 b2) - ) - + ) + let pretty () x = text (show x) let hash (a,b) = Hashtbl.hash (Base1.hash a * Base2.hash b) (*Todo: is this ok?*) end @@ -156,24 +156,24 @@ struct struct include SetDomain.Make (Base) (* Set of Tuples*) let name () = "contexts" - let printXml f a = + let printXml f a = BatPrintf.fprintf f "\n"; iter (Base.printXml f) a; BatPrintf.fprintf f "\n\n" end (* Make the given module Goupable*) - module C_Printable (C: Printable.S) = - struct - include C - include Printable.Std (* To make it Groupable *) - let printXml f c = BatPrintf.fprintf f - "\n + module C_Printable (C: Printable.S) = + struct + include C + include Printable.Std (* To make it Groupable *) + let printXml f c = BatPrintf.fprintf f + "\n callee_context\n%a\n\n " printXml c - end + end - module CMap = + module CMap = struct include MapDomain.MapBot (C_Printable (C)) (CSet) let printXml f c = BatPrintf.fprintf f " @@ -188,14 +188,14 @@ struct | `Bot -> G.bot () | `Lifted1 x -> x | _ -> failwith "GVarGSet.spec" - let contexts = function + let contexts = function | `Bot -> CSet.bot () | `Lifted2 x -> x | _ -> failwith "GVarGSet.contexts" - let create_spec spec = `Lifted1 spec - let create_contexts contexts = `Lifted2 contexts + let create_spec spec = `Lifted1 spec + let create_contexts contexts = `Lifted2 contexts - let printXml f = function + let printXml f = function | `Lifted1 x -> G.printXml f x | `Lifted2 x -> BatPrintf.fprintf f "%a" CMap.printXml x | x -> BatPrintf.fprintf f "%a" printXml x From ba596d77282c549e29678885ba718ce592fc42eb Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Wed, 28 Jun 2023 16:31:35 +0200 Subject: [PATCH 0212/1312] Remove build_testing.yml --- .github/workflows/build_testing.yml | 35 ----------------------------- 1 file changed, 35 deletions(-) delete mode 100644 .github/workflows/build_testing.yml diff --git a/.github/workflows/build_testing.yml b/.github/workflows/build_testing.yml deleted file mode 100644 index 1b464043c7..0000000000 --- a/.github/workflows/build_testing.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: build_testing - -on: - - push - - pull_request - -jobs: - tests: - strategy: - fail-fast: false - matrix: - os: - - macos-latest - - ubuntu-latest - ocaml-compiler: - - 4.12.0 - - runs-on: ${{ matrix.os }} - - steps: - - name: Checkout code - uses: actions/checkout@v2 - - - name: Setup OCaml ${{ matrix.ocaml-compiler }} - env: - OPAMLOCKED: locked - uses: ocaml/setup-ocaml@v2 - with: - ocaml-compiler: ${{ matrix.ocaml-compiler }} - - - name: Install opam dependencies - run: opam install . --deps-only --locked - - - name: Build - run: ./make.sh nat From fbef2088d75d627084e442c3648386c7d23ce419 Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Thu, 29 Jun 2023 12:24:33 +0200 Subject: [PATCH 0213/1312] No skipping --- .../regression/80-termination/09-complex-for-loop-terminating.c | 2 +- tests/regression/80-termination/10-complex-loop-terminating.c | 2 +- .../80-termination/15-complex-loop-combination-terminating.c | 2 +- tests/regression/81-recursion/02-simple-nonterminating.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/regression/80-termination/09-complex-for-loop-terminating.c b/tests/regression/80-termination/09-complex-for-loop-terminating.c index 8951924c06..7fcd5c5eba 100644 --- a/tests/regression/80-termination/09-complex-for-loop-terminating.c +++ b/tests/regression/80-termination/09-complex-for-loop-terminating.c @@ -1,4 +1,4 @@ -// SKIP LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/80-termination/10-complex-loop-terminating.c b/tests/regression/80-termination/10-complex-loop-terminating.c index dbce2a6b5b..def1786937 100644 --- a/tests/regression/80-termination/10-complex-loop-terminating.c +++ b/tests/regression/80-termination/10-complex-loop-terminating.c @@ -1,4 +1,4 @@ -// SKIP LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/80-termination/15-complex-loop-combination-terminating.c b/tests/regression/80-termination/15-complex-loop-combination-terminating.c index 7daa9b98fe..9ab04745e8 100644 --- a/tests/regression/80-termination/15-complex-loop-combination-terminating.c +++ b/tests/regression/80-termination/15-complex-loop-combination-terminating.c @@ -1,4 +1,4 @@ -// SKIP LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval +// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval #include int main() diff --git a/tests/regression/81-recursion/02-simple-nonterminating.c b/tests/regression/81-recursion/02-simple-nonterminating.c index c7902e2e7f..37c4bbd801 100644 --- a/tests/regression/81-recursion/02-simple-nonterminating.c +++ b/tests/regression/81-recursion/02-simple-nonterminating.c @@ -1,4 +1,4 @@ -// SKIP NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include void recursiveFunction(int n) { From 13f369acdb55b89378c9768edb07723091c724b7 Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Thu, 29 Jun 2023 12:31:36 +0200 Subject: [PATCH 0214/1312] New non terminating for loop --- .../34-nested-for-loop-nonterminating.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 tests/regression/80-termination/34-nested-for-loop-nonterminating.c diff --git a/tests/regression/80-termination/34-nested-for-loop-nonterminating.c b/tests/regression/80-termination/34-nested-for-loop-nonterminating.c new file mode 100644 index 0000000000..1f68f3c0a5 --- /dev/null +++ b/tests/regression/80-termination/34-nested-for-loop-nonterminating.c @@ -0,0 +1,19 @@ +// TODO NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int outerCount, innerCount; + + for (outerCount = 1; outerCount <= 3; outerCount++) + { + for (innerCount = 1; innerCount > 0; innerCount++) + { + printf("(%d, %d) ", outerCount, innerCount); + } + + printf("\n"); + } + + return 0; +} From d681158d9bc6d37afd3a16ffa100336782311f97 Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Thu, 29 Jun 2023 12:34:13 +0200 Subject: [PATCH 0215/1312] Folder Renaming --- .../01-simple-loop-terminating.c | 0 .../02-simple-loop-nonterminating.c | 0 .../03-nested-loop-terminating.c | 0 .../04-nested-loop-nonterminating.c | 0 .../05-for-loop-terminating.c | 0 .../06-for-loop-nonterminating.c | 0 .../07-nested-for-loop-terminating.c | 0 .../08-nested-for-loop-nonterminating.c | 0 .../09-complex-for-loop-terminating.c | 0 .../10-complex-loop-terminating.c | 0 .../11-loopless-termination.c | 0 .../12-do-while-instant-terminating.c | 0 .../13-do-while-terminating.c | 0 .../14-do-while-nonterminating.c | 0 .../15-complex-loop-combination-terminating.c | 0 .../16-nested-loop-nontrivial-nonterminating.c | 0 .../{80-termination => 74-loop_termination}/17-goto-terminating.c | 0 .../18-goto-nonterminating.c | 0 .../{80-termination => 74-loop_termination}/19-rand-terminating.c | 0 .../20-rand-nonterminating.c | 0 .../21-no-exit-on-rand-unproofable.c | 0 .../22-exit-on-rand-unproofable.c | 0 .../23-exit-on-rand-terminating.c | 0 .../24-upjumping-goto-loopless-terminating.c | 0 .../25-leave-loop-goto-terminating.c | 0 .../26-enter-loop-goto-terminating.c | 0 .../27-upjumping-goto-nonterminating.c | 0 .../28-do-while-continue-terminating.c | 0 .../29-do-while-continue-nonterminating.c | 0 .../30-goto-out-of-inner-loop-terminating.c | 0 .../31-goto-out-of-inner-loop-nonterminating.c | 0 .../32-multithread-terminating.c | 0 .../33-multithread-nonterminating.c | 0 .../34-nested-for-loop-nonterminating.c | 0 .../01-simple-terminating.c | 0 .../02-simple-nonterminating.c | 0 .../03-nested-terminating.c | 0 .../04-nested-nonterminating.c | 0 38 files changed, 0 insertions(+), 0 deletions(-) rename tests/regression/{80-termination => 74-loop_termination}/01-simple-loop-terminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/02-simple-loop-nonterminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/03-nested-loop-terminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/04-nested-loop-nonterminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/05-for-loop-terminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/06-for-loop-nonterminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/07-nested-for-loop-terminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/08-nested-for-loop-nonterminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/09-complex-for-loop-terminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/10-complex-loop-terminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/11-loopless-termination.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/12-do-while-instant-terminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/13-do-while-terminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/14-do-while-nonterminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/15-complex-loop-combination-terminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/16-nested-loop-nontrivial-nonterminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/17-goto-terminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/18-goto-nonterminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/19-rand-terminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/20-rand-nonterminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/21-no-exit-on-rand-unproofable.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/22-exit-on-rand-unproofable.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/23-exit-on-rand-terminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/24-upjumping-goto-loopless-terminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/25-leave-loop-goto-terminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/26-enter-loop-goto-terminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/27-upjumping-goto-nonterminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/28-do-while-continue-terminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/29-do-while-continue-nonterminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/30-goto-out-of-inner-loop-terminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/31-goto-out-of-inner-loop-nonterminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/32-multithread-terminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/33-multithread-nonterminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/34-nested-for-loop-nonterminating.c (100%) rename tests/regression/{81-recursion => 75-recursion_termination}/01-simple-terminating.c (100%) rename tests/regression/{81-recursion => 75-recursion_termination}/02-simple-nonterminating.c (100%) rename tests/regression/{81-recursion => 75-recursion_termination}/03-nested-terminating.c (100%) rename tests/regression/{81-recursion => 75-recursion_termination}/04-nested-nonterminating.c (100%) diff --git a/tests/regression/80-termination/01-simple-loop-terminating.c b/tests/regression/74-loop_termination/01-simple-loop-terminating.c similarity index 100% rename from tests/regression/80-termination/01-simple-loop-terminating.c rename to tests/regression/74-loop_termination/01-simple-loop-terminating.c diff --git a/tests/regression/80-termination/02-simple-loop-nonterminating.c b/tests/regression/74-loop_termination/02-simple-loop-nonterminating.c similarity index 100% rename from tests/regression/80-termination/02-simple-loop-nonterminating.c rename to tests/regression/74-loop_termination/02-simple-loop-nonterminating.c diff --git a/tests/regression/80-termination/03-nested-loop-terminating.c b/tests/regression/74-loop_termination/03-nested-loop-terminating.c similarity index 100% rename from tests/regression/80-termination/03-nested-loop-terminating.c rename to tests/regression/74-loop_termination/03-nested-loop-terminating.c diff --git a/tests/regression/80-termination/04-nested-loop-nonterminating.c b/tests/regression/74-loop_termination/04-nested-loop-nonterminating.c similarity index 100% rename from tests/regression/80-termination/04-nested-loop-nonterminating.c rename to tests/regression/74-loop_termination/04-nested-loop-nonterminating.c diff --git a/tests/regression/80-termination/05-for-loop-terminating.c b/tests/regression/74-loop_termination/05-for-loop-terminating.c similarity index 100% rename from tests/regression/80-termination/05-for-loop-terminating.c rename to tests/regression/74-loop_termination/05-for-loop-terminating.c diff --git a/tests/regression/80-termination/06-for-loop-nonterminating.c b/tests/regression/74-loop_termination/06-for-loop-nonterminating.c similarity index 100% rename from tests/regression/80-termination/06-for-loop-nonterminating.c rename to tests/regression/74-loop_termination/06-for-loop-nonterminating.c diff --git a/tests/regression/80-termination/07-nested-for-loop-terminating.c b/tests/regression/74-loop_termination/07-nested-for-loop-terminating.c similarity index 100% rename from tests/regression/80-termination/07-nested-for-loop-terminating.c rename to tests/regression/74-loop_termination/07-nested-for-loop-terminating.c diff --git a/tests/regression/80-termination/08-nested-for-loop-nonterminating.c b/tests/regression/74-loop_termination/08-nested-for-loop-nonterminating.c similarity index 100% rename from tests/regression/80-termination/08-nested-for-loop-nonterminating.c rename to tests/regression/74-loop_termination/08-nested-for-loop-nonterminating.c diff --git a/tests/regression/80-termination/09-complex-for-loop-terminating.c b/tests/regression/74-loop_termination/09-complex-for-loop-terminating.c similarity index 100% rename from tests/regression/80-termination/09-complex-for-loop-terminating.c rename to tests/regression/74-loop_termination/09-complex-for-loop-terminating.c diff --git a/tests/regression/80-termination/10-complex-loop-terminating.c b/tests/regression/74-loop_termination/10-complex-loop-terminating.c similarity index 100% rename from tests/regression/80-termination/10-complex-loop-terminating.c rename to tests/regression/74-loop_termination/10-complex-loop-terminating.c diff --git a/tests/regression/80-termination/11-loopless-termination.c b/tests/regression/74-loop_termination/11-loopless-termination.c similarity index 100% rename from tests/regression/80-termination/11-loopless-termination.c rename to tests/regression/74-loop_termination/11-loopless-termination.c diff --git a/tests/regression/80-termination/12-do-while-instant-terminating.c b/tests/regression/74-loop_termination/12-do-while-instant-terminating.c similarity index 100% rename from tests/regression/80-termination/12-do-while-instant-terminating.c rename to tests/regression/74-loop_termination/12-do-while-instant-terminating.c diff --git a/tests/regression/80-termination/13-do-while-terminating.c b/tests/regression/74-loop_termination/13-do-while-terminating.c similarity index 100% rename from tests/regression/80-termination/13-do-while-terminating.c rename to tests/regression/74-loop_termination/13-do-while-terminating.c diff --git a/tests/regression/80-termination/14-do-while-nonterminating.c b/tests/regression/74-loop_termination/14-do-while-nonterminating.c similarity index 100% rename from tests/regression/80-termination/14-do-while-nonterminating.c rename to tests/regression/74-loop_termination/14-do-while-nonterminating.c diff --git a/tests/regression/80-termination/15-complex-loop-combination-terminating.c b/tests/regression/74-loop_termination/15-complex-loop-combination-terminating.c similarity index 100% rename from tests/regression/80-termination/15-complex-loop-combination-terminating.c rename to tests/regression/74-loop_termination/15-complex-loop-combination-terminating.c diff --git a/tests/regression/80-termination/16-nested-loop-nontrivial-nonterminating.c b/tests/regression/74-loop_termination/16-nested-loop-nontrivial-nonterminating.c similarity index 100% rename from tests/regression/80-termination/16-nested-loop-nontrivial-nonterminating.c rename to tests/regression/74-loop_termination/16-nested-loop-nontrivial-nonterminating.c diff --git a/tests/regression/80-termination/17-goto-terminating.c b/tests/regression/74-loop_termination/17-goto-terminating.c similarity index 100% rename from tests/regression/80-termination/17-goto-terminating.c rename to tests/regression/74-loop_termination/17-goto-terminating.c diff --git a/tests/regression/80-termination/18-goto-nonterminating.c b/tests/regression/74-loop_termination/18-goto-nonterminating.c similarity index 100% rename from tests/regression/80-termination/18-goto-nonterminating.c rename to tests/regression/74-loop_termination/18-goto-nonterminating.c diff --git a/tests/regression/80-termination/19-rand-terminating.c b/tests/regression/74-loop_termination/19-rand-terminating.c similarity index 100% rename from tests/regression/80-termination/19-rand-terminating.c rename to tests/regression/74-loop_termination/19-rand-terminating.c diff --git a/tests/regression/80-termination/20-rand-nonterminating.c b/tests/regression/74-loop_termination/20-rand-nonterminating.c similarity index 100% rename from tests/regression/80-termination/20-rand-nonterminating.c rename to tests/regression/74-loop_termination/20-rand-nonterminating.c diff --git a/tests/regression/80-termination/21-no-exit-on-rand-unproofable.c b/tests/regression/74-loop_termination/21-no-exit-on-rand-unproofable.c similarity index 100% rename from tests/regression/80-termination/21-no-exit-on-rand-unproofable.c rename to tests/regression/74-loop_termination/21-no-exit-on-rand-unproofable.c diff --git a/tests/regression/80-termination/22-exit-on-rand-unproofable.c b/tests/regression/74-loop_termination/22-exit-on-rand-unproofable.c similarity index 100% rename from tests/regression/80-termination/22-exit-on-rand-unproofable.c rename to tests/regression/74-loop_termination/22-exit-on-rand-unproofable.c diff --git a/tests/regression/80-termination/23-exit-on-rand-terminating.c b/tests/regression/74-loop_termination/23-exit-on-rand-terminating.c similarity index 100% rename from tests/regression/80-termination/23-exit-on-rand-terminating.c rename to tests/regression/74-loop_termination/23-exit-on-rand-terminating.c diff --git a/tests/regression/80-termination/24-upjumping-goto-loopless-terminating.c b/tests/regression/74-loop_termination/24-upjumping-goto-loopless-terminating.c similarity index 100% rename from tests/regression/80-termination/24-upjumping-goto-loopless-terminating.c rename to tests/regression/74-loop_termination/24-upjumping-goto-loopless-terminating.c diff --git a/tests/regression/80-termination/25-leave-loop-goto-terminating.c b/tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c similarity index 100% rename from tests/regression/80-termination/25-leave-loop-goto-terminating.c rename to tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c diff --git a/tests/regression/80-termination/26-enter-loop-goto-terminating.c b/tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c similarity index 100% rename from tests/regression/80-termination/26-enter-loop-goto-terminating.c rename to tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c diff --git a/tests/regression/80-termination/27-upjumping-goto-nonterminating.c b/tests/regression/74-loop_termination/27-upjumping-goto-nonterminating.c similarity index 100% rename from tests/regression/80-termination/27-upjumping-goto-nonterminating.c rename to tests/regression/74-loop_termination/27-upjumping-goto-nonterminating.c diff --git a/tests/regression/80-termination/28-do-while-continue-terminating.c b/tests/regression/74-loop_termination/28-do-while-continue-terminating.c similarity index 100% rename from tests/regression/80-termination/28-do-while-continue-terminating.c rename to tests/regression/74-loop_termination/28-do-while-continue-terminating.c diff --git a/tests/regression/80-termination/29-do-while-continue-nonterminating.c b/tests/regression/74-loop_termination/29-do-while-continue-nonterminating.c similarity index 100% rename from tests/regression/80-termination/29-do-while-continue-nonterminating.c rename to tests/regression/74-loop_termination/29-do-while-continue-nonterminating.c diff --git a/tests/regression/80-termination/30-goto-out-of-inner-loop-terminating.c b/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c similarity index 100% rename from tests/regression/80-termination/30-goto-out-of-inner-loop-terminating.c rename to tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c diff --git a/tests/regression/80-termination/31-goto-out-of-inner-loop-nonterminating.c b/tests/regression/74-loop_termination/31-goto-out-of-inner-loop-nonterminating.c similarity index 100% rename from tests/regression/80-termination/31-goto-out-of-inner-loop-nonterminating.c rename to tests/regression/74-loop_termination/31-goto-out-of-inner-loop-nonterminating.c diff --git a/tests/regression/80-termination/32-multithread-terminating.c b/tests/regression/74-loop_termination/32-multithread-terminating.c similarity index 100% rename from tests/regression/80-termination/32-multithread-terminating.c rename to tests/regression/74-loop_termination/32-multithread-terminating.c diff --git a/tests/regression/80-termination/33-multithread-nonterminating.c b/tests/regression/74-loop_termination/33-multithread-nonterminating.c similarity index 100% rename from tests/regression/80-termination/33-multithread-nonterminating.c rename to tests/regression/74-loop_termination/33-multithread-nonterminating.c diff --git a/tests/regression/80-termination/34-nested-for-loop-nonterminating.c b/tests/regression/74-loop_termination/34-nested-for-loop-nonterminating.c similarity index 100% rename from tests/regression/80-termination/34-nested-for-loop-nonterminating.c rename to tests/regression/74-loop_termination/34-nested-for-loop-nonterminating.c diff --git a/tests/regression/81-recursion/01-simple-terminating.c b/tests/regression/75-recursion_termination/01-simple-terminating.c similarity index 100% rename from tests/regression/81-recursion/01-simple-terminating.c rename to tests/regression/75-recursion_termination/01-simple-terminating.c diff --git a/tests/regression/81-recursion/02-simple-nonterminating.c b/tests/regression/75-recursion_termination/02-simple-nonterminating.c similarity index 100% rename from tests/regression/81-recursion/02-simple-nonterminating.c rename to tests/regression/75-recursion_termination/02-simple-nonterminating.c diff --git a/tests/regression/81-recursion/03-nested-terminating.c b/tests/regression/75-recursion_termination/03-nested-terminating.c similarity index 100% rename from tests/regression/81-recursion/03-nested-terminating.c rename to tests/regression/75-recursion_termination/03-nested-terminating.c diff --git a/tests/regression/81-recursion/04-nested-nonterminating.c b/tests/regression/75-recursion_termination/04-nested-nonterminating.c similarity index 100% rename from tests/regression/81-recursion/04-nested-nonterminating.c rename to tests/regression/75-recursion_termination/04-nested-nonterminating.c From 541e49b1484fed17376bce8237c54054a85882b9 Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Thu, 29 Jun 2023 12:49:43 +0200 Subject: [PATCH 0216/1312] Enable polyhedra; Removed unnecessary TODOs --- .../15-complex-loop-combination-terminating.c | 2 +- .../74-loop_termination/34-nested-for-loop-nonterminating.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/regression/74-loop_termination/15-complex-loop-combination-terminating.c b/tests/regression/74-loop_termination/15-complex-loop-combination-terminating.c index 9ab04745e8..ec6b50512e 100644 --- a/tests/regression/74-loop_termination/15-complex-loop-combination-terminating.c +++ b/tests/regression/74-loop_termination/15-complex-loop-combination-terminating.c @@ -1,4 +1,4 @@ -// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval +// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/34-nested-for-loop-nonterminating.c b/tests/regression/74-loop_termination/34-nested-for-loop-nonterminating.c index 1f68f3c0a5..d8aa9d487d 100644 --- a/tests/regression/74-loop_termination/34-nested-for-loop-nonterminating.c +++ b/tests/regression/74-loop_termination/34-nested-for-loop-nonterminating.c @@ -1,4 +1,4 @@ -// TODO NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() From 1b5db9c80811fcc083b90ac66554f03fb70dd9ab Mon Sep 17 00:00:00 2001 From: Isidor Zweckstetter Date: Thu, 29 Jun 2023 12:59:03 +0200 Subject: [PATCH 0217/1312] patched loop termination check position to right after the loop head --- src/util/terminationPreprocessing.ml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index 45794b56cc..abc0bd36ef 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -35,8 +35,10 @@ class loopCounterVisitor lc lg le (fd : fundec) = object(self) let inc_stmt = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in let check_stmt = mkStmtOneInstr @@ Set ((var !le), (Lval (var v)), loc, eloc) in (match b.bstmts with - | cont :: cond :: ss -> - b.bstmts <- cont :: inc_stmt :: check_stmt :: cond :: ss; (*cont :: cond :: inc_stmt :: ss = it is also possible, but for loops with cond at the end, inc is also at the end*) + | ss -> + b.bstmts <- inc_stmt :: check_stmt :: ss; (*cont :: cond :: inc_stmt :: ss = it is also possible, but for loops with cond at the end, inc is also at the end*) + (*| cont :: ss -> + b.bstmts <- cont :: inc_stmt :: check_stmt :: ss;*) | _ -> ()); lc := VarToStmt.add (v: varinfo) (s: stmt) !lc; let nb = mkBlock [init_stmt; mkStmt s.skind] in From e6ba5597dd0138b773ac21621d445b6eeca7e372 Mon Sep 17 00:00:00 2001 From: Isidor Zweckstetter Date: Thu, 29 Jun 2023 12:59:49 +0200 Subject: [PATCH 0218/1312] removed temporary comment --- src/util/terminationPreprocessing.ml | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index abc0bd36ef..480bede358 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -37,8 +37,6 @@ class loopCounterVisitor lc lg le (fd : fundec) = object(self) (match b.bstmts with | ss -> b.bstmts <- inc_stmt :: check_stmt :: ss; (*cont :: cond :: inc_stmt :: ss = it is also possible, but for loops with cond at the end, inc is also at the end*) - (*| cont :: ss -> - b.bstmts <- cont :: inc_stmt :: check_stmt :: ss;*) | _ -> ()); lc := VarToStmt.add (v: varinfo) (s: stmt) !lc; let nb = mkBlock [init_stmt; mkStmt s.skind] in From 16b8c375d77934e572c7cd9576dcb115bfcfa4f2 Mon Sep 17 00:00:00 2001 From: Isidor Zweckstetter Date: Thu, 29 Jun 2023 13:01:43 +0200 Subject: [PATCH 0219/1312] removed outdated code --- src/util/terminationPreprocessing.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index 480bede358..6e29c48917 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -36,8 +36,8 @@ class loopCounterVisitor lc lg le (fd : fundec) = object(self) let check_stmt = mkStmtOneInstr @@ Set ((var !le), (Lval (var v)), loc, eloc) in (match b.bstmts with | ss -> - b.bstmts <- inc_stmt :: check_stmt :: ss; (*cont :: cond :: inc_stmt :: ss = it is also possible, but for loops with cond at the end, inc is also at the end*) - | _ -> ()); + b.bstmts <- inc_stmt :: check_stmt :: ss; + ); lc := VarToStmt.add (v: varinfo) (s: stmt) !lc; let nb = mkBlock [init_stmt; mkStmt s.skind] in s.skind <- Block nb; From 1cefa964032e3d2e39c9380efcfd7c59f9f65517 Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Thu, 29 Jun 2023 13:04:25 +0200 Subject: [PATCH 0220/1312] Removed unnecessary TODOs --- .../regression/74-loop_termination/06-for-loop-nonterminating.c | 2 +- .../74-loop_termination/08-nested-for-loop-nonterminating.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/regression/74-loop_termination/06-for-loop-nonterminating.c b/tests/regression/74-loop_termination/06-for-loop-nonterminating.c index 73a8b8c6fd..b8f30361d1 100644 --- a/tests/regression/74-loop_termination/06-for-loop-nonterminating.c +++ b/tests/regression/74-loop_termination/06-for-loop-nonterminating.c @@ -1,4 +1,4 @@ -// TODO NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/74-loop_termination/08-nested-for-loop-nonterminating.c b/tests/regression/74-loop_termination/08-nested-for-loop-nonterminating.c index 8b451e56dd..0368120b13 100644 --- a/tests/regression/74-loop_termination/08-nested-for-loop-nonterminating.c +++ b/tests/regression/74-loop_termination/08-nested-for-loop-nonterminating.c @@ -1,4 +1,4 @@ -// TODO NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() From 2430f3fc9a798d37025f50e80fc5000551e5b45f Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Thu, 29 Jun 2023 13:17:23 +0200 Subject: [PATCH 0221/1312] Renamed LOCAL_TERM to TERM --- scripts/update_suite.rb | 26 +++++++++---------- .../01-simple-loop-terminating.c | 2 +- .../02-simple-loop-nonterminating.c | 2 +- .../03-nested-loop-terminating.c | 2 +- .../04-nested-loop-nonterminating.c | 2 +- .../05-for-loop-terminating.c | 2 +- .../06-for-loop-nonterminating.c | 2 +- .../07-nested-for-loop-terminating.c | 2 +- .../08-nested-for-loop-nonterminating.c | 2 +- .../09-complex-for-loop-terminating.c | 2 +- .../10-complex-loop-terminating.c | 2 +- .../11-loopless-termination.c | 2 +- .../12-do-while-instant-terminating.c | 2 +- .../13-do-while-terminating.c | 2 +- .../14-do-while-nonterminating.c | 2 +- .../15-complex-loop-combination-terminating.c | 2 +- ...16-nested-loop-nontrivial-nonterminating.c | 2 +- .../74-loop_termination/17-goto-terminating.c | 2 +- .../18-goto-nonterminating.c | 2 +- .../74-loop_termination/19-rand-terminating.c | 2 +- .../20-rand-nonterminating.c | 2 +- .../21-no-exit-on-rand-unproofable.c | 2 +- .../22-exit-on-rand-unproofable.c | 2 +- .../23-exit-on-rand-terminating.c | 2 +- .../24-upjumping-goto-loopless-terminating.c | 2 +- .../25-leave-loop-goto-terminating.c | 2 +- .../26-enter-loop-goto-terminating.c | 2 +- .../27-upjumping-goto-nonterminating.c | 2 +- .../28-do-while-continue-terminating.c | 2 +- .../29-do-while-continue-nonterminating.c | 2 +- .../30-goto-out-of-inner-loop-terminating.c | 2 +- ...31-goto-out-of-inner-loop-nonterminating.c | 2 +- .../32-multithread-terminating.c | 2 +- .../33-multithread-nonterminating.c | 2 +- .../34-nested-for-loop-nonterminating.c | 2 +- .../01-simple-terminating.c | 2 +- .../02-simple-nonterminating.c | 2 +- .../03-nested-terminating.c | 2 +- .../04-nested-nonterminating.c | 2 +- 39 files changed, 50 insertions(+), 52 deletions(-) diff --git a/scripts/update_suite.rb b/scripts/update_suite.rb index 98eff124af..8841ecea88 100755 --- a/scripts/update_suite.rb +++ b/scripts/update_suite.rb @@ -145,11 +145,11 @@ def collect_warnings @vars = $1 @evals = $2 end - if l =~ /\[NonTerminating\]/ then warnings[-1] = "non_local_term" end # Get NonTerminating warning + if l =~ /\[NonTerminating\]/ then warnings[-1] = "nonterm" end # Get NonTerminating warning next unless l =~ /(.*)\(.*?\:(\d+)(?:\:\d+)?(?:-(?:\d+)(?:\:\d+)?)?\)/ obj,i = $1,$2.to_i - ranking = ["other", "warn", "local_term", "non_local_term", "race", "norace", "deadlock", "nodeadlock", "success", "fail", "unknown"] + ranking = ["other", "warn", "term", "nonterm", "race", "norace", "deadlock", "nodeadlock", "success", "fail", "unknown"] thiswarn = case obj when /\(conf\. \d+\)/ then "race" when /Deadlock/ then "deadlock" @@ -208,9 +208,9 @@ def compare_warnings case type when "deadlock", "race", "fail", "unknown", "warn" check.call warnings[idx] == type - when "non_local_term" + when "nonterm" check.call warnings[idx] == type - when "nowarn", "local_term" + when "nowarn", "term" check.call warnings[idx].nil? when "assert", "success" check.call warnings[idx] == "success" @@ -324,19 +324,17 @@ def parse_tests (lines) case lines[0] when /TODO|SKIP/ case lines[0] - when /NON_LOCAL_TERM/ - tests[-1] = "non_local_term" + when /NONTERM/ + tests[-1] = "nonterm" todo << -1 - when /LOCAL_TERM/ - tests[-1] = "local_term" + when /TERM/ + tests[-1] = "term" todo << -1 end - when /NON_LOCAL_TERM/ - # covers "TERM" as keyword but a combined use of NON_LOCAL_TERM (loop termination) and TERM would be pointless - tests[-1] = "non_local_term" - when /LOCAL_TERM/ - # covers "TERM" as keyword but a combined use of NON_LOCAL_TERM (loop termination) and TERM would be pointless - tests[-1] = "local_term" + when /NONTERM/ + tests[-1] = "nonterm" + when /TERM/ + tests[-1] = "term" end Tests.new(self, tests, tests_line, todo) end diff --git a/tests/regression/74-loop_termination/01-simple-loop-terminating.c b/tests/regression/74-loop_termination/01-simple-loop-terminating.c index a517d0d608..a80084868a 100644 --- a/tests/regression/74-loop_termination/01-simple-loop-terminating.c +++ b/tests/regression/74-loop_termination/01-simple-loop-terminating.c @@ -1,4 +1,4 @@ -// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/02-simple-loop-nonterminating.c b/tests/regression/74-loop_termination/02-simple-loop-nonterminating.c index bcb9909f80..eef9f81ea3 100644 --- a/tests/regression/74-loop_termination/02-simple-loop-nonterminating.c +++ b/tests/regression/74-loop_termination/02-simple-loop-nonterminating.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/03-nested-loop-terminating.c b/tests/regression/74-loop_termination/03-nested-loop-terminating.c index 366cbaeea5..5e72ec3284 100644 --- a/tests/regression/74-loop_termination/03-nested-loop-terminating.c +++ b/tests/regression/74-loop_termination/03-nested-loop-terminating.c @@ -1,4 +1,4 @@ -// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/04-nested-loop-nonterminating.c b/tests/regression/74-loop_termination/04-nested-loop-nonterminating.c index ee2aa4a8c4..1fb5ada507 100644 --- a/tests/regression/74-loop_termination/04-nested-loop-nonterminating.c +++ b/tests/regression/74-loop_termination/04-nested-loop-nonterminating.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/05-for-loop-terminating.c b/tests/regression/74-loop_termination/05-for-loop-terminating.c index 2a16184f6d..cf71fa5135 100644 --- a/tests/regression/74-loop_termination/05-for-loop-terminating.c +++ b/tests/regression/74-loop_termination/05-for-loop-terminating.c @@ -1,4 +1,4 @@ -// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/06-for-loop-nonterminating.c b/tests/regression/74-loop_termination/06-for-loop-nonterminating.c index b8f30361d1..8c1500cfb1 100644 --- a/tests/regression/74-loop_termination/06-for-loop-nonterminating.c +++ b/tests/regression/74-loop_termination/06-for-loop-nonterminating.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/74-loop_termination/07-nested-for-loop-terminating.c b/tests/regression/74-loop_termination/07-nested-for-loop-terminating.c index def0787d39..4b3395bd11 100644 --- a/tests/regression/74-loop_termination/07-nested-for-loop-terminating.c +++ b/tests/regression/74-loop_termination/07-nested-for-loop-terminating.c @@ -1,4 +1,4 @@ -// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/08-nested-for-loop-nonterminating.c b/tests/regression/74-loop_termination/08-nested-for-loop-nonterminating.c index 0368120b13..818146e456 100644 --- a/tests/regression/74-loop_termination/08-nested-for-loop-nonterminating.c +++ b/tests/regression/74-loop_termination/08-nested-for-loop-nonterminating.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/09-complex-for-loop-terminating.c b/tests/regression/74-loop_termination/09-complex-for-loop-terminating.c index 7fcd5c5eba..c26fde710f 100644 --- a/tests/regression/74-loop_termination/09-complex-for-loop-terminating.c +++ b/tests/regression/74-loop_termination/09-complex-for-loop-terminating.c @@ -1,4 +1,4 @@ -// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/10-complex-loop-terminating.c b/tests/regression/74-loop_termination/10-complex-loop-terminating.c index def1786937..017c10b8a4 100644 --- a/tests/regression/74-loop_termination/10-complex-loop-terminating.c +++ b/tests/regression/74-loop_termination/10-complex-loop-terminating.c @@ -1,4 +1,4 @@ -// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/11-loopless-termination.c b/tests/regression/74-loop_termination/11-loopless-termination.c index 86a300f18e..01f9a953e0 100644 --- a/tests/regression/74-loop_termination/11-loopless-termination.c +++ b/tests/regression/74-loop_termination/11-loopless-termination.c @@ -1,4 +1,4 @@ -// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/74-loop_termination/12-do-while-instant-terminating.c b/tests/regression/74-loop_termination/12-do-while-instant-terminating.c index 15032b7b4f..b34dff3f5f 100644 --- a/tests/regression/74-loop_termination/12-do-while-instant-terminating.c +++ b/tests/regression/74-loop_termination/12-do-while-instant-terminating.c @@ -1,4 +1,4 @@ -// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/13-do-while-terminating.c b/tests/regression/74-loop_termination/13-do-while-terminating.c index 2e04f3e393..651acb8fd8 100644 --- a/tests/regression/74-loop_termination/13-do-while-terminating.c +++ b/tests/regression/74-loop_termination/13-do-while-terminating.c @@ -1,4 +1,4 @@ -// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/14-do-while-nonterminating.c b/tests/regression/74-loop_termination/14-do-while-nonterminating.c index 5ed18175e9..1e05e2be6e 100644 --- a/tests/regression/74-loop_termination/14-do-while-nonterminating.c +++ b/tests/regression/74-loop_termination/14-do-while-nonterminating.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/15-complex-loop-combination-terminating.c b/tests/regression/74-loop_termination/15-complex-loop-combination-terminating.c index ec6b50512e..07fbe38cfd 100644 --- a/tests/regression/74-loop_termination/15-complex-loop-combination-terminating.c +++ b/tests/regression/74-loop_termination/15-complex-loop-combination-terminating.c @@ -1,4 +1,4 @@ -// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/16-nested-loop-nontrivial-nonterminating.c b/tests/regression/74-loop_termination/16-nested-loop-nontrivial-nonterminating.c index 5ff890e461..b9ccea76af 100644 --- a/tests/regression/74-loop_termination/16-nested-loop-nontrivial-nonterminating.c +++ b/tests/regression/74-loop_termination/16-nested-loop-nontrivial-nonterminating.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/17-goto-terminating.c b/tests/regression/74-loop_termination/17-goto-terminating.c index e988926359..b11b5b3da9 100644 --- a/tests/regression/74-loop_termination/17-goto-terminating.c +++ b/tests/regression/74-loop_termination/17-goto-terminating.c @@ -1,4 +1,4 @@ -// TODO LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/18-goto-nonterminating.c b/tests/regression/74-loop_termination/18-goto-nonterminating.c index cfe5ab481d..aab37803aa 100644 --- a/tests/regression/74-loop_termination/18-goto-nonterminating.c +++ b/tests/regression/74-loop_termination/18-goto-nonterminating.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/19-rand-terminating.c b/tests/regression/74-loop_termination/19-rand-terminating.c index 426c5cdcca..5d3cde9f3d 100644 --- a/tests/regression/74-loop_termination/19-rand-terminating.c +++ b/tests/regression/74-loop_termination/19-rand-terminating.c @@ -1,4 +1,4 @@ -// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include #include #include diff --git a/tests/regression/74-loop_termination/20-rand-nonterminating.c b/tests/regression/74-loop_termination/20-rand-nonterminating.c index 7c21538612..124a19d0f8 100644 --- a/tests/regression/74-loop_termination/20-rand-nonterminating.c +++ b/tests/regression/74-loop_termination/20-rand-nonterminating.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include #include #include diff --git a/tests/regression/74-loop_termination/21-no-exit-on-rand-unproofable.c b/tests/regression/74-loop_termination/21-no-exit-on-rand-unproofable.c index f54af1da7c..3bf479b6f9 100644 --- a/tests/regression/74-loop_termination/21-no-exit-on-rand-unproofable.c +++ b/tests/regression/74-loop_termination/21-no-exit-on-rand-unproofable.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/22-exit-on-rand-unproofable.c b/tests/regression/74-loop_termination/22-exit-on-rand-unproofable.c index 1bc104258d..1f1a9bbd89 100644 --- a/tests/regression/74-loop_termination/22-exit-on-rand-unproofable.c +++ b/tests/regression/74-loop_termination/22-exit-on-rand-unproofable.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/23-exit-on-rand-terminating.c b/tests/regression/74-loop_termination/23-exit-on-rand-terminating.c index 253d38c5df..4b9aacd0fd 100644 --- a/tests/regression/74-loop_termination/23-exit-on-rand-terminating.c +++ b/tests/regression/74-loop_termination/23-exit-on-rand-terminating.c @@ -1,4 +1,4 @@ -// TODO LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include #include diff --git a/tests/regression/74-loop_termination/24-upjumping-goto-loopless-terminating.c b/tests/regression/74-loop_termination/24-upjumping-goto-loopless-terminating.c index 01bffde383..3c4fffdc8a 100644 --- a/tests/regression/74-loop_termination/24-upjumping-goto-loopless-terminating.c +++ b/tests/regression/74-loop_termination/24-upjumping-goto-loopless-terminating.c @@ -1,4 +1,4 @@ -// TODO LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c b/tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c index fed0e218ac..380e98ded0 100644 --- a/tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c +++ b/tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c @@ -1,4 +1,4 @@ -// TODO LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c b/tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c index 2a43933758..b676ca6985 100644 --- a/tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c +++ b/tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c @@ -1,4 +1,4 @@ -// TODO LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/74-loop_termination/27-upjumping-goto-nonterminating.c b/tests/regression/74-loop_termination/27-upjumping-goto-nonterminating.c index a230827356..52ad7ea820 100644 --- a/tests/regression/74-loop_termination/27-upjumping-goto-nonterminating.c +++ b/tests/regression/74-loop_termination/27-upjumping-goto-nonterminating.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/74-loop_termination/28-do-while-continue-terminating.c b/tests/regression/74-loop_termination/28-do-while-continue-terminating.c index 5989c61fed..aa215a502a 100644 --- a/tests/regression/74-loop_termination/28-do-while-continue-terminating.c +++ b/tests/regression/74-loop_termination/28-do-while-continue-terminating.c @@ -1,4 +1,4 @@ -// TODO LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/29-do-while-continue-nonterminating.c b/tests/regression/74-loop_termination/29-do-while-continue-nonterminating.c index 806456e887..896d8fea95 100644 --- a/tests/regression/74-loop_termination/29-do-while-continue-nonterminating.c +++ b/tests/regression/74-loop_termination/29-do-while-continue-nonterminating.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c b/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c index 76c272a654..0526e20bb4 100644 --- a/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c +++ b/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c @@ -1,4 +1,4 @@ -// TODO LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/74-loop_termination/31-goto-out-of-inner-loop-nonterminating.c b/tests/regression/74-loop_termination/31-goto-out-of-inner-loop-nonterminating.c index c1824227d0..722694eb88 100644 --- a/tests/regression/74-loop_termination/31-goto-out-of-inner-loop-nonterminating.c +++ b/tests/regression/74-loop_termination/31-goto-out-of-inner-loop-nonterminating.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/74-loop_termination/32-multithread-terminating.c b/tests/regression/74-loop_termination/32-multithread-terminating.c index a08fe01398..1f98b88eee 100644 --- a/tests/regression/74-loop_termination/32-multithread-terminating.c +++ b/tests/regression/74-loop_termination/32-multithread-terminating.c @@ -1,4 +1,4 @@ -// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include #include #include diff --git a/tests/regression/74-loop_termination/33-multithread-nonterminating.c b/tests/regression/74-loop_termination/33-multithread-nonterminating.c index 77cd2aafe6..007af3b57b 100644 --- a/tests/regression/74-loop_termination/33-multithread-nonterminating.c +++ b/tests/regression/74-loop_termination/33-multithread-nonterminating.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include #include #include diff --git a/tests/regression/74-loop_termination/34-nested-for-loop-nonterminating.c b/tests/regression/74-loop_termination/34-nested-for-loop-nonterminating.c index d8aa9d487d..29e4ff3835 100644 --- a/tests/regression/74-loop_termination/34-nested-for-loop-nonterminating.c +++ b/tests/regression/74-loop_termination/34-nested-for-loop-nonterminating.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/75-recursion_termination/01-simple-terminating.c b/tests/regression/75-recursion_termination/01-simple-terminating.c index 4f09950025..583f8ccca1 100644 --- a/tests/regression/75-recursion_termination/01-simple-terminating.c +++ b/tests/regression/75-recursion_termination/01-simple-terminating.c @@ -1,4 +1,4 @@ -// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include void recursiveFunction(int n) { diff --git a/tests/regression/75-recursion_termination/02-simple-nonterminating.c b/tests/regression/75-recursion_termination/02-simple-nonterminating.c index 37c4bbd801..41a2b7b678 100644 --- a/tests/regression/75-recursion_termination/02-simple-nonterminating.c +++ b/tests/regression/75-recursion_termination/02-simple-nonterminating.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include void recursiveFunction(int n) { diff --git a/tests/regression/75-recursion_termination/03-nested-terminating.c b/tests/regression/75-recursion_termination/03-nested-terminating.c index 23bedef644..4cede747f2 100644 --- a/tests/regression/75-recursion_termination/03-nested-terminating.c +++ b/tests/regression/75-recursion_termination/03-nested-terminating.c @@ -1,4 +1,4 @@ -// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include void innerRecursiveFunction(int n) { diff --git a/tests/regression/75-recursion_termination/04-nested-nonterminating.c b/tests/regression/75-recursion_termination/04-nested-nonterminating.c index d28685e139..2d3239f371 100644 --- a/tests/regression/75-recursion_termination/04-nested-nonterminating.c +++ b/tests/regression/75-recursion_termination/04-nested-nonterminating.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include void innerRecursiveFunction() { From 2c7394861d6070f96e8287b24c1b94887e9dd608 Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Thu, 29 Jun 2023 13:42:29 +0200 Subject: [PATCH 0222/1312] Reformat --- .../75-recursion_termination/02-simple-nonterminating.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression/75-recursion_termination/02-simple-nonterminating.c b/tests/regression/75-recursion_termination/02-simple-nonterminating.c index 41a2b7b678..0dc3cbcf63 100644 --- a/tests/regression/75-recursion_termination/02-simple-nonterminating.c +++ b/tests/regression/75-recursion_termination/02-simple-nonterminating.c @@ -1,4 +1,4 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include void recursiveFunction(int n) { From 7f484cb2abf7f438313e7730951dfa1a73ebd928 Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Thu, 29 Jun 2023 13:43:41 +0200 Subject: [PATCH 0223/1312] Skip crashing tests --- .../74-loop_termination/09-complex-for-loop-terminating.c | 2 +- .../74-loop_termination/10-complex-loop-terminating.c | 2 +- .../15-complex-loop-combination-terminating.c | 2 +- .../75-recursion_termination/04-nested-nonterminating.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/regression/74-loop_termination/09-complex-for-loop-terminating.c b/tests/regression/74-loop_termination/09-complex-for-loop-terminating.c index c26fde710f..7fb9262aab 100644 --- a/tests/regression/74-loop_termination/09-complex-for-loop-terminating.c +++ b/tests/regression/74-loop_termination/09-complex-for-loop-terminating.c @@ -1,4 +1,4 @@ -// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/10-complex-loop-terminating.c b/tests/regression/74-loop_termination/10-complex-loop-terminating.c index 017c10b8a4..e39613b563 100644 --- a/tests/regression/74-loop_termination/10-complex-loop-terminating.c +++ b/tests/regression/74-loop_termination/10-complex-loop-terminating.c @@ -1,4 +1,4 @@ -// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/15-complex-loop-combination-terminating.c b/tests/regression/74-loop_termination/15-complex-loop-combination-terminating.c index 07fbe38cfd..ad64a9a5f9 100644 --- a/tests/regression/74-loop_termination/15-complex-loop-combination-terminating.c +++ b/tests/regression/74-loop_termination/15-complex-loop-combination-terminating.c @@ -1,4 +1,4 @@ -// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/75-recursion_termination/04-nested-nonterminating.c b/tests/regression/75-recursion_termination/04-nested-nonterminating.c index 2d3239f371..5299544a70 100644 --- a/tests/regression/75-recursion_termination/04-nested-nonterminating.c +++ b/tests/regression/75-recursion_termination/04-nested-nonterminating.c @@ -1,4 +1,4 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include void innerRecursiveFunction() { From a91e716de0d7bcdce098b31babe6333ee32111dd Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Thu, 29 Jun 2023 13:45:31 +0200 Subject: [PATCH 0224/1312] Rename loop termination analysis --- src/analyses/{termination_new.ml => loop_termination.ml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/analyses/{termination_new.ml => loop_termination.ml} (100%) diff --git a/src/analyses/termination_new.ml b/src/analyses/loop_termination.ml similarity index 100% rename from src/analyses/termination_new.ml rename to src/analyses/loop_termination.ml From 8483cece62310439d56da7a373781ca74e9de848 Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Thu, 29 Jun 2023 14:06:34 +0200 Subject: [PATCH 0225/1312] Removed Todos --- tests/regression/74-loop_termination/17-goto-terminating.c | 2 +- .../74-loop_termination/23-exit-on-rand-terminating.c | 2 +- .../24-upjumping-goto-loopless-terminating.c | 2 +- .../74-loop_termination/25-leave-loop-goto-terminating.c | 2 +- .../74-loop_termination/26-enter-loop-goto-terminating.c | 2 +- .../74-loop_termination/28-do-while-continue-terminating.c | 2 +- .../74-loop_termination/30-goto-out-of-inner-loop-terminating.c | 2 +- .../75-recursion_termination/02-simple-nonterminating.c | 2 +- .../75-recursion_termination/04-nested-nonterminating.c | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/regression/74-loop_termination/17-goto-terminating.c b/tests/regression/74-loop_termination/17-goto-terminating.c index b11b5b3da9..1e4c1e719e 100644 --- a/tests/regression/74-loop_termination/17-goto-terminating.c +++ b/tests/regression/74-loop_termination/17-goto-terminating.c @@ -1,4 +1,4 @@ -// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/23-exit-on-rand-terminating.c b/tests/regression/74-loop_termination/23-exit-on-rand-terminating.c index 4b9aacd0fd..94e47d5d9a 100644 --- a/tests/regression/74-loop_termination/23-exit-on-rand-terminating.c +++ b/tests/regression/74-loop_termination/23-exit-on-rand-terminating.c @@ -1,4 +1,4 @@ -// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include #include diff --git a/tests/regression/74-loop_termination/24-upjumping-goto-loopless-terminating.c b/tests/regression/74-loop_termination/24-upjumping-goto-loopless-terminating.c index 3c4fffdc8a..e5c4f10f0d 100644 --- a/tests/regression/74-loop_termination/24-upjumping-goto-loopless-terminating.c +++ b/tests/regression/74-loop_termination/24-upjumping-goto-loopless-terminating.c @@ -1,4 +1,4 @@ -// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c b/tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c index 380e98ded0..ea353eb466 100644 --- a/tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c +++ b/tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c @@ -1,4 +1,4 @@ -// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c b/tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c index b676ca6985..8c2f3da4a2 100644 --- a/tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c +++ b/tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c @@ -1,4 +1,4 @@ -// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/74-loop_termination/28-do-while-continue-terminating.c b/tests/regression/74-loop_termination/28-do-while-continue-terminating.c index aa215a502a..7f91ecc149 100644 --- a/tests/regression/74-loop_termination/28-do-while-continue-terminating.c +++ b/tests/regression/74-loop_termination/28-do-while-continue-terminating.c @@ -1,4 +1,4 @@ -// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c b/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c index 0526e20bb4..45cadb583f 100644 --- a/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c +++ b/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c @@ -1,4 +1,4 @@ -// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/75-recursion_termination/02-simple-nonterminating.c b/tests/regression/75-recursion_termination/02-simple-nonterminating.c index 0dc3cbcf63..26f30e726b 100644 --- a/tests/regression/75-recursion_termination/02-simple-nonterminating.c +++ b/tests/regression/75-recursion_termination/02-simple-nonterminating.c @@ -1,4 +1,4 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include void recursiveFunction(int n) { diff --git a/tests/regression/75-recursion_termination/04-nested-nonterminating.c b/tests/regression/75-recursion_termination/04-nested-nonterminating.c index 5299544a70..2d3239f371 100644 --- a/tests/regression/75-recursion_termination/04-nested-nonterminating.c +++ b/tests/regression/75-recursion_termination/04-nested-nonterminating.c @@ -1,4 +1,4 @@ -// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include void innerRecursiveFunction() { From 91f18efe32ca4f7ddc4c163d156b715e4e56685b Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Thu, 29 Jun 2023 14:26:45 +0200 Subject: [PATCH 0226/1312] Adapted tests to current state --- tests/regression/74-loop_termination/17-goto-terminating.c | 4 ++-- .../74-loop_termination/23-exit-on-rand-terminating.c | 4 ++-- .../24-upjumping-goto-loopless-terminating.c | 4 ++-- .../74-loop_termination/25-leave-loop-goto-terminating.c | 4 ++-- .../74-loop_termination/26-enter-loop-goto-terminating.c | 4 ++-- .../30-goto-out-of-inner-loop-terminating.c | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/regression/74-loop_termination/17-goto-terminating.c b/tests/regression/74-loop_termination/17-goto-terminating.c index 1e4c1e719e..c4ba717784 100644 --- a/tests/regression/74-loop_termination/17-goto-terminating.c +++ b/tests/regression/74-loop_termination/17-goto-terminating.c @@ -1,4 +1,4 @@ -// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() @@ -11,7 +11,7 @@ int main() if (num <= 10) { - goto loop; + goto loop; // We are not able to detect up-jumping gotos as terminating, we just warn about them might being nonterminating. } return 0; diff --git a/tests/regression/74-loop_termination/23-exit-on-rand-terminating.c b/tests/regression/74-loop_termination/23-exit-on-rand-terminating.c index 94e47d5d9a..226f46b16e 100644 --- a/tests/regression/74-loop_termination/23-exit-on-rand-terminating.c +++ b/tests/regression/74-loop_termination/23-exit-on-rand-terminating.c @@ -1,4 +1,4 @@ -// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include #include @@ -6,7 +6,7 @@ int main() { int short_run, i = 0; - while (i < 90 && short_run != 1) + while (i < 90 && short_run != 1) // Currently not able to detect this as terminating { i++; if (rand()) diff --git a/tests/regression/74-loop_termination/24-upjumping-goto-loopless-terminating.c b/tests/regression/74-loop_termination/24-upjumping-goto-loopless-terminating.c index e5c4f10f0d..e256df9986 100644 --- a/tests/regression/74-loop_termination/24-upjumping-goto-loopless-terminating.c +++ b/tests/regression/74-loop_termination/24-upjumping-goto-loopless-terminating.c @@ -1,7 +1,7 @@ -// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() { // Currently not able to detect this as terminating goto mark2; mark1: diff --git a/tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c b/tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c index ea353eb466..cbbb115868 100644 --- a/tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c +++ b/tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c @@ -1,4 +1,4 @@ -// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { @@ -13,7 +13,7 @@ int main() { printf("Result: %d\n", result); // Condition to terminate the loop - if (result >= 10) { + if (result >= 10) { // Apron is not able to detect this goto end; } } diff --git a/tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c b/tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c index 8c2f3da4a2..17220a589b 100644 --- a/tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c +++ b/tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c @@ -1,4 +1,4 @@ -// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { @@ -16,7 +16,7 @@ int main() { printf("Result: %d\n", result); // Condition to terminate the loop - if (result >= 10) { + if (result >= 10) { // Apron is not able to detect this goto end; } } diff --git a/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c b/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c index 45cadb583f..6b36919c2d 100644 --- a/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c +++ b/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c @@ -1,4 +1,4 @@ -// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { @@ -9,7 +9,7 @@ int main() { for (int i = 1; i <= rows; i++) { // Inner loop for columns for (int j = 1; j <= columns; j++) { - if (j == 3) { + if (j == 3) { // Apron is not able to detect this goto outer_loop; // Jump to the label "outer_loop" } printf("(%d, %d) ", i, j); From 1c430398152499f9b7450100c563c2295a4f61e3 Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Thu, 29 Jun 2023 14:32:01 +0200 Subject: [PATCH 0227/1312] Adapted tests to current state --- .../74-loop_termination/25-leave-loop-goto-terminating.c | 2 +- .../74-loop_termination/26-enter-loop-goto-terminating.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c b/tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c index cbbb115868..ce11a73060 100644 --- a/tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c +++ b/tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c @@ -1,4 +1,4 @@ -// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain octagon #include int main() { diff --git a/tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c b/tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c index 17220a589b..355c1ebf00 100644 --- a/tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c +++ b/tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c @@ -1,4 +1,4 @@ -// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { From c890d5b3cb9649a0a46eb505bbe56178f15f8aaf Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Thu, 29 Jun 2023 12:24:33 +0200 Subject: [PATCH 0228/1312] No skipping --- .../regression/80-termination/09-complex-for-loop-terminating.c | 2 +- tests/regression/80-termination/10-complex-loop-terminating.c | 2 +- .../80-termination/15-complex-loop-combination-terminating.c | 2 +- tests/regression/81-recursion/02-simple-nonterminating.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/regression/80-termination/09-complex-for-loop-terminating.c b/tests/regression/80-termination/09-complex-for-loop-terminating.c index 8951924c06..7fcd5c5eba 100644 --- a/tests/regression/80-termination/09-complex-for-loop-terminating.c +++ b/tests/regression/80-termination/09-complex-for-loop-terminating.c @@ -1,4 +1,4 @@ -// SKIP LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/80-termination/10-complex-loop-terminating.c b/tests/regression/80-termination/10-complex-loop-terminating.c index dbce2a6b5b..def1786937 100644 --- a/tests/regression/80-termination/10-complex-loop-terminating.c +++ b/tests/regression/80-termination/10-complex-loop-terminating.c @@ -1,4 +1,4 @@ -// SKIP LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/80-termination/15-complex-loop-combination-terminating.c b/tests/regression/80-termination/15-complex-loop-combination-terminating.c index 7daa9b98fe..9ab04745e8 100644 --- a/tests/regression/80-termination/15-complex-loop-combination-terminating.c +++ b/tests/regression/80-termination/15-complex-loop-combination-terminating.c @@ -1,4 +1,4 @@ -// SKIP LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval +// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval #include int main() diff --git a/tests/regression/81-recursion/02-simple-nonterminating.c b/tests/regression/81-recursion/02-simple-nonterminating.c index c7902e2e7f..37c4bbd801 100644 --- a/tests/regression/81-recursion/02-simple-nonterminating.c +++ b/tests/regression/81-recursion/02-simple-nonterminating.c @@ -1,4 +1,4 @@ -// SKIP NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include void recursiveFunction(int n) { From 569fefdc111e23eb159b51076e36de9c0be794e6 Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Thu, 29 Jun 2023 12:31:36 +0200 Subject: [PATCH 0229/1312] New non terminating for loop --- .../34-nested-for-loop-nonterminating.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 tests/regression/80-termination/34-nested-for-loop-nonterminating.c diff --git a/tests/regression/80-termination/34-nested-for-loop-nonterminating.c b/tests/regression/80-termination/34-nested-for-loop-nonterminating.c new file mode 100644 index 0000000000..1f68f3c0a5 --- /dev/null +++ b/tests/regression/80-termination/34-nested-for-loop-nonterminating.c @@ -0,0 +1,19 @@ +// TODO NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int outerCount, innerCount; + + for (outerCount = 1; outerCount <= 3; outerCount++) + { + for (innerCount = 1; innerCount > 0; innerCount++) + { + printf("(%d, %d) ", outerCount, innerCount); + } + + printf("\n"); + } + + return 0; +} From aca1ec58c0a8cab65883b19ad963fdad6826b927 Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Thu, 29 Jun 2023 12:34:13 +0200 Subject: [PATCH 0230/1312] Folder Renaming --- .../01-simple-loop-terminating.c | 0 .../02-simple-loop-nonterminating.c | 0 .../03-nested-loop-terminating.c | 0 .../04-nested-loop-nonterminating.c | 0 .../05-for-loop-terminating.c | 0 .../06-for-loop-nonterminating.c | 0 .../07-nested-for-loop-terminating.c | 0 .../08-nested-for-loop-nonterminating.c | 0 .../09-complex-for-loop-terminating.c | 0 .../10-complex-loop-terminating.c | 0 .../11-loopless-termination.c | 0 .../12-do-while-instant-terminating.c | 0 .../13-do-while-terminating.c | 0 .../14-do-while-nonterminating.c | 0 .../15-complex-loop-combination-terminating.c | 0 .../16-nested-loop-nontrivial-nonterminating.c | 0 .../{80-termination => 74-loop_termination}/17-goto-terminating.c | 0 .../18-goto-nonterminating.c | 0 .../{80-termination => 74-loop_termination}/19-rand-terminating.c | 0 .../20-rand-nonterminating.c | 0 .../21-no-exit-on-rand-unproofable.c | 0 .../22-exit-on-rand-unproofable.c | 0 .../23-exit-on-rand-terminating.c | 0 .../24-upjumping-goto-loopless-terminating.c | 0 .../25-leave-loop-goto-terminating.c | 0 .../26-enter-loop-goto-terminating.c | 0 .../27-upjumping-goto-nonterminating.c | 0 .../28-do-while-continue-terminating.c | 0 .../29-do-while-continue-nonterminating.c | 0 .../30-goto-out-of-inner-loop-terminating.c | 0 .../31-goto-out-of-inner-loop-nonterminating.c | 0 .../32-multithread-terminating.c | 0 .../33-multithread-nonterminating.c | 0 .../34-nested-for-loop-nonterminating.c | 0 .../01-simple-terminating.c | 0 .../02-simple-nonterminating.c | 0 .../03-nested-terminating.c | 0 .../04-nested-nonterminating.c | 0 38 files changed, 0 insertions(+), 0 deletions(-) rename tests/regression/{80-termination => 74-loop_termination}/01-simple-loop-terminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/02-simple-loop-nonterminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/03-nested-loop-terminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/04-nested-loop-nonterminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/05-for-loop-terminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/06-for-loop-nonterminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/07-nested-for-loop-terminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/08-nested-for-loop-nonterminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/09-complex-for-loop-terminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/10-complex-loop-terminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/11-loopless-termination.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/12-do-while-instant-terminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/13-do-while-terminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/14-do-while-nonterminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/15-complex-loop-combination-terminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/16-nested-loop-nontrivial-nonterminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/17-goto-terminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/18-goto-nonterminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/19-rand-terminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/20-rand-nonterminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/21-no-exit-on-rand-unproofable.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/22-exit-on-rand-unproofable.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/23-exit-on-rand-terminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/24-upjumping-goto-loopless-terminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/25-leave-loop-goto-terminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/26-enter-loop-goto-terminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/27-upjumping-goto-nonterminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/28-do-while-continue-terminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/29-do-while-continue-nonterminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/30-goto-out-of-inner-loop-terminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/31-goto-out-of-inner-loop-nonterminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/32-multithread-terminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/33-multithread-nonterminating.c (100%) rename tests/regression/{80-termination => 74-loop_termination}/34-nested-for-loop-nonterminating.c (100%) rename tests/regression/{81-recursion => 75-recursion_termination}/01-simple-terminating.c (100%) rename tests/regression/{81-recursion => 75-recursion_termination}/02-simple-nonterminating.c (100%) rename tests/regression/{81-recursion => 75-recursion_termination}/03-nested-terminating.c (100%) rename tests/regression/{81-recursion => 75-recursion_termination}/04-nested-nonterminating.c (100%) diff --git a/tests/regression/80-termination/01-simple-loop-terminating.c b/tests/regression/74-loop_termination/01-simple-loop-terminating.c similarity index 100% rename from tests/regression/80-termination/01-simple-loop-terminating.c rename to tests/regression/74-loop_termination/01-simple-loop-terminating.c diff --git a/tests/regression/80-termination/02-simple-loop-nonterminating.c b/tests/regression/74-loop_termination/02-simple-loop-nonterminating.c similarity index 100% rename from tests/regression/80-termination/02-simple-loop-nonterminating.c rename to tests/regression/74-loop_termination/02-simple-loop-nonterminating.c diff --git a/tests/regression/80-termination/03-nested-loop-terminating.c b/tests/regression/74-loop_termination/03-nested-loop-terminating.c similarity index 100% rename from tests/regression/80-termination/03-nested-loop-terminating.c rename to tests/regression/74-loop_termination/03-nested-loop-terminating.c diff --git a/tests/regression/80-termination/04-nested-loop-nonterminating.c b/tests/regression/74-loop_termination/04-nested-loop-nonterminating.c similarity index 100% rename from tests/regression/80-termination/04-nested-loop-nonterminating.c rename to tests/regression/74-loop_termination/04-nested-loop-nonterminating.c diff --git a/tests/regression/80-termination/05-for-loop-terminating.c b/tests/regression/74-loop_termination/05-for-loop-terminating.c similarity index 100% rename from tests/regression/80-termination/05-for-loop-terminating.c rename to tests/regression/74-loop_termination/05-for-loop-terminating.c diff --git a/tests/regression/80-termination/06-for-loop-nonterminating.c b/tests/regression/74-loop_termination/06-for-loop-nonterminating.c similarity index 100% rename from tests/regression/80-termination/06-for-loop-nonterminating.c rename to tests/regression/74-loop_termination/06-for-loop-nonterminating.c diff --git a/tests/regression/80-termination/07-nested-for-loop-terminating.c b/tests/regression/74-loop_termination/07-nested-for-loop-terminating.c similarity index 100% rename from tests/regression/80-termination/07-nested-for-loop-terminating.c rename to tests/regression/74-loop_termination/07-nested-for-loop-terminating.c diff --git a/tests/regression/80-termination/08-nested-for-loop-nonterminating.c b/tests/regression/74-loop_termination/08-nested-for-loop-nonterminating.c similarity index 100% rename from tests/regression/80-termination/08-nested-for-loop-nonterminating.c rename to tests/regression/74-loop_termination/08-nested-for-loop-nonterminating.c diff --git a/tests/regression/80-termination/09-complex-for-loop-terminating.c b/tests/regression/74-loop_termination/09-complex-for-loop-terminating.c similarity index 100% rename from tests/regression/80-termination/09-complex-for-loop-terminating.c rename to tests/regression/74-loop_termination/09-complex-for-loop-terminating.c diff --git a/tests/regression/80-termination/10-complex-loop-terminating.c b/tests/regression/74-loop_termination/10-complex-loop-terminating.c similarity index 100% rename from tests/regression/80-termination/10-complex-loop-terminating.c rename to tests/regression/74-loop_termination/10-complex-loop-terminating.c diff --git a/tests/regression/80-termination/11-loopless-termination.c b/tests/regression/74-loop_termination/11-loopless-termination.c similarity index 100% rename from tests/regression/80-termination/11-loopless-termination.c rename to tests/regression/74-loop_termination/11-loopless-termination.c diff --git a/tests/regression/80-termination/12-do-while-instant-terminating.c b/tests/regression/74-loop_termination/12-do-while-instant-terminating.c similarity index 100% rename from tests/regression/80-termination/12-do-while-instant-terminating.c rename to tests/regression/74-loop_termination/12-do-while-instant-terminating.c diff --git a/tests/regression/80-termination/13-do-while-terminating.c b/tests/regression/74-loop_termination/13-do-while-terminating.c similarity index 100% rename from tests/regression/80-termination/13-do-while-terminating.c rename to tests/regression/74-loop_termination/13-do-while-terminating.c diff --git a/tests/regression/80-termination/14-do-while-nonterminating.c b/tests/regression/74-loop_termination/14-do-while-nonterminating.c similarity index 100% rename from tests/regression/80-termination/14-do-while-nonterminating.c rename to tests/regression/74-loop_termination/14-do-while-nonterminating.c diff --git a/tests/regression/80-termination/15-complex-loop-combination-terminating.c b/tests/regression/74-loop_termination/15-complex-loop-combination-terminating.c similarity index 100% rename from tests/regression/80-termination/15-complex-loop-combination-terminating.c rename to tests/regression/74-loop_termination/15-complex-loop-combination-terminating.c diff --git a/tests/regression/80-termination/16-nested-loop-nontrivial-nonterminating.c b/tests/regression/74-loop_termination/16-nested-loop-nontrivial-nonterminating.c similarity index 100% rename from tests/regression/80-termination/16-nested-loop-nontrivial-nonterminating.c rename to tests/regression/74-loop_termination/16-nested-loop-nontrivial-nonterminating.c diff --git a/tests/regression/80-termination/17-goto-terminating.c b/tests/regression/74-loop_termination/17-goto-terminating.c similarity index 100% rename from tests/regression/80-termination/17-goto-terminating.c rename to tests/regression/74-loop_termination/17-goto-terminating.c diff --git a/tests/regression/80-termination/18-goto-nonterminating.c b/tests/regression/74-loop_termination/18-goto-nonterminating.c similarity index 100% rename from tests/regression/80-termination/18-goto-nonterminating.c rename to tests/regression/74-loop_termination/18-goto-nonterminating.c diff --git a/tests/regression/80-termination/19-rand-terminating.c b/tests/regression/74-loop_termination/19-rand-terminating.c similarity index 100% rename from tests/regression/80-termination/19-rand-terminating.c rename to tests/regression/74-loop_termination/19-rand-terminating.c diff --git a/tests/regression/80-termination/20-rand-nonterminating.c b/tests/regression/74-loop_termination/20-rand-nonterminating.c similarity index 100% rename from tests/regression/80-termination/20-rand-nonterminating.c rename to tests/regression/74-loop_termination/20-rand-nonterminating.c diff --git a/tests/regression/80-termination/21-no-exit-on-rand-unproofable.c b/tests/regression/74-loop_termination/21-no-exit-on-rand-unproofable.c similarity index 100% rename from tests/regression/80-termination/21-no-exit-on-rand-unproofable.c rename to tests/regression/74-loop_termination/21-no-exit-on-rand-unproofable.c diff --git a/tests/regression/80-termination/22-exit-on-rand-unproofable.c b/tests/regression/74-loop_termination/22-exit-on-rand-unproofable.c similarity index 100% rename from tests/regression/80-termination/22-exit-on-rand-unproofable.c rename to tests/regression/74-loop_termination/22-exit-on-rand-unproofable.c diff --git a/tests/regression/80-termination/23-exit-on-rand-terminating.c b/tests/regression/74-loop_termination/23-exit-on-rand-terminating.c similarity index 100% rename from tests/regression/80-termination/23-exit-on-rand-terminating.c rename to tests/regression/74-loop_termination/23-exit-on-rand-terminating.c diff --git a/tests/regression/80-termination/24-upjumping-goto-loopless-terminating.c b/tests/regression/74-loop_termination/24-upjumping-goto-loopless-terminating.c similarity index 100% rename from tests/regression/80-termination/24-upjumping-goto-loopless-terminating.c rename to tests/regression/74-loop_termination/24-upjumping-goto-loopless-terminating.c diff --git a/tests/regression/80-termination/25-leave-loop-goto-terminating.c b/tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c similarity index 100% rename from tests/regression/80-termination/25-leave-loop-goto-terminating.c rename to tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c diff --git a/tests/regression/80-termination/26-enter-loop-goto-terminating.c b/tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c similarity index 100% rename from tests/regression/80-termination/26-enter-loop-goto-terminating.c rename to tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c diff --git a/tests/regression/80-termination/27-upjumping-goto-nonterminating.c b/tests/regression/74-loop_termination/27-upjumping-goto-nonterminating.c similarity index 100% rename from tests/regression/80-termination/27-upjumping-goto-nonterminating.c rename to tests/regression/74-loop_termination/27-upjumping-goto-nonterminating.c diff --git a/tests/regression/80-termination/28-do-while-continue-terminating.c b/tests/regression/74-loop_termination/28-do-while-continue-terminating.c similarity index 100% rename from tests/regression/80-termination/28-do-while-continue-terminating.c rename to tests/regression/74-loop_termination/28-do-while-continue-terminating.c diff --git a/tests/regression/80-termination/29-do-while-continue-nonterminating.c b/tests/regression/74-loop_termination/29-do-while-continue-nonterminating.c similarity index 100% rename from tests/regression/80-termination/29-do-while-continue-nonterminating.c rename to tests/regression/74-loop_termination/29-do-while-continue-nonterminating.c diff --git a/tests/regression/80-termination/30-goto-out-of-inner-loop-terminating.c b/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c similarity index 100% rename from tests/regression/80-termination/30-goto-out-of-inner-loop-terminating.c rename to tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c diff --git a/tests/regression/80-termination/31-goto-out-of-inner-loop-nonterminating.c b/tests/regression/74-loop_termination/31-goto-out-of-inner-loop-nonterminating.c similarity index 100% rename from tests/regression/80-termination/31-goto-out-of-inner-loop-nonterminating.c rename to tests/regression/74-loop_termination/31-goto-out-of-inner-loop-nonterminating.c diff --git a/tests/regression/80-termination/32-multithread-terminating.c b/tests/regression/74-loop_termination/32-multithread-terminating.c similarity index 100% rename from tests/regression/80-termination/32-multithread-terminating.c rename to tests/regression/74-loop_termination/32-multithread-terminating.c diff --git a/tests/regression/80-termination/33-multithread-nonterminating.c b/tests/regression/74-loop_termination/33-multithread-nonterminating.c similarity index 100% rename from tests/regression/80-termination/33-multithread-nonterminating.c rename to tests/regression/74-loop_termination/33-multithread-nonterminating.c diff --git a/tests/regression/80-termination/34-nested-for-loop-nonterminating.c b/tests/regression/74-loop_termination/34-nested-for-loop-nonterminating.c similarity index 100% rename from tests/regression/80-termination/34-nested-for-loop-nonterminating.c rename to tests/regression/74-loop_termination/34-nested-for-loop-nonterminating.c diff --git a/tests/regression/81-recursion/01-simple-terminating.c b/tests/regression/75-recursion_termination/01-simple-terminating.c similarity index 100% rename from tests/regression/81-recursion/01-simple-terminating.c rename to tests/regression/75-recursion_termination/01-simple-terminating.c diff --git a/tests/regression/81-recursion/02-simple-nonterminating.c b/tests/regression/75-recursion_termination/02-simple-nonterminating.c similarity index 100% rename from tests/regression/81-recursion/02-simple-nonterminating.c rename to tests/regression/75-recursion_termination/02-simple-nonterminating.c diff --git a/tests/regression/81-recursion/03-nested-terminating.c b/tests/regression/75-recursion_termination/03-nested-terminating.c similarity index 100% rename from tests/regression/81-recursion/03-nested-terminating.c rename to tests/regression/75-recursion_termination/03-nested-terminating.c diff --git a/tests/regression/81-recursion/04-nested-nonterminating.c b/tests/regression/75-recursion_termination/04-nested-nonterminating.c similarity index 100% rename from tests/regression/81-recursion/04-nested-nonterminating.c rename to tests/regression/75-recursion_termination/04-nested-nonterminating.c From 70bad4c386e3c7bbaa45ff3aec86e618901fd063 Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Thu, 29 Jun 2023 12:49:43 +0200 Subject: [PATCH 0231/1312] Enable polyhedra; Removed unnecessary TODOs --- .../15-complex-loop-combination-terminating.c | 2 +- .../74-loop_termination/34-nested-for-loop-nonterminating.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/regression/74-loop_termination/15-complex-loop-combination-terminating.c b/tests/regression/74-loop_termination/15-complex-loop-combination-terminating.c index 9ab04745e8..ec6b50512e 100644 --- a/tests/regression/74-loop_termination/15-complex-loop-combination-terminating.c +++ b/tests/regression/74-loop_termination/15-complex-loop-combination-terminating.c @@ -1,4 +1,4 @@ -// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval +// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/34-nested-for-loop-nonterminating.c b/tests/regression/74-loop_termination/34-nested-for-loop-nonterminating.c index 1f68f3c0a5..d8aa9d487d 100644 --- a/tests/regression/74-loop_termination/34-nested-for-loop-nonterminating.c +++ b/tests/regression/74-loop_termination/34-nested-for-loop-nonterminating.c @@ -1,4 +1,4 @@ -// TODO NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() From e70761c567064f43ebdd1b36bf13e34621a4d50f Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Thu, 29 Jun 2023 13:04:25 +0200 Subject: [PATCH 0232/1312] Removed unnecessary TODOs --- .../regression/74-loop_termination/06-for-loop-nonterminating.c | 2 +- .../74-loop_termination/08-nested-for-loop-nonterminating.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/regression/74-loop_termination/06-for-loop-nonterminating.c b/tests/regression/74-loop_termination/06-for-loop-nonterminating.c index 73a8b8c6fd..b8f30361d1 100644 --- a/tests/regression/74-loop_termination/06-for-loop-nonterminating.c +++ b/tests/regression/74-loop_termination/06-for-loop-nonterminating.c @@ -1,4 +1,4 @@ -// TODO NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/74-loop_termination/08-nested-for-loop-nonterminating.c b/tests/regression/74-loop_termination/08-nested-for-loop-nonterminating.c index 8b451e56dd..0368120b13 100644 --- a/tests/regression/74-loop_termination/08-nested-for-loop-nonterminating.c +++ b/tests/regression/74-loop_termination/08-nested-for-loop-nonterminating.c @@ -1,4 +1,4 @@ -// TODO NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() From ee9392942d4df2d43cfab49e4002394b2d42fd33 Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Thu, 29 Jun 2023 13:17:23 +0200 Subject: [PATCH 0233/1312] Renamed LOCAL_TERM to TERM --- scripts/update_suite.rb | 26 +++++++++---------- .../01-simple-loop-terminating.c | 2 +- .../02-simple-loop-nonterminating.c | 2 +- .../03-nested-loop-terminating.c | 2 +- .../04-nested-loop-nonterminating.c | 2 +- .../05-for-loop-terminating.c | 2 +- .../06-for-loop-nonterminating.c | 2 +- .../07-nested-for-loop-terminating.c | 2 +- .../08-nested-for-loop-nonterminating.c | 2 +- .../09-complex-for-loop-terminating.c | 2 +- .../10-complex-loop-terminating.c | 2 +- .../11-loopless-termination.c | 2 +- .../12-do-while-instant-terminating.c | 2 +- .../13-do-while-terminating.c | 2 +- .../14-do-while-nonterminating.c | 2 +- .../15-complex-loop-combination-terminating.c | 2 +- ...16-nested-loop-nontrivial-nonterminating.c | 2 +- .../74-loop_termination/17-goto-terminating.c | 2 +- .../18-goto-nonterminating.c | 2 +- .../74-loop_termination/19-rand-terminating.c | 2 +- .../20-rand-nonterminating.c | 2 +- .../21-no-exit-on-rand-unproofable.c | 2 +- .../22-exit-on-rand-unproofable.c | 2 +- .../23-exit-on-rand-terminating.c | 2 +- .../24-upjumping-goto-loopless-terminating.c | 2 +- .../25-leave-loop-goto-terminating.c | 2 +- .../26-enter-loop-goto-terminating.c | 2 +- .../27-upjumping-goto-nonterminating.c | 2 +- .../28-do-while-continue-terminating.c | 2 +- .../29-do-while-continue-nonterminating.c | 2 +- .../30-goto-out-of-inner-loop-terminating.c | 2 +- ...31-goto-out-of-inner-loop-nonterminating.c | 2 +- .../32-multithread-terminating.c | 2 +- .../33-multithread-nonterminating.c | 2 +- .../34-nested-for-loop-nonterminating.c | 2 +- .../01-simple-terminating.c | 2 +- .../02-simple-nonterminating.c | 2 +- .../03-nested-terminating.c | 2 +- .../04-nested-nonterminating.c | 2 +- 39 files changed, 50 insertions(+), 52 deletions(-) diff --git a/scripts/update_suite.rb b/scripts/update_suite.rb index 98eff124af..8841ecea88 100755 --- a/scripts/update_suite.rb +++ b/scripts/update_suite.rb @@ -145,11 +145,11 @@ def collect_warnings @vars = $1 @evals = $2 end - if l =~ /\[NonTerminating\]/ then warnings[-1] = "non_local_term" end # Get NonTerminating warning + if l =~ /\[NonTerminating\]/ then warnings[-1] = "nonterm" end # Get NonTerminating warning next unless l =~ /(.*)\(.*?\:(\d+)(?:\:\d+)?(?:-(?:\d+)(?:\:\d+)?)?\)/ obj,i = $1,$2.to_i - ranking = ["other", "warn", "local_term", "non_local_term", "race", "norace", "deadlock", "nodeadlock", "success", "fail", "unknown"] + ranking = ["other", "warn", "term", "nonterm", "race", "norace", "deadlock", "nodeadlock", "success", "fail", "unknown"] thiswarn = case obj when /\(conf\. \d+\)/ then "race" when /Deadlock/ then "deadlock" @@ -208,9 +208,9 @@ def compare_warnings case type when "deadlock", "race", "fail", "unknown", "warn" check.call warnings[idx] == type - when "non_local_term" + when "nonterm" check.call warnings[idx] == type - when "nowarn", "local_term" + when "nowarn", "term" check.call warnings[idx].nil? when "assert", "success" check.call warnings[idx] == "success" @@ -324,19 +324,17 @@ def parse_tests (lines) case lines[0] when /TODO|SKIP/ case lines[0] - when /NON_LOCAL_TERM/ - tests[-1] = "non_local_term" + when /NONTERM/ + tests[-1] = "nonterm" todo << -1 - when /LOCAL_TERM/ - tests[-1] = "local_term" + when /TERM/ + tests[-1] = "term" todo << -1 end - when /NON_LOCAL_TERM/ - # covers "TERM" as keyword but a combined use of NON_LOCAL_TERM (loop termination) and TERM would be pointless - tests[-1] = "non_local_term" - when /LOCAL_TERM/ - # covers "TERM" as keyword but a combined use of NON_LOCAL_TERM (loop termination) and TERM would be pointless - tests[-1] = "local_term" + when /NONTERM/ + tests[-1] = "nonterm" + when /TERM/ + tests[-1] = "term" end Tests.new(self, tests, tests_line, todo) end diff --git a/tests/regression/74-loop_termination/01-simple-loop-terminating.c b/tests/regression/74-loop_termination/01-simple-loop-terminating.c index a517d0d608..a80084868a 100644 --- a/tests/regression/74-loop_termination/01-simple-loop-terminating.c +++ b/tests/regression/74-loop_termination/01-simple-loop-terminating.c @@ -1,4 +1,4 @@ -// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/02-simple-loop-nonterminating.c b/tests/regression/74-loop_termination/02-simple-loop-nonterminating.c index bcb9909f80..eef9f81ea3 100644 --- a/tests/regression/74-loop_termination/02-simple-loop-nonterminating.c +++ b/tests/regression/74-loop_termination/02-simple-loop-nonterminating.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/03-nested-loop-terminating.c b/tests/regression/74-loop_termination/03-nested-loop-terminating.c index 366cbaeea5..5e72ec3284 100644 --- a/tests/regression/74-loop_termination/03-nested-loop-terminating.c +++ b/tests/regression/74-loop_termination/03-nested-loop-terminating.c @@ -1,4 +1,4 @@ -// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/04-nested-loop-nonterminating.c b/tests/regression/74-loop_termination/04-nested-loop-nonterminating.c index ee2aa4a8c4..1fb5ada507 100644 --- a/tests/regression/74-loop_termination/04-nested-loop-nonterminating.c +++ b/tests/regression/74-loop_termination/04-nested-loop-nonterminating.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/05-for-loop-terminating.c b/tests/regression/74-loop_termination/05-for-loop-terminating.c index 2a16184f6d..cf71fa5135 100644 --- a/tests/regression/74-loop_termination/05-for-loop-terminating.c +++ b/tests/regression/74-loop_termination/05-for-loop-terminating.c @@ -1,4 +1,4 @@ -// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/06-for-loop-nonterminating.c b/tests/regression/74-loop_termination/06-for-loop-nonterminating.c index b8f30361d1..8c1500cfb1 100644 --- a/tests/regression/74-loop_termination/06-for-loop-nonterminating.c +++ b/tests/regression/74-loop_termination/06-for-loop-nonterminating.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/74-loop_termination/07-nested-for-loop-terminating.c b/tests/regression/74-loop_termination/07-nested-for-loop-terminating.c index def0787d39..4b3395bd11 100644 --- a/tests/regression/74-loop_termination/07-nested-for-loop-terminating.c +++ b/tests/regression/74-loop_termination/07-nested-for-loop-terminating.c @@ -1,4 +1,4 @@ -// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/08-nested-for-loop-nonterminating.c b/tests/regression/74-loop_termination/08-nested-for-loop-nonterminating.c index 0368120b13..818146e456 100644 --- a/tests/regression/74-loop_termination/08-nested-for-loop-nonterminating.c +++ b/tests/regression/74-loop_termination/08-nested-for-loop-nonterminating.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/09-complex-for-loop-terminating.c b/tests/regression/74-loop_termination/09-complex-for-loop-terminating.c index 7fcd5c5eba..c26fde710f 100644 --- a/tests/regression/74-loop_termination/09-complex-for-loop-terminating.c +++ b/tests/regression/74-loop_termination/09-complex-for-loop-terminating.c @@ -1,4 +1,4 @@ -// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/10-complex-loop-terminating.c b/tests/regression/74-loop_termination/10-complex-loop-terminating.c index def1786937..017c10b8a4 100644 --- a/tests/regression/74-loop_termination/10-complex-loop-terminating.c +++ b/tests/regression/74-loop_termination/10-complex-loop-terminating.c @@ -1,4 +1,4 @@ -// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/11-loopless-termination.c b/tests/regression/74-loop_termination/11-loopless-termination.c index 86a300f18e..01f9a953e0 100644 --- a/tests/regression/74-loop_termination/11-loopless-termination.c +++ b/tests/regression/74-loop_termination/11-loopless-termination.c @@ -1,4 +1,4 @@ -// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/74-loop_termination/12-do-while-instant-terminating.c b/tests/regression/74-loop_termination/12-do-while-instant-terminating.c index 15032b7b4f..b34dff3f5f 100644 --- a/tests/regression/74-loop_termination/12-do-while-instant-terminating.c +++ b/tests/regression/74-loop_termination/12-do-while-instant-terminating.c @@ -1,4 +1,4 @@ -// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/13-do-while-terminating.c b/tests/regression/74-loop_termination/13-do-while-terminating.c index 2e04f3e393..651acb8fd8 100644 --- a/tests/regression/74-loop_termination/13-do-while-terminating.c +++ b/tests/regression/74-loop_termination/13-do-while-terminating.c @@ -1,4 +1,4 @@ -// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/14-do-while-nonterminating.c b/tests/regression/74-loop_termination/14-do-while-nonterminating.c index 5ed18175e9..1e05e2be6e 100644 --- a/tests/regression/74-loop_termination/14-do-while-nonterminating.c +++ b/tests/regression/74-loop_termination/14-do-while-nonterminating.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/15-complex-loop-combination-terminating.c b/tests/regression/74-loop_termination/15-complex-loop-combination-terminating.c index ec6b50512e..07fbe38cfd 100644 --- a/tests/regression/74-loop_termination/15-complex-loop-combination-terminating.c +++ b/tests/regression/74-loop_termination/15-complex-loop-combination-terminating.c @@ -1,4 +1,4 @@ -// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/16-nested-loop-nontrivial-nonterminating.c b/tests/regression/74-loop_termination/16-nested-loop-nontrivial-nonterminating.c index 5ff890e461..b9ccea76af 100644 --- a/tests/regression/74-loop_termination/16-nested-loop-nontrivial-nonterminating.c +++ b/tests/regression/74-loop_termination/16-nested-loop-nontrivial-nonterminating.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/17-goto-terminating.c b/tests/regression/74-loop_termination/17-goto-terminating.c index e988926359..b11b5b3da9 100644 --- a/tests/regression/74-loop_termination/17-goto-terminating.c +++ b/tests/regression/74-loop_termination/17-goto-terminating.c @@ -1,4 +1,4 @@ -// TODO LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/18-goto-nonterminating.c b/tests/regression/74-loop_termination/18-goto-nonterminating.c index cfe5ab481d..aab37803aa 100644 --- a/tests/regression/74-loop_termination/18-goto-nonterminating.c +++ b/tests/regression/74-loop_termination/18-goto-nonterminating.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/19-rand-terminating.c b/tests/regression/74-loop_termination/19-rand-terminating.c index 426c5cdcca..5d3cde9f3d 100644 --- a/tests/regression/74-loop_termination/19-rand-terminating.c +++ b/tests/regression/74-loop_termination/19-rand-terminating.c @@ -1,4 +1,4 @@ -// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include #include #include diff --git a/tests/regression/74-loop_termination/20-rand-nonterminating.c b/tests/regression/74-loop_termination/20-rand-nonterminating.c index 7c21538612..124a19d0f8 100644 --- a/tests/regression/74-loop_termination/20-rand-nonterminating.c +++ b/tests/regression/74-loop_termination/20-rand-nonterminating.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include #include #include diff --git a/tests/regression/74-loop_termination/21-no-exit-on-rand-unproofable.c b/tests/regression/74-loop_termination/21-no-exit-on-rand-unproofable.c index f54af1da7c..3bf479b6f9 100644 --- a/tests/regression/74-loop_termination/21-no-exit-on-rand-unproofable.c +++ b/tests/regression/74-loop_termination/21-no-exit-on-rand-unproofable.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/22-exit-on-rand-unproofable.c b/tests/regression/74-loop_termination/22-exit-on-rand-unproofable.c index 1bc104258d..1f1a9bbd89 100644 --- a/tests/regression/74-loop_termination/22-exit-on-rand-unproofable.c +++ b/tests/regression/74-loop_termination/22-exit-on-rand-unproofable.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/23-exit-on-rand-terminating.c b/tests/regression/74-loop_termination/23-exit-on-rand-terminating.c index 253d38c5df..4b9aacd0fd 100644 --- a/tests/regression/74-loop_termination/23-exit-on-rand-terminating.c +++ b/tests/regression/74-loop_termination/23-exit-on-rand-terminating.c @@ -1,4 +1,4 @@ -// TODO LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include #include diff --git a/tests/regression/74-loop_termination/24-upjumping-goto-loopless-terminating.c b/tests/regression/74-loop_termination/24-upjumping-goto-loopless-terminating.c index 01bffde383..3c4fffdc8a 100644 --- a/tests/regression/74-loop_termination/24-upjumping-goto-loopless-terminating.c +++ b/tests/regression/74-loop_termination/24-upjumping-goto-loopless-terminating.c @@ -1,4 +1,4 @@ -// TODO LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c b/tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c index fed0e218ac..380e98ded0 100644 --- a/tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c +++ b/tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c @@ -1,4 +1,4 @@ -// TODO LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c b/tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c index 2a43933758..b676ca6985 100644 --- a/tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c +++ b/tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c @@ -1,4 +1,4 @@ -// TODO LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/74-loop_termination/27-upjumping-goto-nonterminating.c b/tests/regression/74-loop_termination/27-upjumping-goto-nonterminating.c index a230827356..52ad7ea820 100644 --- a/tests/regression/74-loop_termination/27-upjumping-goto-nonterminating.c +++ b/tests/regression/74-loop_termination/27-upjumping-goto-nonterminating.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/74-loop_termination/28-do-while-continue-terminating.c b/tests/regression/74-loop_termination/28-do-while-continue-terminating.c index 5989c61fed..aa215a502a 100644 --- a/tests/regression/74-loop_termination/28-do-while-continue-terminating.c +++ b/tests/regression/74-loop_termination/28-do-while-continue-terminating.c @@ -1,4 +1,4 @@ -// TODO LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/29-do-while-continue-nonterminating.c b/tests/regression/74-loop_termination/29-do-while-continue-nonterminating.c index 806456e887..896d8fea95 100644 --- a/tests/regression/74-loop_termination/29-do-while-continue-nonterminating.c +++ b/tests/regression/74-loop_termination/29-do-while-continue-nonterminating.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c b/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c index 76c272a654..0526e20bb4 100644 --- a/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c +++ b/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c @@ -1,4 +1,4 @@ -// TODO LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/74-loop_termination/31-goto-out-of-inner-loop-nonterminating.c b/tests/regression/74-loop_termination/31-goto-out-of-inner-loop-nonterminating.c index c1824227d0..722694eb88 100644 --- a/tests/regression/74-loop_termination/31-goto-out-of-inner-loop-nonterminating.c +++ b/tests/regression/74-loop_termination/31-goto-out-of-inner-loop-nonterminating.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/74-loop_termination/32-multithread-terminating.c b/tests/regression/74-loop_termination/32-multithread-terminating.c index a08fe01398..1f98b88eee 100644 --- a/tests/regression/74-loop_termination/32-multithread-terminating.c +++ b/tests/regression/74-loop_termination/32-multithread-terminating.c @@ -1,4 +1,4 @@ -// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include #include #include diff --git a/tests/regression/74-loop_termination/33-multithread-nonterminating.c b/tests/regression/74-loop_termination/33-multithread-nonterminating.c index 77cd2aafe6..007af3b57b 100644 --- a/tests/regression/74-loop_termination/33-multithread-nonterminating.c +++ b/tests/regression/74-loop_termination/33-multithread-nonterminating.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include #include #include diff --git a/tests/regression/74-loop_termination/34-nested-for-loop-nonterminating.c b/tests/regression/74-loop_termination/34-nested-for-loop-nonterminating.c index d8aa9d487d..29e4ff3835 100644 --- a/tests/regression/74-loop_termination/34-nested-for-loop-nonterminating.c +++ b/tests/regression/74-loop_termination/34-nested-for-loop-nonterminating.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/75-recursion_termination/01-simple-terminating.c b/tests/regression/75-recursion_termination/01-simple-terminating.c index 4f09950025..583f8ccca1 100644 --- a/tests/regression/75-recursion_termination/01-simple-terminating.c +++ b/tests/regression/75-recursion_termination/01-simple-terminating.c @@ -1,4 +1,4 @@ -// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include void recursiveFunction(int n) { diff --git a/tests/regression/75-recursion_termination/02-simple-nonterminating.c b/tests/regression/75-recursion_termination/02-simple-nonterminating.c index 37c4bbd801..41a2b7b678 100644 --- a/tests/regression/75-recursion_termination/02-simple-nonterminating.c +++ b/tests/regression/75-recursion_termination/02-simple-nonterminating.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include void recursiveFunction(int n) { diff --git a/tests/regression/75-recursion_termination/03-nested-terminating.c b/tests/regression/75-recursion_termination/03-nested-terminating.c index 23bedef644..4cede747f2 100644 --- a/tests/regression/75-recursion_termination/03-nested-terminating.c +++ b/tests/regression/75-recursion_termination/03-nested-terminating.c @@ -1,4 +1,4 @@ -// LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include void innerRecursiveFunction(int n) { diff --git a/tests/regression/75-recursion_termination/04-nested-nonterminating.c b/tests/regression/75-recursion_termination/04-nested-nonterminating.c index d28685e139..2d3239f371 100644 --- a/tests/regression/75-recursion_termination/04-nested-nonterminating.c +++ b/tests/regression/75-recursion_termination/04-nested-nonterminating.c @@ -1,4 +1,4 @@ -// NON_LOCAL_TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include void innerRecursiveFunction() { From d05da3a797473c9ee813b331900513366d0e2a3a Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Thu, 29 Jun 2023 13:42:29 +0200 Subject: [PATCH 0234/1312] Reformat --- .../75-recursion_termination/02-simple-nonterminating.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression/75-recursion_termination/02-simple-nonterminating.c b/tests/regression/75-recursion_termination/02-simple-nonterminating.c index 41a2b7b678..0dc3cbcf63 100644 --- a/tests/regression/75-recursion_termination/02-simple-nonterminating.c +++ b/tests/regression/75-recursion_termination/02-simple-nonterminating.c @@ -1,4 +1,4 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include void recursiveFunction(int n) { From 036dd175358dbdb2ef1748aed2ca253b28385d32 Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Thu, 29 Jun 2023 13:43:41 +0200 Subject: [PATCH 0235/1312] Skip crashing tests --- .../74-loop_termination/09-complex-for-loop-terminating.c | 2 +- .../74-loop_termination/10-complex-loop-terminating.c | 2 +- .../15-complex-loop-combination-terminating.c | 2 +- .../75-recursion_termination/04-nested-nonterminating.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/regression/74-loop_termination/09-complex-for-loop-terminating.c b/tests/regression/74-loop_termination/09-complex-for-loop-terminating.c index c26fde710f..7fb9262aab 100644 --- a/tests/regression/74-loop_termination/09-complex-for-loop-terminating.c +++ b/tests/regression/74-loop_termination/09-complex-for-loop-terminating.c @@ -1,4 +1,4 @@ -// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/10-complex-loop-terminating.c b/tests/regression/74-loop_termination/10-complex-loop-terminating.c index 017c10b8a4..e39613b563 100644 --- a/tests/regression/74-loop_termination/10-complex-loop-terminating.c +++ b/tests/regression/74-loop_termination/10-complex-loop-terminating.c @@ -1,4 +1,4 @@ -// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/15-complex-loop-combination-terminating.c b/tests/regression/74-loop_termination/15-complex-loop-combination-terminating.c index 07fbe38cfd..ad64a9a5f9 100644 --- a/tests/regression/74-loop_termination/15-complex-loop-combination-terminating.c +++ b/tests/regression/74-loop_termination/15-complex-loop-combination-terminating.c @@ -1,4 +1,4 @@ -// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/75-recursion_termination/04-nested-nonterminating.c b/tests/regression/75-recursion_termination/04-nested-nonterminating.c index 2d3239f371..5299544a70 100644 --- a/tests/regression/75-recursion_termination/04-nested-nonterminating.c +++ b/tests/regression/75-recursion_termination/04-nested-nonterminating.c @@ -1,4 +1,4 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include void innerRecursiveFunction() { From 4cb5da0fb3afca195cdad847f848dd15f0e3cded Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Thu, 29 Jun 2023 14:06:34 +0200 Subject: [PATCH 0236/1312] Removed Todos --- tests/regression/74-loop_termination/17-goto-terminating.c | 2 +- .../74-loop_termination/23-exit-on-rand-terminating.c | 2 +- .../24-upjumping-goto-loopless-terminating.c | 2 +- .../74-loop_termination/25-leave-loop-goto-terminating.c | 2 +- .../74-loop_termination/26-enter-loop-goto-terminating.c | 2 +- .../74-loop_termination/28-do-while-continue-terminating.c | 2 +- .../74-loop_termination/30-goto-out-of-inner-loop-terminating.c | 2 +- .../75-recursion_termination/02-simple-nonterminating.c | 2 +- .../75-recursion_termination/04-nested-nonterminating.c | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/regression/74-loop_termination/17-goto-terminating.c b/tests/regression/74-loop_termination/17-goto-terminating.c index b11b5b3da9..1e4c1e719e 100644 --- a/tests/regression/74-loop_termination/17-goto-terminating.c +++ b/tests/regression/74-loop_termination/17-goto-terminating.c @@ -1,4 +1,4 @@ -// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/23-exit-on-rand-terminating.c b/tests/regression/74-loop_termination/23-exit-on-rand-terminating.c index 4b9aacd0fd..94e47d5d9a 100644 --- a/tests/regression/74-loop_termination/23-exit-on-rand-terminating.c +++ b/tests/regression/74-loop_termination/23-exit-on-rand-terminating.c @@ -1,4 +1,4 @@ -// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include #include diff --git a/tests/regression/74-loop_termination/24-upjumping-goto-loopless-terminating.c b/tests/regression/74-loop_termination/24-upjumping-goto-loopless-terminating.c index 3c4fffdc8a..e5c4f10f0d 100644 --- a/tests/regression/74-loop_termination/24-upjumping-goto-loopless-terminating.c +++ b/tests/regression/74-loop_termination/24-upjumping-goto-loopless-terminating.c @@ -1,4 +1,4 @@ -// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c b/tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c index 380e98ded0..ea353eb466 100644 --- a/tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c +++ b/tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c @@ -1,4 +1,4 @@ -// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c b/tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c index b676ca6985..8c2f3da4a2 100644 --- a/tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c +++ b/tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c @@ -1,4 +1,4 @@ -// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/74-loop_termination/28-do-while-continue-terminating.c b/tests/regression/74-loop_termination/28-do-while-continue-terminating.c index aa215a502a..7f91ecc149 100644 --- a/tests/regression/74-loop_termination/28-do-while-continue-terminating.c +++ b/tests/regression/74-loop_termination/28-do-while-continue-terminating.c @@ -1,4 +1,4 @@ -// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c b/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c index 0526e20bb4..45cadb583f 100644 --- a/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c +++ b/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c @@ -1,4 +1,4 @@ -// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/75-recursion_termination/02-simple-nonterminating.c b/tests/regression/75-recursion_termination/02-simple-nonterminating.c index 0dc3cbcf63..26f30e726b 100644 --- a/tests/regression/75-recursion_termination/02-simple-nonterminating.c +++ b/tests/regression/75-recursion_termination/02-simple-nonterminating.c @@ -1,4 +1,4 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include void recursiveFunction(int n) { diff --git a/tests/regression/75-recursion_termination/04-nested-nonterminating.c b/tests/regression/75-recursion_termination/04-nested-nonterminating.c index 5299544a70..2d3239f371 100644 --- a/tests/regression/75-recursion_termination/04-nested-nonterminating.c +++ b/tests/regression/75-recursion_termination/04-nested-nonterminating.c @@ -1,4 +1,4 @@ -// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include void innerRecursiveFunction() { From fec0d6266469ca2e114c606d7e7c4602018428f2 Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Thu, 29 Jun 2023 14:26:45 +0200 Subject: [PATCH 0237/1312] Adapted tests to current state --- tests/regression/74-loop_termination/17-goto-terminating.c | 4 ++-- .../74-loop_termination/23-exit-on-rand-terminating.c | 4 ++-- .../24-upjumping-goto-loopless-terminating.c | 4 ++-- .../74-loop_termination/25-leave-loop-goto-terminating.c | 4 ++-- .../74-loop_termination/26-enter-loop-goto-terminating.c | 4 ++-- .../30-goto-out-of-inner-loop-terminating.c | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/regression/74-loop_termination/17-goto-terminating.c b/tests/regression/74-loop_termination/17-goto-terminating.c index 1e4c1e719e..c4ba717784 100644 --- a/tests/regression/74-loop_termination/17-goto-terminating.c +++ b/tests/regression/74-loop_termination/17-goto-terminating.c @@ -1,4 +1,4 @@ -// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() @@ -11,7 +11,7 @@ int main() if (num <= 10) { - goto loop; + goto loop; // We are not able to detect up-jumping gotos as terminating, we just warn about them might being nonterminating. } return 0; diff --git a/tests/regression/74-loop_termination/23-exit-on-rand-terminating.c b/tests/regression/74-loop_termination/23-exit-on-rand-terminating.c index 94e47d5d9a..226f46b16e 100644 --- a/tests/regression/74-loop_termination/23-exit-on-rand-terminating.c +++ b/tests/regression/74-loop_termination/23-exit-on-rand-terminating.c @@ -1,4 +1,4 @@ -// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include #include @@ -6,7 +6,7 @@ int main() { int short_run, i = 0; - while (i < 90 && short_run != 1) + while (i < 90 && short_run != 1) // Currently not able to detect this as terminating { i++; if (rand()) diff --git a/tests/regression/74-loop_termination/24-upjumping-goto-loopless-terminating.c b/tests/regression/74-loop_termination/24-upjumping-goto-loopless-terminating.c index e5c4f10f0d..e256df9986 100644 --- a/tests/regression/74-loop_termination/24-upjumping-goto-loopless-terminating.c +++ b/tests/regression/74-loop_termination/24-upjumping-goto-loopless-terminating.c @@ -1,7 +1,7 @@ -// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() { // Currently not able to detect this as terminating goto mark2; mark1: diff --git a/tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c b/tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c index ea353eb466..cbbb115868 100644 --- a/tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c +++ b/tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c @@ -1,4 +1,4 @@ -// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { @@ -13,7 +13,7 @@ int main() { printf("Result: %d\n", result); // Condition to terminate the loop - if (result >= 10) { + if (result >= 10) { // Apron is not able to detect this goto end; } } diff --git a/tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c b/tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c index 8c2f3da4a2..17220a589b 100644 --- a/tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c +++ b/tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c @@ -1,4 +1,4 @@ -// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { @@ -16,7 +16,7 @@ int main() { printf("Result: %d\n", result); // Condition to terminate the loop - if (result >= 10) { + if (result >= 10) { // Apron is not able to detect this goto end; } } diff --git a/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c b/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c index 45cadb583f..6b36919c2d 100644 --- a/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c +++ b/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c @@ -1,4 +1,4 @@ -// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { @@ -9,7 +9,7 @@ int main() { for (int i = 1; i <= rows; i++) { // Inner loop for columns for (int j = 1; j <= columns; j++) { - if (j == 3) { + if (j == 3) { // Apron is not able to detect this goto outer_loop; // Jump to the label "outer_loop" } printf("(%d, %d) ", i, j); From 68b01aff36092ad7727383249d81fe26cf4374a7 Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Thu, 29 Jun 2023 14:32:01 +0200 Subject: [PATCH 0238/1312] Adapted tests to current state --- .../74-loop_termination/25-leave-loop-goto-terminating.c | 2 +- .../74-loop_termination/26-enter-loop-goto-terminating.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c b/tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c index cbbb115868..ce11a73060 100644 --- a/tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c +++ b/tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c @@ -1,4 +1,4 @@ -// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain octagon #include int main() { diff --git a/tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c b/tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c index 17220a589b..355c1ebf00 100644 --- a/tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c +++ b/tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c @@ -1,4 +1,4 @@ -// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { From b7c6ec5493b90eb400522f8663efad91b25e9ef4 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Thu, 29 Jun 2023 17:23:34 +0200 Subject: [PATCH 0239/1312] Restore apronAnalysis.apron.ml --- src/analyses/apron/apronAnalysis.apron.ml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/analyses/apron/apronAnalysis.apron.ml b/src/analyses/apron/apronAnalysis.apron.ml index bec0c4ec57..29e295a662 100644 --- a/src/analyses/apron/apronAnalysis.apron.ml +++ b/src/analyses/apron/apronAnalysis.apron.ml @@ -4,7 +4,6 @@ open Analyses include RelationAnalysis - let spec_module: (module MCPSpec) Lazy.t = lazy ( let module Man = (val ApronDomain.get_manager ()) in @@ -35,8 +34,7 @@ let get_spec (): (module MCPSpec) = let after_config () = let module Spec = (val get_spec ()) in MCP.register_analysis (module Spec : MCPSpec); - GobConfig.set_string "ana.path_sens[+]" (Spec.name ()) - + GobConfig.set_string "ana.path_sens[+]" (Spec.name ()) let _ = AfterConfig.register after_config From 432e92d7e1e542b63e38d52b84942f2a3470f7f8 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Thu, 29 Jun 2023 17:39:48 +0200 Subject: [PATCH 0240/1312] Explicitly use () as the abstract local state --- src/analyses/loop_termination.ml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/analyses/loop_termination.ml b/src/analyses/loop_termination.ml index 7ecb11a12c..6aed200192 100644 --- a/src/analyses/loop_termination.ml +++ b/src/analyses/loop_termination.ml @@ -69,7 +69,7 @@ struct module V = UnitV module G = MapDomain.MapBot (Statements) (BoolDomain.MustBool) - let startstate _ = D.bot () + let startstate _ = () let exitstate = startstate let assign ctx (lval : lval) (rval : exp) = @@ -81,12 +81,12 @@ struct let is_bounded = check_bounded ctx x in let loop_statement = VarToStmt.find x !loop_counters in let () = ctx.sideg () (G.add (`Lifted loop_statement) is_bounded (ctx.global ())) in - ctx.local - | _ -> ctx.local + () + | _ -> () let special ctx (lval : lval option) (f : varinfo) (arglist : exp list) = (* TODO: Implement check for our special loop exit indicator function *) - ctx.local + () (** Checks whether a new thread was spawned some time. We want to discard * any knowledge about termination then (see query function) *) From 29532421f232de013f95da615820af7e4d944a44 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Thu, 29 Jun 2023 17:44:30 +0200 Subject: [PATCH 0241/1312] Change let-in clause to sequential statements --- src/analyses/loop_termination.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/loop_termination.ml b/src/analyses/loop_termination.ml index 6aed200192..1d6a7db262 100644 --- a/src/analyses/loop_termination.ml +++ b/src/analyses/loop_termination.ml @@ -80,7 +80,7 @@ struct (* TODO: Move to special *) let is_bounded = check_bounded ctx x in let loop_statement = VarToStmt.find x !loop_counters in - let () = ctx.sideg () (G.add (`Lifted loop_statement) is_bounded (ctx.global ())) in + ctx.sideg () (G.add (`Lifted loop_statement) is_bounded (ctx.global ())); () | _ -> () From c2c69cf1bc3e8e6f8a9978d9769786a0f44669b1 Mon Sep 17 00:00:00 2001 From: serenita <33780257+serenita@users.noreply.github.com> Date: Thu, 29 Jun 2023 18:17:01 +0200 Subject: [PATCH 0242/1312] Update src/framework/constraints.ml Make comment more precise Co-authored-by: Michael Schwarz --- src/framework/constraints.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index ce3ecadc1e..ccee950a7d 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1695,7 +1695,7 @@ struct end -(** Add cycle detection in the function call graph to a analysis *) +(** Add cycle detection in the context-sensitive dynamic function call graph to an analysis *) module RecursionTermLifter (S: Spec) : Spec with module D = S.D and module C = S.C From d18d8fe8a32a613ac7a0b4dc27c9d6747c2b9295 Mon Sep 17 00:00:00 2001 From: serenita <33780257+serenita@users.noreply.github.com> Date: Thu, 29 Jun 2023 18:18:45 +0200 Subject: [PATCH 0243/1312] Update src/framework/constraints.ml Co-authored-by: Michael Schwarz --- src/framework/constraints.ml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index ccee950a7d..bd65dd5ba4 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1709,10 +1709,7 @@ module RecursionTermLifter (S: Spec) struct include S - module V = - struct - include GVarF(S.V) - end + module V = GVarF(S.V) module G = GVarGSet (S.G) (S.C) (T (CilType.Fundec) (S.C)) From 0d77172c0933d6044c83cee065737200730277ac Mon Sep 17 00:00:00 2001 From: serenita <33780257+serenita@users.noreply.github.com> Date: Thu, 29 Jun 2023 18:22:29 +0200 Subject: [PATCH 0244/1312] Remove unnecessary semicola Co-authored-by: Michael Schwarz --- src/util/terminationPreprocessing.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index 6e29c48917..47a17575c4 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -1,7 +1,7 @@ open GoblintCil include Printf -module VarToStmt = Map.Make(CilType.Varinfo);; (* maps varinfos (= loop counter variable) to the statement of the corresponding loop*) +module VarToStmt = Map.Make(CilType.Varinfo) (* maps varinfos (= loop counter variable) to the statement of the corresponding loop*) let extract_file_name s = (*There still may be a need to filter more chars*) let ls = String.split_on_char '/' s in (*Assuming '/' as path seperator*) From 76230f473ef68c940f117d221393cfbf515a7505 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Thu, 29 Jun 2023 18:31:30 +0200 Subject: [PATCH 0245/1312] Restore 01-simple-cases.c from white space change --- tests/regression/55-loop-unrolling/01-simple-cases.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression/55-loop-unrolling/01-simple-cases.c b/tests/regression/55-loop-unrolling/01-simple-cases.c index add8c6a8dd..0073717187 100644 --- a/tests/regression/55-loop-unrolling/01-simple-cases.c +++ b/tests/regression/55-loop-unrolling/01-simple-cases.c @@ -193,4 +193,4 @@ void example10(void) ++i; } return 0; -} \ No newline at end of file +} From 472ece8771bb366cf589680e7c65419ec2081fbf Mon Sep 17 00:00:00 2001 From: Nathan Schmidt Date: Thu, 29 Jun 2023 21:03:42 +0200 Subject: [PATCH 0246/1312] Feature: better treatment of edge cases --- src/analyses/base.ml | 30 ++- src/cdomains/arrayDomain.ml | 202 +++++++++++++----- src/cdomains/arrayDomain.mli | 18 +- src/cdomains/valueDomain.ml | 2 +- src/util/options.schema.json | 6 + .../regression/73-strings/03-string_basics.c | 4 +- tests/regression/73-strings/04-char_arrays.c | 9 +- 7 files changed, 189 insertions(+), 82 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index abd266f08d..dbe6438fca 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2126,12 +2126,13 @@ struct (* if s string literal, compute strlen in string literals domain *) if AD.type_of address = charPtrType then Int(AD.to_string_length address) - (* else compute strlen in array domain; TODO: is there any more elegant way than this? The following didn't work :( *) - (* let eval_dst = eval_rv (Analyses.ask_of_ctx ctx) gs st s1 in - let eval_src = eval_rv (Analyses.ask_of_ctx ctx) gs st s2 in - match eval_dst, eval_src with - | Array array_dst, Array array_src -> ... *) + (* else compute strlen in array domain *) else + (* (* TODO: why isn't the following working? *) + begin match get (Analyses.ask_of_ctx ctx) gs st address None with + | Array array_s -> Int(CArrays.to_string_length array_s) + | _ -> VD.top_value (unrollType dest_typ) + end) in *) begin match lval with | (Var v, _) -> begin match CPA.find_opt v st.cpa with @@ -2145,22 +2146,17 @@ struct end | Strstr { haystack; needle }, _ -> begin match lv with - | Some _ -> + | Some lv_val -> (* when haystack, needle and dest type coincide, check if needle is a substring of haystack: if that is the case, assign the substring of haystack starting at the first occurrence of needle to dest, else use top *) - let dest_a, dest_typ, value, var = string_manipulation haystack needle lv true (Some (fun h_a n_a -> Address(AD.substring_extraction h_a n_a))) + let dest_a, dest_typ, value, _ = string_manipulation haystack needle lv true (Some (fun h_a n_a -> Address(AD.substring_extraction h_a n_a))) (fun h_ar n_ar -> match CArrays.substring_extraction h_ar n_ar with - | Some ar -> Array(ar) - | None -> Address(AD.null_ptr)) in - begin match var with - | Some v -> - begin match value with - | Address _ -> set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value - | _ -> {st with cpa = CPA.add v value st.cpa} - end - | None -> set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value - end + | true, false -> Address(AD.null_ptr) + | false, true -> Address(eval_lv (Analyses.ask_of_ctx ctx) gs st (mkMem ~addr:(Cil.stripCasts haystack) ~off:NoOffset)) + (* TODO: below, instead of ~off:NoOffset, how to have a top offset = don't know exactly at which index pointing? *) + | _ -> Address(AD.join (eval_lv (Analyses.ask_of_ctx ctx) gs st (mkMem ~addr:(Cil.stripCasts haystack) ~off:NoOffset)) (AD.null_ptr))) in + set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value | None -> st end | Strcmp { s1; s2; n }, _ -> diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 2661bb7767..f10988fda9 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -79,10 +79,11 @@ sig val get: ?checkBounds:bool -> VDQ.t -> t -> Basetype.CilExp.t option * idx -> ret + val to_null_byte_domain: string -> t val to_string_length: t -> idx val string_copy: t -> t -> int option -> t val string_concat: t -> t -> int option -> t - val substring_extraction: t -> t -> t option + val substring_extraction: t -> t -> bool * bool val string_comparison: t -> t -> int option -> idx end @@ -1270,6 +1271,18 @@ struct (* string functions *) + let to_null_byte_domain s = + let last_null = Z.of_int (String.length s) in + let rec build_set i set = + if Z.geq (Z.of_int i) last_null then + MayNulls.add last_null set + else + match String.index_from_opt s i '\x00' with + | Some i -> build_set (i + 1) (MayNulls.add (Z.of_int i) set) + | None -> MayNulls.add last_null set in + let set = build_set 0 (MayNulls.empty ()) in + (set, set, Idx.of_int ILong (Z.succ last_null)) + (** Returns an abstract value with at most one null byte marking the end of the string *) let to_string (must_nulls_set, may_nulls_set, size) = (* if must_nulls_set and min_nulls_set empty, definitely no null byte in array => warn about certain buffer overflow and return tuple unchanged *) @@ -1386,9 +1399,9 @@ struct else Idx.of_interval !Cil.kindOfSizeOf (may_nulls_min_elt may_nulls_set, must_nulls_min_elt must_nulls_set) - let string_copy (must_nulls_set1, may_nulls_set1, size1) ar2 n = + let string_copy (must_nulls_set1, may_nulls_set1, size1) (must_nulls_set2, may_nulls_set2, size2) n = (* filter out indexes before strlen(src) from dest sets and after strlen(src) from src sets and build union, keep size of dest *) - let update_sets must_nulls_set2 may_nulls_set2 size2 len2 = + let update_sets must_nulls_set2' may_nulls_set2' size2' len2 = match Idx.minimal size1, idx_maximal size1, Idx.minimal len2, idx_maximal len2 with | Some min_size1, Some max_size1, Some min_len2, Some max_len2 -> (if Z.lt max_size1 min_len2 then @@ -1396,19 +1409,19 @@ struct else if Z.lt min_size1 max_len2 then M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src may be greater than the allocated size for dest"); let must_nulls_set_result = - let min_size2 = match Idx.minimal size2 with + let min_size2 = match Idx.minimal size2' with | Some min_size2 -> min_size2 | None -> Z.zero in (* get must nulls from src string < minimal size of dest *) - must_nulls_filter (Z.gt min_size1) must_nulls_set2 min_size2 + must_nulls_filter (Z.gt min_size1) must_nulls_set2' min_size2 (* and keep indexes of dest >= maximal strlen of src *) |> MustNulls.union (must_nulls_filter (Z.leq max_len2) must_nulls_set1 min_size1) in let may_nulls_set_result = - let max_size2 = match idx_maximal size2 with + let max_size2 = match idx_maximal size2' with | Some max_size2 -> max_size2 | None -> max_size1 in (* get may nulls from src string < maximal size of dest *) - may_nulls_filter (Z.gt max_size1) may_nulls_set2 max_size2 + may_nulls_filter (Z.gt max_size1) may_nulls_set2' max_size2 (* and keep indexes of dest >= minimal strlen of src *) |> MayNulls.union (may_nulls_filter (Z.leq min_len2) may_nulls_set1 max_size1) in (must_nulls_set_result, may_nulls_set_result, size1) @@ -1416,14 +1429,14 @@ struct (if Z.lt min_size1 max_len2 then M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src may be greater than the allocated size for dest"); let must_nulls_set_result = - let min_size2 = match Idx.minimal size2 with + let min_size2 = match Idx.minimal size2' with | Some min_size2 -> min_size2 | None -> Z.zero in - must_nulls_filter (Z.gt min_size1) must_nulls_set2 min_size2 + must_nulls_filter (Z.gt min_size1) must_nulls_set2' min_size2 |> MustNulls.union (must_nulls_filter (Z.leq max_len2) must_nulls_set1 min_size1) in let may_nulls_set_result = (* get all may nulls from src string as no maximal size of dest *) - may_nulls_set2 + may_nulls_set2' |> MayNulls.union (may_nulls_filter (Z.leq min_len2) may_nulls_set1 (Z.succ min_len2)) in (must_nulls_set_result, may_nulls_set_result, size1) | Some min_size1, Some max_size1, Some min_len2, None -> @@ -1433,15 +1446,15 @@ struct M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src may be greater than the allocated size for dest"); (* do not keep any index of dest as no maximal strlen of src *) let must_nulls_set_result = - let min_size2 = match Idx.minimal size2 with + let min_size2 = match Idx.minimal size2' with | Some min_size2 -> min_size2 | None -> Z.zero in - must_nulls_filter (Z.gt min_size1) must_nulls_set2 min_size2 in + must_nulls_filter (Z.gt min_size1) must_nulls_set2' min_size2 in let may_nulls_set_result = - let max_size2 = match idx_maximal size2 with + let max_size2 = match idx_maximal size2' with | Some max_size2 -> max_size2 | None -> max_size1 in - may_nulls_filter (Z.gt max_size1) may_nulls_set2 max_size2 + may_nulls_filter (Z.gt max_size1) may_nulls_set2' max_size2 |> MayNulls.union (may_nulls_filter (Z.leq min_len2) may_nulls_set1 max_size1) in (must_nulls_set_result, may_nulls_set_result, size1) | Some min_size1, None, Some min_len2, None -> @@ -1449,29 +1462,54 @@ struct M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src may be greater than the allocated size for dest"); (* do not keep any index of dest as no maximal strlen of src *) let must_nulls_set_result = - let min_size2 = match Idx.minimal size2 with + let min_size2 = match Idx.minimal size2' with | Some min_size2 -> min_size2 | None -> Z.zero in - must_nulls_filter (Z.gt min_size1) must_nulls_set2 min_size2 in + must_nulls_filter (Z.gt min_size1) must_nulls_set2' min_size2 in let may_nulls_set_result = (* get all may nulls from src string as no maximal size of dest *) - may_nulls_set2 + may_nulls_set2' |> MayNulls.union (may_nulls_filter (Z.leq min_len2) may_nulls_set1 (Z.succ min_len2)) in (must_nulls_set_result, may_nulls_set_result, size1) (* any other case shouldn't happen as minimal index is always >= 0 *) | _ -> (MustNulls.top (), MayNulls.top (), size1) in - - (* TODO: would it be useful to warn if size of ar2 is (potentially bigger) than size of ar1? *) + + (* warn if size of dest is (potentially) smaller than size of src and the latter (potentially) has no null byte at index < size of dest *) + let sizes_warning size2 = + (match Idx.minimal size1, idx_maximal size1, Idx.minimal size2, idx_maximal size2 with + | Some min_size1, _, Some min_size2, _ when Z.lt min_size1 min_size2 -> + if not (MayNulls.exists (Z.gt min_size1) may_nulls_set2) then + M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "src doesn't contain a null byte at an index smaller than the size of dest" + else if not (MustNulls.exists (Z.gt min_size1) must_nulls_set2) then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "src may not contain a null byte at an index smaller than the size of dest" + | Some min_size1, _, _, Some max_size2 when Z.lt min_size1 max_size2 -> + if not (MayNulls.exists (Z.gt min_size1) may_nulls_set2) then + M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "src doesn't contain a null byte at an index smaller than the size of dest" + else if not (MustNulls.exists (Z.gt min_size1) must_nulls_set2) then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "src may not contain a null byte at an index smaller than the size of dest" + | Some min_size1, _, _, None -> + if not (MustNulls.exists (Z.gt min_size1) must_nulls_set2) then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "src may not contain a null byte at an index smaller than the size of dest" + | _, Some max_size1, _, Some max_size2 when Z.lt max_size1 max_size2 -> + if not (MustNulls.exists (Z.gt max_size1) must_nulls_set2) then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "src may not contain a null byte at an index smaller than the size of dest" + |_, Some max_size1, _, None -> + if not (MustNulls.exists (Z.gt max_size1) must_nulls_set2) then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "src may not contain a null byte at an index smaller than the size of dest" + | _ -> ()) in + match n with (* strcpy *) | None -> - let must_nulls_set2, may_nulls_set2, size2 = to_string ar2 in - let strlen2 = to_string_length ar2 in - update_sets must_nulls_set2 may_nulls_set2 size2 strlen2 + sizes_warning size2; + let must_nulls_set2', may_nulls_set2', size2' = to_string (must_nulls_set2, may_nulls_set2, size2) in + let strlen2 = to_string_length (must_nulls_set2, may_nulls_set2, size2) in + update_sets must_nulls_set2' may_nulls_set2' size2' strlen2 (* strncpy = exactly n bytes from src are copied to dest *) | Some n when n >= 0 -> - let must_nulls_set2, may_nulls_set2, size2 = to_n_string ar2 n in - update_sets must_nulls_set2 may_nulls_set2 size2 (Idx.of_int !Cil.kindOfSizeOf (Z.of_int n)) + sizes_warning (Idx.of_int ILong (Z.of_int n)); + let must_nulls_set2', may_nulls_set2', size2' = to_n_string (must_nulls_set2, may_nulls_set2, size2) n in + update_sets must_nulls_set2' may_nulls_set2' size2' (Idx.of_int !Cil.kindOfSizeOf (Z.of_int n)) | _ -> (MustNulls.top (), MayNulls.top (), size1) let string_concat (must_nulls_set1, may_nulls_set1, size1) (must_nulls_set2, may_nulls_set2, size2) n = @@ -1606,9 +1644,9 @@ struct | _ -> (MustNulls.top (), MayNulls.top (), size1) let substring_extraction haystack (must_nulls_set_needle, may_nulls_set_needle, size_needle) = - (* if needle is empty string, i.e. certain null byte at index 0, return haystack as string *) + (* if needle is empty string, i.e. certain null byte at index 0, return value of strstr is pointer to haystack *) if MustNulls.mem Z.zero must_nulls_set_needle then - Some (to_string haystack) + false, true else let haystack_len = to_string_length haystack in let needle_len = to_string_length (must_nulls_set_needle, may_nulls_set_needle, size_needle) in @@ -1616,10 +1654,10 @@ struct | Some haystack_max, Some needle_min -> (* if strlen(haystack) < strlen(needle), needle can never be substring of haystack => return None *) if Z.lt haystack_max needle_min then - None + true, false else - Some (MustNulls.top (), MayNulls.top (), Idx.top_of ILong) - | _ -> Some (MustNulls.top (), MayNulls.top (), Idx.top_of ILong) + false, false + | _ -> false, false let string_comparison (must_nulls_set1, may_nulls_set1, size1) (must_nulls_set2, may_nulls_set2, size2) n = let compare n n_exists = @@ -1836,34 +1874,96 @@ struct let get ?(checkBounds=true) (ask: VDQ.t) (t_f, t_n) i = let f_get = F.get ask t_f i in - let n_get = N.get ask t_n i in - match Val.is_int_ikind f_get, n_get with - | Some ik, Null -> Val.meet f_get (Val.zero_of_ikind ik) - | Some ik, NotNull -> Val.meet f_get (Val.not_zero_of_ikind ik) - | _ -> f_get - let set (ask:VDQ.t) (t_f, t_n) i v = (F.set ask t_f i v, N.set ask t_n i v) - let make ?(varAttr=[]) ?(typAttr=[]) i v = (F.make i v, N.make i v) - let length (_, t_n) = N.length t_n + if get_bool "ana.base.arrays.nullbytes" then + let n_get = N.get ask t_n i in + match Val.is_int_ikind f_get, n_get with + | Some ik, Null -> Val.meet f_get (Val.zero_of_ikind ik) + | Some ik, NotNull -> Val.meet f_get (Val.not_zero_of_ikind ik) + | _ -> f_get + else + f_get + let set (ask:VDQ.t) (t_f, t_n) i v = + if get_bool "ana.base.arrays.nullbytes" then + (F.set ask t_f i v, N.set ask t_n i v) + else + (F.set ask t_f i v, N.top ()) + let make ?(varAttr=[]) ?(typAttr=[]) i v = + if get_bool "ana.base.arrays.nullbytes" then + (F.make i v, N.make i v) + else + (F.make i v, N.top ()) + let length (t_f, t_n) = + if get_bool "ana.base.arrays.nullbytes" then + N.length t_n + else + F.length t_f let move_if_affected ?(replace_with_const=false) (ask:VDQ.t) (t_f, t_n) v f = (F.move_if_affected ask t_f v f, N.move_if_affected ask t_n v f) let get_vars_in_e (t_f, _) = F.get_vars_in_e t_f - let map f (t_f, t_n) = (F.map f t_f, N.map f t_n) - let fold_left f acc (t_f, t_n) = F.fold_left f acc t_f + let map f (t_f, t_n) = + if get_bool "ana.base.arrays.nullbytes" then + (F.map f t_f, N.map f t_n) + else + (F.map f t_f, N.top ()) + let fold_left f acc (t_f, _) = F.fold_left f acc t_f - let content_to_top (t_f, t_n) = (F.content_to_top t_f, N.content_to_top t_n) + let content_to_top (t_f, t_n) = + if get_bool "ana.base.arrays.nullbytes" then + (F.content_to_top t_f, N.content_to_top t_n) + else + (F.content_to_top t_f, N.top ()) - let smart_join x y (t_f1, t_n1) (t_f2, t_n2) = (F.smart_join x y t_f1 t_f2, N.smart_join x y t_n1 t_n2) - let smart_widen x y (t_f1, t_n1) (t_f2, t_n2) = (F.smart_widen x y t_f1 t_f2, N.smart_widen x y t_n1 t_n2) - let smart_leq x y (t_f1, t_n1) (t_f2, t_n2) = F.smart_leq x y t_f1 t_f2 && N.smart_leq x y t_n1 t_n2 + let smart_join x y (t_f1, t_n1) (t_f2, t_n2) = + if get_bool "ana.base.arrays.nullbytes" then + (F.smart_join x y t_f1 t_f2, N.smart_join x y t_n1 t_n2) + else + (F.smart_join x y t_f1 t_f2, N.top ()) + let smart_widen x y (t_f1, t_n1) (t_f2, t_n2) = + if get_bool "ana.base.arrays.nullbytes" then + (F.smart_widen x y t_f1 t_f2, N.smart_widen x y t_n1 t_n2) + else + (F.smart_widen x y t_f1 t_f2, N.top ()) + let smart_leq x y (t_f1, t_n1) (t_f2, t_n2) = + if get_bool "ana.base.arrays.nullbytes" then + F.smart_leq x y t_f1 t_f2 && N.smart_leq x y t_n1 t_n2 + else + F.smart_leq x y t_f1 t_f2 - let to_string_length (_, t_n) = N.to_string_length t_n - let string_copy (t_f1, t_n1) (_, t_n2) n = (F.content_to_top t_f1, N.string_copy t_n1 t_n2 n) - let string_concat (t_f1, t_n1) (_, t_n2) n = (F.content_to_top t_f1, N.string_concat t_n1 t_n2 n) - let substring_extraction (t_f1, t_n1) (_, t_n2) = match N.substring_extraction t_n1 t_n2 with - | Some res -> Some (F.content_to_top t_f1, res) - | None -> None - let string_comparison (_, t_n1) (_, t_n2) n = N.string_comparison t_n1 t_n2 n + let to_null_byte_domain s = + if get_bool "ana.base.arrays.nullbytes" then + (F.top (), N.to_null_byte_domain s) + else + (F.top (), N.top ()) + let to_string_length (_, t_n) = + if get_bool "ana.base.arrays.nullbytes" then + N.to_string_length t_n + else + Idx.top_of !Cil.kindOfSizeOf + let string_copy (t_f1, t_n1) (_, t_n2) n = + if get_bool "ana.base.arrays.nullbytes" then + (F.content_to_top t_f1, N.string_copy t_n1 t_n2 n) + else + (F.content_to_top t_f1, N.top ()) + let string_concat (t_f1, t_n1) (_, t_n2) n = + if get_bool "ana.base.arrays.nullbytes" then + (F.content_to_top t_f1, N.string_concat t_n1 t_n2 n) + else + (F.content_to_top t_f1, N.top ()) + let substring_extraction (_, t_n1) (_, t_n2) = + if get_bool "ana.base.arrays.nullbytes" then + N.substring_extraction t_n1 t_n2 + else + false, false + let string_comparison (_, t_n1) (_, t_n2) n = + if get_bool "ana.base.arrays.nullbytes" then + N.string_comparison t_n1 t_n2 n + else + Idx.top_of IInt - let update_length newl (t_f, t_n) = (F.update_length newl t_f, N.update_length newl t_n) + let update_length newl (t_f, t_n) = + if get_bool "ana.base.arrays.nullbytes" then + (F.update_length newl t_f, N.update_length newl t_n) + else + (F.update_length newl t_f, N.top ()) let project ?(varAttr=[]) ?(typAttr=[]) ask (t_f, t_n) = (F.project ask t_f, N.project ask t_n) let invariant ~value_invariant ~offset ~lval (t_f, _) = F.invariant ~value_invariant ~offset ~lval t_f end diff --git a/src/cdomains/arrayDomain.mli b/src/cdomains/arrayDomain.mli index dc1b381340..894fa9192e 100644 --- a/src/cdomains/arrayDomain.mli +++ b/src/cdomains/arrayDomain.mli @@ -79,6 +79,9 @@ sig val get: ?checkBounds:bool -> VDQ.t -> t -> Basetype.CilExp.t option * idx -> ret (* overwrites get of module S *) + val to_null_byte_domain: string -> t + (* Converts a string to its abstract value in the NullByte domain *) + val to_string_length: t -> idx (** Returns length of string represented by input abstract value *) @@ -91,10 +94,11 @@ sig * concatenation of the input abstract values [s1] and [s2], taking at most [n] bytes of * [s2] if present *) - val substring_extraction: t -> t -> t option - (** [substring_extraction haystack needle] returns None if the string represented by the - * abstract value [needle] surely isn't a substring of [haystack], Some [to_string haystack] - * if [needle] is empty the empty string, else Some top *) + val substring_extraction: t -> t -> bool * bool + (** [substring_extraction haystack needle] returns [is_null_ptr, is_offset_0], i.e. + * [true, false] if the string represented by the abstract value [needle] surely isn't a + * substring of [haystack], [false, true] if [needle] is the empty string, + * else [false, false] *) val string_comparison: t -> t -> int option -> idx (** [string_comparison s1 s2 n] returns a negative / positive idx element if the string @@ -151,7 +155,7 @@ module Partitioned (Val: LatticeWithSmartOps) (Idx: IntDomain.Z): S with type va module PartitionedWithLength (Val: LatticeWithSmartOps) (Idx:IntDomain.Z): S with type value = Val.t and type idx = Idx.t (** Like partitioned but additionally manages the length of the array. *) -module NullByte (Val: LatticeWithNull) (Idx: IntDomain.Z): SMinusDomainAndRet with type value = Val.t and type idx = Idx.t +module NullByte (Val: LatticeWithNull) (Idx: IntDomain.Z): Str with type value = Val.t and type idx = Idx.t (** This functor creates an array representation by the indexes of all null bytes * the array must and may contain. This is useful to analyze strings, i.e. null- * terminated char arrays, and particularly to determine if operations on strings @@ -163,4 +167,6 @@ module FlagHelperAttributeConfiguredArrayDomain (Val: LatticeWithSmartOps) (Idx: (** Switches between PartitionedWithLength, TrivialWithLength and Unroll based on variable, type, and flag. *) module AttributeConfiguredArrayDomain (Val: LatticeWithNull) (Idx: IntDomain.Z): StrWithDomain with type value = Val.t and type idx = Idx.t -(** Like FlagHelperAttributeConfiguredArrayDomain but additionally runs NullByte in parallel. *) +(** Like FlagHelperAttributeConfiguredArrayDomain but additionally runs NullByte + * in parallel if flag "ana.base.arrays.nullbytes" is set. +*) diff --git a/src/cdomains/valueDomain.ml b/src/cdomains/valueDomain.ml index 2ae980369e..6fa3b21731 100644 --- a/src/cdomains/valueDomain.ml +++ b/src/cdomains/valueDomain.ml @@ -256,7 +256,7 @@ struct | _ -> Top let tag_name : t -> string = function - | Top -> "Top" | Int _ -> "Int" | Float _ -> "Float" | Address _ -> "Address" | Struct _ -> "Struct" | Union _ -> "Union" | Array _ -> "Array" | Blob _ -> "Blob" | Thread _ -> "Thread" | Mutex -> "Mutex" | MutexAttr _ -> "MutexAttr" | JmpBuf _ -> "JmpBuf" | Bot -> "Bot" + | Top -> "Top" | Int _ -> "Int" | Float _ -> "Float" | Address _ -> "Address" | Struct _ -> "Struct" | Union _ -> "Union" | Array _ -> "Array" | Blob _ -> "Blob" | Thread _ -> "Thread" | Mutex -> "Mutex" | MutexAttr _ -> "MutexAttr" | JmpBuf _ -> "JmpBuf" | Bot -> "Bot" include Printable.Std let name () = "compound" diff --git a/src/util/options.schema.json b/src/util/options.schema.json index 02fc929a8a..471ce8c31d 100644 --- a/src/util/options.schema.json +++ b/src/util/options.schema.json @@ -685,6 +685,12 @@ "description": "Indicates how many values will the unrolled part of the unrolled array domain contain.", "type": "integer", "default": 0 + }, + "nullbytes": { + "title": "ana.base.arrays.nullbytes", + "description": "Whether the Null Byte array domain should be activated.", + "type": "boolean", + "default": false } }, "additionalProperties": false diff --git a/tests/regression/73-strings/03-string_basics.c b/tests/regression/73-strings/03-string_basics.c index 1cfa33a689..180d9a00bc 100644 --- a/tests/regression/73-strings/03-string_basics.c +++ b/tests/regression/73-strings/03-string_basics.c @@ -1,4 +1,4 @@ -// PARAM: --disable ana.base.limit-string-addresses --enable ana.int.interval +// PARAM: --disable ana.base.limit-string-addresses --enable ana.int.interval --enable ana.base.arrays.nullbytes #include #include @@ -55,7 +55,7 @@ int main() { char* cmp = strstr(s1, "bab"); __goblint_check(cmp != NULL); // UNKNOWN - i = strcmp(cmp, "babcd"); // WARN: no check if cmp != NULL (even if it obviously is != NULL) + i = strcmp(cmp, "babcd"); // NOWARN: cmp != NULL __goblint_check(i == 0); // UNKNOWN i = strncmp(s4, s3, 4); diff --git a/tests/regression/73-strings/04-char_arrays.c b/tests/regression/73-strings/04-char_arrays.c index 20e8cababb..2d1b1bb07f 100644 --- a/tests/regression/73-strings/04-char_arrays.c +++ b/tests/regression/73-strings/04-char_arrays.c @@ -1,4 +1,4 @@ -// PARAM: --disable ana.base.limit-string-addresses --enable ana.int.interval +// PARAM: --disable ana.base.limit-string-addresses --enable ana.int.interval --enable ana.base.arrays.nullbytes #include #include @@ -161,10 +161,9 @@ void example8() { char s2[] = "test"; // must and may null at 4 char cmp[50]; - strcpy(cmp, strstr(s1, empty)); // WARN - size_t len = strlen(cmp); // WARN - __goblint_check(len == 11); // UNKNOWN because can't directly assign result of strstr to cmp, - // TODO: might make handling of this useless in NullByte domain? + strcpy(cmp, strstr(s1, empty)); // NOWARN: strstr(s1, empty) != NULL + size_t len = strlen(cmp); + __goblint_check(len == 11); // TODO: shouldn't this be known? char* cmp_ptr = strstr(s2, s1); __goblint_check(cmp_ptr == NULL); From 6e620417fd57bbdd6b7e9c05c6e7ad6b69bab4de Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Fri, 30 Jun 2023 11:05:32 +0200 Subject: [PATCH 0247/1312] adapted changes from pull request: 1. using a module for tuple; 2. deleted dublicated method; 3. removed the arg from the arg_termination variable --- src/framework/analyses.ml | 39 ------------------------------------ src/framework/constraints.ml | 12 +++++------ src/framework/control.ml | 4 ++-- 3 files changed, 8 insertions(+), 47 deletions(-) diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 1c3d596cc2..712399d619 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -118,38 +118,6 @@ struct | x -> BatPrintf.fprintf f "%a" printXml x end -(* Tuple of fundec and S.C*) -module T (Base1: Printable.S) (Base2: Printable.S) = -struct - include Printable.Std - type t = (Base1.t * Base2.t) - - let fundec (a,_) = a - let context (_,b) = b - let equal (a1, b1) (a2, b2) = if (Base1.equal a1 a2 && Base2.equal b1 b2) then true else false - let show (a,b) = (Base1.show a) ^ (Base2.show b) - let name () = "Tuple" - let to_yojson x = `String (show x) - let relift (a,b) = (a,b) (*Todo: is this correct?*) - let printXml f (a,b) = - BatPrintf.fprintf f "\n - Tuple:\n\n - caller_fundec\n%a\n\n - caller_context\n%a\n\n - \n" Base1.printXml a Base2.printXml b - - let compare (a1,b1) (a2,b2) = (*Todo: is this ok?*) - if equal (a1, b1) (a2, b2) then 0 - else( - let val_a a = if (a > 0) then 1 else -1 in - let val_b b = if (b > 0) then 3 else -3 in - val_a (Base1.compare a1 a2) + val_b (Base2.compare b1 b2) - ) - - let pretty () x = text (show x) - let hash (a,b) = Hashtbl.hash (Base1.hash a * Base2.hash b) (*Todo: is this ok?*) -end - module GVarGSet (G: Lattice.S) (C: Printable.S) (Base: Printable.S) = struct module CSet = @@ -200,13 +168,6 @@ struct | `Lifted2 x -> BatPrintf.fprintf f "%a" CMap.printXml x | x -> BatPrintf.fprintf f "%a" printXml x - let s = function - | `Bot -> G.bot () - | `Lifted1 x -> x - | _ -> failwith "RecursionTerm.s" - - let create_s s = `Lifted1 s - let base2 instance = match instance with | `Lifted2 n -> Some n diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index bd65dd5ba4..c60f84d5e8 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1711,24 +1711,24 @@ struct include S module V = GVarF(S.V) - module G = GVarGSet (S.G) (S.C) (T (CilType.Fundec) (S.C)) + module G = GVarGSet (S.G) (S.C) (Printable.Prod (CilType.Fundec) (S.C)) let name () = "termination" let conv (ctx: (_, G.t, _, V.t) ctx): (_, S.G.t, _, S.V.t) ctx = { ctx with - global = (fun v -> G.s (ctx.global (V.spec v))); - sideg = (fun v g -> ctx.sideg (V.spec v) (G.create_s g)); + global = (fun v -> G.spec (ctx.global (V.spec v))); + sideg = (fun v g -> ctx.sideg (V.spec v) (G.create_spec g)); } let cycleDetection ctx v v' = - let module LH = Hashtbl.Make (T (CilType.Fundec) (S.C)) in - let module LS = Set.Make (T (CilType.Fundec) (S.C)) in + let module LH = Hashtbl.Make (Printable.Prod (CilType.Fundec) (S.C)) in + let module LS = Set.Make (Printable.Prod (CilType.Fundec) (S.C)) in (* find all cycles/SCCs *) let global_visited_calls = LH.create 100 in (* DFS *) - let rec iter_call (path_visited_calls: LS.t) (call:T (CilType.Fundec) (S.C).t) = + let rec iter_call (path_visited_calls: LS.t) (call:Printable.Prod (CilType.Fundec) (S.C).t) = let ((fundec_e:fundec), (context_e: C.t)) = call in (*unpack tuple for later use*) if LS.mem call path_visited_calls then ( AnalysisState.svcomp_may_not_terminate := true; diff --git a/src/framework/control.ml b/src/framework/control.ml index 9a717e2f67..e408aab76a 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -15,7 +15,7 @@ module type S2S = functor (X : Spec) -> Spec let spec_module: (module Spec) Lazy.t = lazy ( GobConfig.building_spec := true; let arg_enabled = get_bool "ana.sv-comp.enabled" || get_bool "exp.arg" in - let arg_termination = List.mem "termination" (get_string_list "ana.activated") in (* check if loop termination analysis is enabled*) + let termination = List.mem "termination" (get_string_list "ana.activated") in (* check if loop termination analysis is enabled*) let open Batteries in (* apply functor F on module X if opt is true *) let lift opt (module F : S2S) (module X : Spec) = (module (val if opt then (module F (X)) else (module X) : Spec) : Spec) in @@ -37,7 +37,7 @@ let spec_module: (module Spec) Lazy.t = lazy ( Also must be outside of deadcode, because deadcode splits (like mutex lock event) don't pass on tokens. *) |> lift (get_bool "ana.widen.tokens") (module WideningTokens.Lifter) |> lift true (module LongjmpLifter) - |> lift arg_termination (module RecursionTermLifter) (* Always activate the recursion termination analysis, when the loop termination analysis is activated*) + |> lift termination (module RecursionTermLifter) (* Always activate the recursion termination analysis, when the loop termination analysis is activated*) ) in GobConfig.building_spec := false; ControlSpecC.control_spec_c := (module S1.C); From 9c8d1283e4e5ad9a1fc13c318d8dcb44e11b3d44 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Fri, 30 Jun 2023 11:38:20 +0200 Subject: [PATCH 0248/1312] changed the order in C_Printable --- src/framework/analyses.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 712399d619..9f25a67a0d 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -133,8 +133,8 @@ struct (* Make the given module Goupable*) module C_Printable (C: Printable.S) = struct - include C include Printable.Std (* To make it Groupable *) + include C let printXml f c = BatPrintf.fprintf f "\n callee_context\n%a\n\n From c776d8cd82def2ba48f1402f179f7af90f092b94 Mon Sep 17 00:00:00 2001 From: Isidor Zweckstetter Date: Sat, 1 Jul 2023 11:30:47 +0200 Subject: [PATCH 0249/1312] changed naming for SV-Comp specification from NoTermination to Termination, to meet naming conventions --- src/autoTune.ml | 2 +- src/witness/svcomp.ml | 2 +- src/witness/svcompSpec.ml | 8 ++++---- src/witness/witness.ml | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/autoTune.ml b/src/autoTune.ml index 4fb8a1db5e..9468d1d366 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -216,7 +216,7 @@ let focusOnSpecification () = | NoDataRace -> (*enable all thread analyses*) print_endline @@ "Specification: NoDataRace -> enabling thread analyses \"" ^ (String.concat ", " notNeccessaryThreadAnalyses) ^ "\""; enableAnalyses notNeccessaryThreadAnalyses; - | NoTermination -> () + | Termination -> () | NoOverflow -> (*We focus on integer analysis*) set_bool "ana.int.def_exc" true; set_bool "ana.int.interval" true diff --git a/src/witness/svcomp.ml b/src/witness/svcomp.ml index a164448210..2fcb32fff9 100644 --- a/src/witness/svcomp.ml +++ b/src/witness/svcomp.ml @@ -52,7 +52,7 @@ struct | UnreachCall _ -> "unreach-call" | NoOverflow -> "no-overflow" | NoDataRace -> "no-data-race" (* not yet in SV-COMP/Benchexec *) - | NoTermination -> "no-termination" + | Termination -> "termination" in "false(" ^ result_spec ^ ")" | Unknown -> "unknown" diff --git a/src/witness/svcompSpec.ml b/src/witness/svcompSpec.ml index f8791d065e..946093bfc0 100644 --- a/src/witness/svcompSpec.ml +++ b/src/witness/svcompSpec.ml @@ -6,7 +6,7 @@ type t = | UnreachCall of string | NoDataRace | NoOverflow - | NoTermination + | Termination let of_string s = let s = String.strip s in @@ -17,8 +17,8 @@ let of_string s = NoDataRace else if global_not = "overflow" then NoOverflow - else if global_not = "termination" then - NoTermination + else if global_not = "no-termination" then + Termination else let call_regex = Str.regexp "call(\\(.*\\)())" in if Str.string_match call_regex global_not 0 then @@ -45,6 +45,6 @@ let to_string spec = | UnreachCall f -> "call(" ^ f ^ "())" | NoDataRace -> "data-race" | NoOverflow -> "overflow" - | NoTermination -> "termination" + | Termination -> "no-termination" in "CHECK( init(main()), LTL(G ! " ^ global_not ^ ") )" diff --git a/src/witness/witness.ml b/src/witness/witness.ml index b62b2b54cd..94ffea0a0a 100644 --- a/src/witness/witness.ml +++ b/src/witness/witness.ml @@ -442,7 +442,7 @@ struct in (module TaskResult:WitnessTaskResult) ) - | NoTermination -> (* TODO: implement this properly*) + | Termination -> (* TODO: implement this properly*) let module TrivialArg = struct include Arg From f3507455563fa0b9e23f25fe7dfae605d53c04ca Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 1 Jul 2023 18:33:43 +0200 Subject: [PATCH 0250/1312] Stick with Lattice.Unit --- src/analyses/memOutOfBounds.ml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index db4066d442..9648f35610 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -5,7 +5,7 @@ module Spec = struct include Analyses.IdentitySpec - module D = Lattice.Unit(*ValueDomain.AddrSetDomain*) + module D = Lattice.Unit module C = Lattice.Unit (* TODO: Do this later *) @@ -186,8 +186,8 @@ struct List.iter (fun arg -> check_exp_for_oob_access ctx arg) args; ctx.local - let startstate v = (*D.empty*) () - let exitstate v = (*D.empty*) () + let startstate v = () + let exitstate v = () end let _ = From b1da8e26a684bbe03c0e60fc41a0ade7b0cd6414 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Sun, 2 Jul 2023 10:49:46 +0200 Subject: [PATCH 0251/1312] renamed termination in control to termination_enabled; added comment for global invariant in RecursionTermLifter --- src/framework/constraints.ml | 12 ++++++++---- src/framework/control.ml | 4 ++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index c60f84d5e8..55763c5852 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1700,12 +1700,16 @@ module RecursionTermLifter (S: Spec) : Spec with module D = S.D and module C = S.C = -(*global invariants: +(* two global invariants: - V -> G + Needed to store the previously built global invariants - fundec -> Map (S.C) (Set (fundec * S.C)) - Therefore: - g -> {c' -> {(f, c)}} - in case f, c --> g, c' *) + The second global invariant maps from the callee fundec to a map, containing the callee context and the caller fundec and context. + This structure therefore stores the context-sensitive call graph. + For example: + let the function f in context c call function g in context c'. + In the global invariant structure it would be stored like this: g -> {c' -> {(f, c)}} +*) struct include S diff --git a/src/framework/control.ml b/src/framework/control.ml index e408aab76a..5bd815634c 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -15,7 +15,7 @@ module type S2S = functor (X : Spec) -> Spec let spec_module: (module Spec) Lazy.t = lazy ( GobConfig.building_spec := true; let arg_enabled = get_bool "ana.sv-comp.enabled" || get_bool "exp.arg" in - let termination = List.mem "termination" (get_string_list "ana.activated") in (* check if loop termination analysis is enabled*) + let termination_enabled = List.mem "termination" (get_string_list "ana.activated") in (* check if loop termination analysis is enabled*) let open Batteries in (* apply functor F on module X if opt is true *) let lift opt (module F : S2S) (module X : Spec) = (module (val if opt then (module F (X)) else (module X) : Spec) : Spec) in @@ -37,7 +37,7 @@ let spec_module: (module Spec) Lazy.t = lazy ( Also must be outside of deadcode, because deadcode splits (like mutex lock event) don't pass on tokens. *) |> lift (get_bool "ana.widen.tokens") (module WideningTokens.Lifter) |> lift true (module LongjmpLifter) - |> lift termination (module RecursionTermLifter) (* Always activate the recursion termination analysis, when the loop termination analysis is activated*) + |> lift termination_enabled (module RecursionTermLifter) (* Always activate the recursion termination analysis, when the loop termination analysis is activated*) ) in GobConfig.building_spec := false; ControlSpecC.control_spec_c := (module S1.C); From 1ace9d69f036db3878b5f449736775a41b9fba58 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Mon, 3 Jul 2023 12:24:33 +0200 Subject: [PATCH 0252/1312] Add description to loop/goto termination analysis --- src/analyses/loop_termination.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/loop_termination.ml b/src/analyses/loop_termination.ml index 1d6a7db262..f066fbbab6 100644 --- a/src/analyses/loop_termination.ml +++ b/src/analyses/loop_termination.ml @@ -1,4 +1,4 @@ -(** Work in progress *) +(** Termination analysis for loops and [goto] statements ([termination]). *) open Analyses open GoblintCil From 30b03ce2fe2892272470d38898bc6e4297dc9ac4 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Mon, 3 Jul 2023 12:32:51 +0200 Subject: [PATCH 0253/1312] Introduce isEverMultiThreaded analysis Does not work properly yet. Need to fix query function. --- src/analyses/everMultiThreaded.ml | 46 +++++++++++++++++++++++++++++++ src/domains/queries.ml | 5 ++++ 2 files changed, 51 insertions(+) create mode 100644 src/analyses/everMultiThreaded.ml diff --git a/src/analyses/everMultiThreaded.ml b/src/analyses/everMultiThreaded.ml new file mode 100644 index 0000000000..c98f173624 --- /dev/null +++ b/src/analyses/everMultiThreaded.ml @@ -0,0 +1,46 @@ +(** Work in progress *) + +open Analyses + +module UnitV = +struct + include Printable.Unit + include StdV +end + +module Spec : Analyses.MCPSpec = +struct + + (** Provides some default implementations *) + include Analyses.IdentitySpec + + let name () = "evermultithreaded" + + module D = Lattice.Unit + module C = D + module V = UnitV + module G = BoolDomain.MayBool + + let startstate _ = () + let exitstate = startstate + + (** Sets the global invariant to true when a thread is spawned *) + let threadspawn ctx lval f args fctx = + ctx.sideg () true; + () + + let query ctx (type a) (q: a Queries.t) : a Queries.result = + match q with + | Queries.IsEverMultiThreaded -> + (* + ctx.global () + *) + Queries.Result.top q (* TODO *) + | _ -> + Queries.Result.top q + +end + +let () = + (* Register this analysis within the master control program *) + MCP.register_analysis (module Spec : MCPSpec) diff --git a/src/domains/queries.ml b/src/domains/queries.ml index c5d7d729b6..2810f07342 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -119,6 +119,7 @@ type _ t = | MayBeModifiedSinceSetjmp: JmpBufDomain.BufferEntry.t -> VS.t t | MustTermLoop: stmt -> MustBool.t t | MustTermProg: MustBool.t t + | IsEverMultiThreaded: MayBool.t t type 'a result = 'a @@ -185,6 +186,7 @@ struct | MayBeModifiedSinceSetjmp _ -> (module VS) | MustTermLoop _ -> (module MustBool) | MustTermProg -> (module MustBool) + | IsEverMultiThreaded -> (module MayBool) (** Get bottom result for query. *) let bot (type a) (q: a t): a result = @@ -250,6 +252,7 @@ struct | MayBeModifiedSinceSetjmp _ -> VS.top () | MustTermLoop _ -> MustBool.top () | MustTermProg -> MustBool.top () + | IsEverMultiThreaded -> MayBool.top () end (* The type any_query can't be directly defined in Any as t, @@ -312,6 +315,7 @@ struct | Any ThreadsJoinedCleanly -> 52 | Any (MustTermLoop _) -> 53 | Any MustTermProg -> 54 + | Any IsEverMultiThreaded -> 55 let rec compare a b = let r = Stdlib.compare (order a) (order b) in @@ -456,6 +460,7 @@ struct | Any MayBeModifiedSinceSetjmp buf -> Pretty.dprintf "MayBeModifiedSinceSetjmp %a" JmpBufDomain.BufferEntry.pretty buf | Any (MustTermLoop s) -> Pretty.dprintf "MustTermLoop %a" CilType.Stmt.pretty s | Any MustTermProg -> Pretty.dprintf "MustTermProg" + | Any IsEverMultiThreaded -> Pretty.dprintf "IsEverMultiThreaded" end let to_value_domain_ask (ask: ask) = From e8aec04aeb4f550ca4822efc20878ec33674cb60 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Mon, 3 Jul 2023 12:59:34 +0200 Subject: [PATCH 0254/1312] Finish and use everMultiThreaded analysis Used by the termination analysis --- src/analyses/everMultiThreaded.ml | 9 +++++---- src/analyses/loop_termination.ml | 2 +- src/autoTune.ml | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/analyses/everMultiThreaded.ml b/src/analyses/everMultiThreaded.ml index c98f173624..5567ef1223 100644 --- a/src/analyses/everMultiThreaded.ml +++ b/src/analyses/everMultiThreaded.ml @@ -32,10 +32,11 @@ struct let query ctx (type a) (q: a Queries.t) : a Queries.result = match q with | Queries.IsEverMultiThreaded -> - (* - ctx.global () - *) - Queries.Result.top q (* TODO *) + (match ctx.global () with + (* I don't know why this wrapping in a match construct is necessary. + * Without it, the compiler throws an error. *) + true -> true + | false -> false) | _ -> Queries.Result.top q diff --git a/src/analyses/loop_termination.ml b/src/analyses/loop_termination.ml index f066fbbab6..4b658071be 100644 --- a/src/analyses/loop_termination.ml +++ b/src/analyses/loop_termination.ml @@ -91,7 +91,7 @@ struct (** Checks whether a new thread was spawned some time. We want to discard * any knowledge about termination then (see query function) *) let must_be_single_threaded_since_start ctx = - ctx.ask (Queries.MustBeSingleThreaded {since_start = true}) + not (ctx.ask Queries.IsEverMultiThreaded) (** Provides information to Goblint *) let query ctx (type a) (q: a Queries.t): a Queries.result = diff --git a/src/autoTune.ml b/src/autoTune.ml index 9468d1d366..9c4fb8f742 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -180,7 +180,7 @@ let enableAnalyses anas = List.iter (GobConfig.set_auto "ana.activated[+]") anas (*If only one thread is used in the program, we can disable most thread analyses*) -(*The exceptions are analyses that are depended on by others: base -> mutex -> mutexEvents, access*) +(*The exceptions are analyses that are depended on by others: base -> mutex -> mutexEvents, access; termination -> isEverMultiThreaded *) (*escape is also still enabled, because otherwise we get a warning*) (*does not consider dynamic calls!*) From f8fa8e314339abd704c07421570503ae45206948 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Mon, 3 Jul 2023 13:35:45 +0200 Subject: [PATCH 0255/1312] Revert whitespace change --- src/cdomains/intDomain.ml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cdomains/intDomain.ml b/src/cdomains/intDomain.ml index df0a4c0507..589239810f 100644 --- a/src/cdomains/intDomain.ml +++ b/src/cdomains/intDomain.ml @@ -3263,7 +3263,6 @@ struct let refine_with_incl_list ik a b = a let project ik p t = t - end module SOverflowLifter (D : S) : SOverflow with type int_t = D.int_t and type t = D.t = struct From 4f60156593453f3dac2e86a41b62ad03d0ad4509 Mon Sep 17 00:00:00 2001 From: Isidor Zweckstetter Date: Mon, 3 Jul 2023 16:32:17 +0200 Subject: [PATCH 0256/1312] added missing __goblint_bounded implementations --- lib/goblint/runtime/include/goblint.h | 2 ++ lib/goblint/runtime/src/goblint.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/goblint/runtime/include/goblint.h b/lib/goblint/runtime/include/goblint.h index b0af41616e..3c1abae838 100644 --- a/lib/goblint/runtime/include/goblint.h +++ b/lib/goblint/runtime/include/goblint.h @@ -6,3 +6,5 @@ void __goblint_assume_join(/* pthread_t thread */); // undeclared argument to av void __goblint_split_begin(int exp); void __goblint_split_end(int exp); + +void __goblint_bounded(int exp); \ No newline at end of file diff --git a/lib/goblint/runtime/src/goblint.c b/lib/goblint/runtime/src/goblint.c index 39c18c5b8e..7929fcf37a 100644 --- a/lib/goblint/runtime/src/goblint.c +++ b/lib/goblint/runtime/src/goblint.c @@ -29,6 +29,6 @@ void __goblint_split_end(int exp) { } -void __goblint_bounded() { +void __goblint_bounded(int exp) { } \ No newline at end of file From 21f2f3428a6be6c62d3d89e794eecb143d7084e9 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Mon, 3 Jul 2023 16:38:11 +0200 Subject: [PATCH 0257/1312] Temp. revert must_be_single_threaded_since_start --- src/analyses/loop_termination.ml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/analyses/loop_termination.ml b/src/analyses/loop_termination.ml index 4b658071be..7341bcd349 100644 --- a/src/analyses/loop_termination.ml +++ b/src/analyses/loop_termination.ml @@ -91,7 +91,10 @@ struct (** Checks whether a new thread was spawned some time. We want to discard * any knowledge about termination then (see query function) *) let must_be_single_threaded_since_start ctx = + (* not (ctx.ask Queries.IsEverMultiThreaded) + *) + ctx.ask (Queries.MustBeSingleThreaded {since_start = true}) (** Provides information to Goblint *) let query ctx (type a) (q: a Queries.t): a Queries.result = From 6bf2d775ae2cecd8e73ca47bd2884c290ea74538 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 4 Jul 2023 16:24:38 +0200 Subject: [PATCH 0258/1312] Pass argument to `move_if_affected` --- src/cdomains/arrayDomain.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index f10988fda9..7f2e8ce2ee 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1897,7 +1897,7 @@ struct N.length t_n else F.length t_f - let move_if_affected ?(replace_with_const=false) (ask:VDQ.t) (t_f, t_n) v f = (F.move_if_affected ask t_f v f, N.move_if_affected ask t_n v f) + let move_if_affected ?(replace_with_const=false) (ask:VDQ.t) (t_f, t_n) v f = (F.move_if_affected ~replace_with_const ask t_f v f, N.move_if_affected ~replace_with_const ask t_n v f) let get_vars_in_e (t_f, _) = F.get_vars_in_e t_f let map f (t_f, t_n) = if get_bool "ana.base.arrays.nullbytes" then From 60d06874f62687227db5afd4bf95163f79a2912e Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 4 Jul 2023 16:39:19 +0200 Subject: [PATCH 0259/1312] More missing optional arguments --- src/cdomains/arrayDomain.ml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 7f2e8ce2ee..2aa7c12976 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1873,9 +1873,9 @@ struct let domain_of_t (t_f, _) = F.domain_of_t t_f let get ?(checkBounds=true) (ask: VDQ.t) (t_f, t_n) i = - let f_get = F.get ask t_f i in + let f_get = F.get ~checkBounds ask t_f i in if get_bool "ana.base.arrays.nullbytes" then - let n_get = N.get ask t_n i in + let n_get = N.get ~checkBounds ask t_n i in match Val.is_int_ikind f_get, n_get with | Some ik, Null -> Val.meet f_get (Val.zero_of_ikind ik) | Some ik, NotNull -> Val.meet f_get (Val.not_zero_of_ikind ik) @@ -1889,9 +1889,9 @@ struct (F.set ask t_f i v, N.top ()) let make ?(varAttr=[]) ?(typAttr=[]) i v = if get_bool "ana.base.arrays.nullbytes" then - (F.make i v, N.make i v) + (F.make ~varAttr ~typAttr i v, N.make i v) else - (F.make i v, N.top ()) + (F.make ~varAttr ~typAttr i v, N.top ()) let length (t_f, t_n) = if get_bool "ana.base.arrays.nullbytes" then N.length t_n @@ -1964,6 +1964,6 @@ struct (F.update_length newl t_f, N.update_length newl t_n) else (F.update_length newl t_f, N.top ()) - let project ?(varAttr=[]) ?(typAttr=[]) ask (t_f, t_n) = (F.project ask t_f, N.project ask t_n) + let project ?(varAttr=[]) ?(typAttr=[]) ask (t_f, t_n) = (F.project ~varAttr ~typAttr ask t_f, N.project ~varAttr ~typAttr ask t_n) let invariant ~value_invariant ~offset ~lval (t_f, _) = F.invariant ~value_invariant ~offset ~lval t_f end From 048de26d949bde76ebf5ffe597bde7654e675e21 Mon Sep 17 00:00:00 2001 From: Isidor Zweckstetter Date: Wed, 5 Jul 2023 18:04:28 +0200 Subject: [PATCH 0260/1312] Patched inconsistency with nested loops --- runningGob.sh | 2 +- src/util/terminationPreprocessing.ml | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/runningGob.sh b/runningGob.sh index fcb5417192..11173bee1f 100755 --- a/runningGob.sh +++ b/runningGob.sh @@ -8,7 +8,7 @@ options_apron="--set ana.activated[+] apron --enable ana.int.interval --set ana. options_signs="--set "ana.activated[+]" signs --enable warn.debug" options_term="--set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra" -cfile_loops="tests/regression/55-loop-unrolling/01-simple-cases.c" +cfile_loops="tests/regression/74-loop_termination/03-nested-loop-terminating.c" cfile_signs="tests/regression/99-tutorials/01-first.c" cfile_goto="tests/incremental/02-cfg-comparison/01-added-return-stmt.c" diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index 47a17575c4..4043c6d256 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -34,7 +34,10 @@ class loopCounterVisitor lc lg le (fd : fundec) = object(self) let init_stmt = mkStmtOneInstr @@ Set (var v, zero, loc, eloc) in let inc_stmt = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in let check_stmt = mkStmtOneInstr @@ Set ((var !le), (Lval (var v)), loc, eloc) in + let inc_stmt2 = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in (match b.bstmts with + | s :: ss -> (*duplicate increment statement here to fix inconsistencies in nested loops*) + b.bstmts <- inc_stmt :: check_stmt :: s :: inc_stmt2 :: ss; | ss -> b.bstmts <- inc_stmt :: check_stmt :: ss; ); From 3b2f4a55736e83350fe71b345cf0d0beb1fd66ef Mon Sep 17 00:00:00 2001 From: Nathan Schmidt Date: Wed, 5 Jul 2023 23:04:53 +0200 Subject: [PATCH 0261/1312] Fixed integration in base using get thanks to Michael's workaround --- src/analyses/base.ml | 125 +++++++++--------- .../73-strings/01-string_literals.c | 2 +- .../regression/73-strings/03-string_basics.c | 14 +- tests/regression/73-strings/04-char_arrays.c | 2 +- 4 files changed, 72 insertions(+), 71 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index dbe6438fca..441444e69a 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2030,46 +2030,66 @@ struct (* do nothing if all characters are needed *) | _ -> None in + let address_from_value (v:value) = match v with + | Address a -> + let rec lo:'a Offset_intf.t -> 'a Offset_intf.t = function + | `Index (i, `NoOffset) -> `NoOffset + | `NoOffset -> `NoOffset + | `Field (f, o) -> `Field (f, lo o) + | `Index (i, o) -> `Index (i, lo o) in + let rmLastOffset = function + | Addr.Addr (v, o) -> Addr.Addr (v, lo o) + | other -> other in + AD.map rmLastOffset a + | _ -> raise (Failure "String function: not an address") + in let string_manipulation s1 s2 lv all op_addr op_array = - let s1_a, s1_typ = addr_type_of_exp s1 in - let s2_a, s2_typ = addr_type_of_exp s2 in + let s1_v = eval_rv (Analyses.ask_of_ctx ctx) gs st s1 in + let s1_a = address_from_value s1_v in + let s1_typ = AD.type_of s1_a in + let s2_v = eval_rv (Analyses.ask_of_ctx ctx) gs st s2 in + let s2_a = address_from_value s2_v in + let s2_typ = AD.type_of s2_a in (* compute value in string literals domain if s1 and s2 are both string literals *) - if AD.type_of s1_a = charPtrType && AD.type_of s2_a = charPtrType then + if s1_typ = charPtrType && s2_typ = charPtrType then begin match lv, op_addr with | Some lv_val, Some f -> (* when whished types coincide, compute result of operation op_addr, otherwise use top *) let lv_a = eval_lv (Analyses.ask_of_ctx ctx) gs st lv_val in let lv_typ = Cilfacade.typeOfLval lv_val in if all && typeSig s1_typ = typeSig s2_typ && typeSig s2_typ = typeSig lv_typ then (* all types need to coincide *) - lv_a, lv_typ, (f s1_a s2_a), None + set ~ctx (Analyses.ask_of_ctx ctx) gs st lv_a lv_typ (f s1_a s2_a) else if not all && typeSig s1_typ = typeSig s2_typ then (* only the types of s1 and s2 need to coincide *) - lv_a, lv_typ, (f s1_a s2_a), None + set ~ctx (Analyses.ask_of_ctx ctx) gs st lv_a lv_typ (f s1_a s2_a) else - lv_a, lv_typ, (VD.top_value (unrollType lv_typ)), None + set ~ctx (Analyses.ask_of_ctx ctx) gs st lv_a lv_typ (VD.top_value (unrollType lv_typ)) | _ -> (* check if s1 is potentially a string literal as writing to it would be undefined behavior; then return top *) let _ = AD.string_writing_defined s1_a in - s1_a, s1_typ, VD.top_value (unrollType s1_typ), None + set ~ctx (Analyses.ask_of_ctx ctx) gs st s1_a s1_typ (VD.top_value (unrollType s1_typ)) end (* else compute value in array domain *) else let lv_a, lv_typ = match lv with | Some lv_val -> eval_lv (Analyses.ask_of_ctx ctx) gs st lv_val, Cilfacade.typeOfLval lv_val | None -> s1_a, s1_typ in - let s1_lval = mkMem ~addr:(Cil.stripCasts s1) ~off:NoOffset in - let s2_lval = mkMem ~addr:(Cil.stripCasts s2) ~off:NoOffset in - match s1_lval, s2_lval with - | (Var v_s1, _), (Var v_s2, _) -> - begin match CPA.find_opt v_s1 st.cpa, CPA.find_opt v_s2 st.cpa with - | Some (Array array_s1), Some (Array array_s2) -> lv_a, lv_typ, op_array array_s1 array_s2, Some v_s1 - | _ -> lv_a, lv_typ, VD.top_value (unrollType lv_typ), None - end - | (Var v_s1, _), _ -> - begin match CPA.find_opt v_s1 st.cpa with - | Some (Array array_s1) -> lv_a, lv_typ, Array(CArrays.content_to_top array_s1), Some v_s1 - | _ -> lv_a, lv_typ, VD.top_value (unrollType lv_typ), Some v_s1 - end - | _ -> lv_a, lv_typ, VD.top_value (unrollType lv_typ), None + begin match get (Analyses.ask_of_ctx ctx) gs st s1_a None, get (Analyses.ask_of_ctx ctx) gs st s2_a None with + | Array array_s1, Array array_s2 -> set ~ctx (Analyses.ask_of_ctx ctx) gs st lv_a lv_typ (op_array array_s1 array_s2) + | Array array_s1, _ when s2_typ = charPtrType -> + let s2_null_bytes = List.map CArrays.to_null_byte_domain (AD.to_string s2_a) in + let array_s2 = List.fold_left CArrays.join (CArrays.bot ()) s2_null_bytes in + set ~ctx (Analyses.ask_of_ctx ctx) gs st lv_a lv_typ (op_array array_s1 array_s2) + | _, Array array_s2 when s1_typ = charPtrType -> + (* if s1 is string literal, str(n)cpy and str(n)cat are undefined *) + if op_addr = None then + let _ = AD.string_writing_defined s1_a in + set ~ctx (Analyses.ask_of_ctx ctx) gs st s1_a s1_typ (VD.top_value (unrollType s1_typ)) + else + let s1_null_bytes = List.map CArrays.to_null_byte_domain (AD.to_string s1_a) in + let array_s1 = List.fold_left CArrays.join (CArrays.bot ()) s1_null_bytes in + set ~ctx (Analyses.ask_of_ctx ctx) gs st lv_a lv_typ (op_array array_s1 array_s2) + | _ -> set ~ctx (Analyses.ask_of_ctx ctx) gs st lv_a lv_typ (VD.top_value (unrollType lv_typ)) + end in let st = match desc.special args, f.vname with | Memset { dest; ch; count; }, _ -> @@ -2103,42 +2123,23 @@ struct VD.top_value (unrollType dest_typ) in set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value - | Strcpy { dest = dst; src; n }, _ -> - let dest_a, dest_typ, value, var = string_manipulation dst src None false None (fun ar1 ar2 -> Array(CArrays.string_copy ar1 ar2 (eval_n n))) in - begin match var with - | Some v -> {st with cpa = CPA.add v value st.cpa} - | None -> set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value - end - | Strcat { dest = dst; src; n }, _ -> - let dest_a, dest_typ, value, var = string_manipulation dst src None false None (fun ar1 ar2 -> Array(CArrays.string_concat ar1 ar2 (eval_n n))) in - begin match var with - | Some v -> {st with cpa = CPA.add v value st.cpa} - | None -> set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value - end + | Strcpy { dest = dst; src; n }, _ -> string_manipulation dst src None false None (fun ar1 ar2 -> Array (CArrays.string_copy ar1 ar2 (eval_n n))) + | Strcat { dest = dst; src; n }, _ -> string_manipulation dst src None false None (fun ar1 ar2 -> Array (CArrays.string_concat ar1 ar2 (eval_n n))) | Strlen s, _ -> begin match lv with | Some lv_val -> let dest_a = eval_lv (Analyses.ask_of_ctx ctx) gs st lv_val in let dest_typ = Cilfacade.typeOfLval lv_val in - let lval = mkMem ~addr:(Cil.stripCasts s) ~off:NoOffset in - let address = eval_lv (Analyses.ask_of_ctx ctx) gs st lval in - let (value:value) = + let v = eval_rv (Analyses.ask_of_ctx ctx) gs st s in + let a = address_from_value v in + let value:value = (* if s string literal, compute strlen in string literals domain *) - if AD.type_of address = charPtrType then - Int(AD.to_string_length address) + if AD.type_of a = charPtrType then + Int (AD.to_string_length a) (* else compute strlen in array domain *) else - (* (* TODO: why isn't the following working? *) - begin match get (Analyses.ask_of_ctx ctx) gs st address None with - | Array array_s -> Int(CArrays.to_string_length array_s) - | _ -> VD.top_value (unrollType dest_typ) - end) in *) - begin match lval with - | (Var v, _) -> - begin match CPA.find_opt v st.cpa with - | Some (Array array_s) -> Int(CArrays.to_string_length array_s) - | _ -> VD.top_value (unrollType dest_typ) - end + begin match get (Analyses.ask_of_ctx ctx) gs st a None with + | Array array_s -> Int (CArrays.to_string_length array_s) | _ -> VD.top_value (unrollType dest_typ) end in set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value @@ -2147,25 +2148,25 @@ struct | Strstr { haystack; needle }, _ -> begin match lv with | Some lv_val -> - (* when haystack, needle and dest type coincide, check if needle is a substring of haystack: - if that is the case, assign the substring of haystack starting at the first occurrence of needle to dest, - else use top *) - let dest_a, dest_typ, value, _ = string_manipulation haystack needle lv true (Some (fun h_a n_a -> Address(AD.substring_extraction h_a n_a))) + (* check if needle is a substring of haystack in string literals domain if haystack and needle are string literals, + else check in null bytes domain if both haystack and needle are / can be transformed to an array domain representation; + if needle is substring, assign the substring of haystack starting at the first occurrence of needle to dest, + if it surely isn't, assign a null_ptr *) + string_manipulation haystack needle lv true (Some (fun h_a n_a -> Address (AD.substring_extraction h_a n_a))) (fun h_ar n_ar -> match CArrays.substring_extraction h_ar n_ar with - | true, false -> Address(AD.null_ptr) - | false, true -> Address(eval_lv (Analyses.ask_of_ctx ctx) gs st (mkMem ~addr:(Cil.stripCasts haystack) ~off:NoOffset)) - (* TODO: below, instead of ~off:NoOffset, how to have a top offset = don't know exactly at which index pointing? *) - | _ -> Address(AD.join (eval_lv (Analyses.ask_of_ctx ctx) gs st (mkMem ~addr:(Cil.stripCasts haystack) ~off:NoOffset)) (AD.null_ptr))) in - set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value + | true, false -> Address (AD.null_ptr) + | false, true -> Address (eval_lv (Analyses.ask_of_ctx ctx) gs st (mkMem ~addr:(Cil.stripCasts haystack) ~off:NoOffset)) + | _ -> Address (AD.join (eval_lv (Analyses.ask_of_ctx ctx) gs st + (mkMem ~addr:(Cil.stripCasts haystack) ~off:(Index (Offset.Index.Exp.any, NoOffset)))) (AD.null_ptr))) | None -> st end | Strcmp { s1; s2; n }, _ -> begin match lv with | Some _ -> - (* when s1 and s2 type coincide, compare both both strings completely or their first n characters, otherwise use top *) - let dest_a, dest_typ, value, _ = string_manipulation s1 s2 lv false (Some (fun s1_a s2_a -> Int(AD.string_comparison s1_a s2_a (eval_n n)))) - (fun s1_ar s2_ar -> Int(CArrays.string_comparison s1_ar s2_ar (eval_n n))) in - set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value + (* when s1 and s2 are string literals, compare both completely or their first n characters in the string literals domain; + else compare them in the null bytes array domain if they are / can be transformed to an array domain representation *) + string_manipulation s1 s2 lv false (Some (fun s1_a s2_a -> Int (AD.string_comparison s1_a s2_a (eval_n n)))) + (fun s1_ar s2_ar -> Int (CArrays.string_comparison s1_ar s2_ar (eval_n n))) | None -> st end | Abort, _ -> raise Deadcode diff --git a/tests/regression/73-strings/01-string_literals.c b/tests/regression/73-strings/01-string_literals.c index 42a888d1b4..bc27c917be 100644 --- a/tests/regression/73-strings/01-string_literals.c +++ b/tests/regression/73-strings/01-string_literals.c @@ -102,7 +102,7 @@ int main() { // do nothing => no warning #else char s4[] = "hello"; - strcpy(s4, s2); // NOWARN + strcpy(s4, s2); // NOWARN -> null byte array domain not enabled strncpy(s4, s3, 2); // NOWARN char s5[13] = "hello"; diff --git a/tests/regression/73-strings/03-string_basics.c b/tests/regression/73-strings/03-string_basics.c index 180d9a00bc..3487a36be7 100644 --- a/tests/regression/73-strings/03-string_basics.c +++ b/tests/regression/73-strings/03-string_basics.c @@ -39,9 +39,9 @@ int main() { __goblint_check(i == 0); // UNKNOWN strcpy(s1, "hi "); - strncpy(s1, s3, 3); - len = strlen(s1); // TODO: produces a false warning -- any possibility to fix? - __goblint_check(len == 3); // UNKNOWN + strncpy(s1, s3, 3); // WARN + len = strlen(s1); + __goblint_check(len == 3); char tmp[] = "hi "; len = strlen(tmp); @@ -64,10 +64,10 @@ int main() { i = strncmp(s4, s3, 5); __goblint_check(i > 0); // UNKNOWN - strncpy(s1, "", 20); + strncpy(s1, "", 20); // WARN strcpy(tmp, "\0hi"); i = strcmp(s1, tmp); - __goblint_check(i == 0); // UNKNOWN + __goblint_check(i == 0); char tmp2[] = ""; strcpy(s1, tmp2); @@ -75,11 +75,11 @@ int main() { __goblint_check(i == 0); i = strcmp(s1, tmp); - __goblint_check(i == 0); // UNKNOWN + __goblint_check(i == 0); concat_1(s1, 30); len = strlen(s1); - __goblint_check(len == 30); // UNKNOWN + __goblint_check(len == 30); cmp = strstr(s1, "0"); __goblint_check(cmp == NULL); // UNKNOWN diff --git a/tests/regression/73-strings/04-char_arrays.c b/tests/regression/73-strings/04-char_arrays.c index 2d1b1bb07f..940960569f 100644 --- a/tests/regression/73-strings/04-char_arrays.c +++ b/tests/regression/73-strings/04-char_arrays.c @@ -163,7 +163,7 @@ void example8() { char cmp[50]; strcpy(cmp, strstr(s1, empty)); // NOWARN: strstr(s1, empty) != NULL size_t len = strlen(cmp); - __goblint_check(len == 11); // TODO: shouldn't this be known? + __goblint_check(len == 11); char* cmp_ptr = strstr(s2, s1); __goblint_check(cmp_ptr == NULL); From b67eacdbead591b054783c99dde36e1bdd391673 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Thu, 6 Jul 2023 12:09:01 +0200 Subject: [PATCH 0262/1312] Rename loop termation analysis Keep camelCase naming convention --- src/analyses/{loop_termination.ml => loopTermination.ml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/analyses/{loop_termination.ml => loopTermination.ml} (100%) diff --git a/src/analyses/loop_termination.ml b/src/analyses/loopTermination.ml similarity index 100% rename from src/analyses/loop_termination.ml rename to src/analyses/loopTermination.ml From 22efd120cd7f59b882b7a68a78b56e784cea96c3 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Thu, 6 Jul 2023 12:10:27 +0200 Subject: [PATCH 0263/1312] changed name --- src/framework/constraints.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 55763c5852..d744937d53 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1717,7 +1717,7 @@ struct module G = GVarGSet (S.G) (S.C) (Printable.Prod (CilType.Fundec) (S.C)) - let name () = "termination" + let name () = "RecursionTermLifter (" ^ S.name () ^ ")" let conv (ctx: (_, G.t, _, V.t) ctx): (_, S.G.t, _, S.V.t) ctx = { ctx with From c5fe485c0de7dbcfce0100f5819d7c2150a90c4e Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Thu, 6 Jul 2023 12:14:22 +0200 Subject: [PATCH 0264/1312] Remove unused code --- src/analyses/loopTermination.ml | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/analyses/loopTermination.ml b/src/analyses/loopTermination.ml index 7341bcd349..d9f8df2f48 100644 --- a/src/analyses/loopTermination.ml +++ b/src/analyses/loopTermination.ml @@ -6,17 +6,6 @@ open TerminationPreprocessing exception PreProcessing of string -(* -let loop_heads () = - let module FileCfg = - struct - let file = !Cilfacade.current_file - module Cfg = (val !MyCFG.current_cfg) - end in - let module WitnessInvariant = WitnessUtil.Invariant (FileCfg) in - WitnessInvariant.loop_heads (* TODO: Unused *) -*) - (** Contains all loop counter variables (varinfo) and maps them to their corresponding loop statement. *) let loop_counters : stmt VarToStmt.t ref = ref VarToStmt.empty From b9a5c361cddce214db5217c3298cf68d56f200ee Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Thu, 6 Jul 2023 12:53:58 +0200 Subject: [PATCH 0265/1312] Tests adapted and new test 74/35 --- .../09-complex-for-loop-terminating.c | 1 + .../10-complex-loop-terminating.c | 1 + .../15-complex-loop-combination-terminating.c | 1 + .../24-upjumping-goto-loopless-terminating.c | 2 +- .../25-leave-loop-goto-terminating.c | 2 +- .../26-enter-loop-goto-terminating.c | 2 +- .../28-do-while-continue-terminating.c | 4 ++-- .../30-goto-out-of-inner-loop-terminating.c | 3 ++- ...out-of-inner-loop-with-print-terminating.c | 22 +++++++++++++++++++ .../02-simple-nonterminating.c | 2 +- 10 files changed, 33 insertions(+), 7 deletions(-) create mode 100644 tests/regression/74-loop_termination/35-goto-out-of-inner-loop-with-print-terminating.c diff --git a/tests/regression/74-loop_termination/09-complex-for-loop-terminating.c b/tests/regression/74-loop_termination/09-complex-for-loop-terminating.c index 7fb9262aab..30ca32a70a 100644 --- a/tests/regression/74-loop_termination/09-complex-for-loop-terminating.c +++ b/tests/regression/74-loop_termination/09-complex-for-loop-terminating.c @@ -1,4 +1,5 @@ // SKIP TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// Goblint does not finish this test #include int main() diff --git a/tests/regression/74-loop_termination/10-complex-loop-terminating.c b/tests/regression/74-loop_termination/10-complex-loop-terminating.c index e39613b563..8970223c6e 100644 --- a/tests/regression/74-loop_termination/10-complex-loop-terminating.c +++ b/tests/regression/74-loop_termination/10-complex-loop-terminating.c @@ -1,4 +1,5 @@ // SKIP TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// Goblint does not finish this test #include int main() diff --git a/tests/regression/74-loop_termination/15-complex-loop-combination-terminating.c b/tests/regression/74-loop_termination/15-complex-loop-combination-terminating.c index ad64a9a5f9..099203d13f 100644 --- a/tests/regression/74-loop_termination/15-complex-loop-combination-terminating.c +++ b/tests/regression/74-loop_termination/15-complex-loop-combination-terminating.c @@ -1,4 +1,5 @@ // SKIP TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// Goblint does not finish this test #include int main() diff --git a/tests/regression/74-loop_termination/24-upjumping-goto-loopless-terminating.c b/tests/regression/74-loop_termination/24-upjumping-goto-loopless-terminating.c index e256df9986..1dc261d06a 100644 --- a/tests/regression/74-loop_termination/24-upjumping-goto-loopless-terminating.c +++ b/tests/regression/74-loop_termination/24-upjumping-goto-loopless-terminating.c @@ -1,7 +1,7 @@ // TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { // Currently not able to detect this as terminating +int main() { // Currently not able to detect up-jumping loop free gotos goto mark2; mark1: diff --git a/tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c b/tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c index ce11a73060..cbbb115868 100644 --- a/tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c +++ b/tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c @@ -1,4 +1,4 @@ -// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain octagon +// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c b/tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c index 355c1ebf00..17220a589b 100644 --- a/tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c +++ b/tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c @@ -1,4 +1,4 @@ -// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/74-loop_termination/28-do-while-continue-terminating.c b/tests/regression/74-loop_termination/28-do-while-continue-terminating.c index 7f91ecc149..7756b51071 100644 --- a/tests/regression/74-loop_termination/28-do-while-continue-terminating.c +++ b/tests/regression/74-loop_termination/28-do-while-continue-terminating.c @@ -1,4 +1,4 @@ -// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() @@ -12,7 +12,7 @@ int main() if (i % 2 == 0) { printf("Skipping %i is even\n", i); - continue; + continue; // This is handled as an goto to line 8 and there an up-jumping goto } } while (i <= 5); diff --git a/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c b/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c index 6b36919c2d..5662e31dc1 100644 --- a/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c +++ b/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c @@ -14,8 +14,9 @@ int main() { } printf("(%d, %d) ", i, j); } - printf("\n"); + printf("Not Skipped?\n"); outer_loop:; // Label for the outer loop + printf("Skipped!\n"); } return 0; diff --git a/tests/regression/74-loop_termination/35-goto-out-of-inner-loop-with-print-terminating.c b/tests/regression/74-loop_termination/35-goto-out-of-inner-loop-with-print-terminating.c new file mode 100644 index 0000000000..b418257c56 --- /dev/null +++ b/tests/regression/74-loop_termination/35-goto-out-of-inner-loop-with-print-terminating.c @@ -0,0 +1,22 @@ +// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() { + int rows = 5; + int columns = 5; + + // Outer loop for rows + for (int i = 1; i <= rows; i++) { + // Inner loop for columns + for (int j = 1; j <= columns; j++) { + if (j == 3) { // Apron is not able to detect this + goto outer_loop; // Jump to the label "outer_loop" + } + printf("(%d, %d) ", i, j); + } + outer_loop:; // Label for the outer loop + printf("\n"); + } + + return 0; +} diff --git a/tests/regression/75-recursion_termination/02-simple-nonterminating.c b/tests/regression/75-recursion_termination/02-simple-nonterminating.c index 26f30e726b..0dc3cbcf63 100644 --- a/tests/regression/75-recursion_termination/02-simple-nonterminating.c +++ b/tests/regression/75-recursion_termination/02-simple-nonterminating.c @@ -1,4 +1,4 @@ -// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include void recursiveFunction(int n) { From 7e12cb506a8897f39f6549251883d3f6d663adae Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Thu, 6 Jul 2023 12:56:06 +0200 Subject: [PATCH 0266/1312] Tests is now passing --- .../74-loop_termination/30-goto-out-of-inner-loop-terminating.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c b/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c index 5662e31dc1..090f3830d5 100644 --- a/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c +++ b/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c @@ -1,4 +1,4 @@ -// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { From bc7bef754340c0a35db3c15fc366c88959c150ce Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Thu, 6 Jul 2023 13:14:18 +0200 Subject: [PATCH 0267/1312] Restrict boundedness checking to postsolving --- src/analyses/loopTermination.ml | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/analyses/loopTermination.ml b/src/analyses/loopTermination.ml index d9f8df2f48..00c7591ad2 100644 --- a/src/analyses/loopTermination.ml +++ b/src/analyses/loopTermination.ml @@ -62,16 +62,17 @@ struct let exitstate = startstate let assign ctx (lval : lval) (rval : exp) = - (* Detect assignment to loop counter variable *) - match lval, rval with - (Var y, NoOffset), Lval (Var x, NoOffset) when is_loop_exit_indicator y -> - (* Loop exit: Check whether loop counter variable is bounded *) - (* TODO: Move to special *) - let is_bounded = check_bounded ctx x in - let loop_statement = VarToStmt.find x !loop_counters in - ctx.sideg () (G.add (`Lifted loop_statement) is_bounded (ctx.global ())); - () - | _ -> () + if !AnalysisState.postsolving then + (* Detect assignment to loop counter variable *) + match lval, rval with + (Var y, NoOffset), Lval (Var x, NoOffset) when is_loop_exit_indicator y -> + (* Loop exit: Check whether loop counter variable is bounded *) + (* TODO: Move to special *) + let is_bounded = check_bounded ctx x in + let loop_statement = VarToStmt.find x !loop_counters in + ctx.sideg () (G.add (`Lifted loop_statement) is_bounded (ctx.global ())); + () + | _ -> () let special ctx (lval : lval option) (f : varinfo) (arglist : exp list) = (* TODO: Implement check for our special loop exit indicator function *) From 08ad8d0595e52ef49643b1745a86b573468eafdc Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Thu, 6 Jul 2023 13:27:58 +0200 Subject: [PATCH 0268/1312] Revert "Restrict boundedness checking to postsolving" This reverts commit bc7bef754340c0a35db3c15fc366c88959c150ce. --- src/analyses/loopTermination.ml | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/analyses/loopTermination.ml b/src/analyses/loopTermination.ml index 00c7591ad2..d9f8df2f48 100644 --- a/src/analyses/loopTermination.ml +++ b/src/analyses/loopTermination.ml @@ -62,17 +62,16 @@ struct let exitstate = startstate let assign ctx (lval : lval) (rval : exp) = - if !AnalysisState.postsolving then - (* Detect assignment to loop counter variable *) - match lval, rval with - (Var y, NoOffset), Lval (Var x, NoOffset) when is_loop_exit_indicator y -> - (* Loop exit: Check whether loop counter variable is bounded *) - (* TODO: Move to special *) - let is_bounded = check_bounded ctx x in - let loop_statement = VarToStmt.find x !loop_counters in - ctx.sideg () (G.add (`Lifted loop_statement) is_bounded (ctx.global ())); - () - | _ -> () + (* Detect assignment to loop counter variable *) + match lval, rval with + (Var y, NoOffset), Lval (Var x, NoOffset) when is_loop_exit_indicator y -> + (* Loop exit: Check whether loop counter variable is bounded *) + (* TODO: Move to special *) + let is_bounded = check_bounded ctx x in + let loop_statement = VarToStmt.find x !loop_counters in + ctx.sideg () (G.add (`Lifted loop_statement) is_bounded (ctx.global ())); + () + | _ -> () let special ctx (lval : lval option) (f : varinfo) (arglist : exp list) = (* TODO: Implement check for our special loop exit indicator function *) From 2e05e9e5710b9a70be84776527906e901ed3b7dd Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Thu, 6 Jul 2023 13:40:51 +0200 Subject: [PATCH 0269/1312] Restrict boundedness checking to postsolving (fix) --- src/analyses/loopTermination.ml | 39 ++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/src/analyses/loopTermination.ml b/src/analyses/loopTermination.ml index d9f8df2f48..921aef28ee 100644 --- a/src/analyses/loopTermination.ml +++ b/src/analyses/loopTermination.ml @@ -36,7 +36,7 @@ let check_bounded ctx varinfo = module UnitV = struct include Printable.Unit - include StdV + let is_write_only _ = true end (** We want to record termination information of loops and use the loop @@ -62,20 +62,33 @@ struct let exitstate = startstate let assign ctx (lval : lval) (rval : exp) = - (* Detect assignment to loop counter variable *) - match lval, rval with - (Var y, NoOffset), Lval (Var x, NoOffset) when is_loop_exit_indicator y -> - (* Loop exit: Check whether loop counter variable is bounded *) - (* TODO: Move to special *) - let is_bounded = check_bounded ctx x in - let loop_statement = VarToStmt.find x !loop_counters in - ctx.sideg () (G.add (`Lifted loop_statement) is_bounded (ctx.global ())); - () - | _ -> () + if !AnalysisState.postsolving then + (* Detect assignment to loop counter variable *) + match lval, rval with + (Var y, NoOffset), Lval (Var x, NoOffset) when is_loop_exit_indicator y -> + (* Loop exit: Check whether loop counter variable is bounded *) + (* TODO: Move to special *) + let is_bounded = check_bounded ctx x in + let loop_statement = VarToStmt.find x !loop_counters in + ctx.sideg () (G.add (`Lifted loop_statement) is_bounded (ctx.global ())); + () + | _ -> () + else () + (* let special ctx (lval : lval option) (f : varinfo) (arglist : exp list) = - (* TODO: Implement check for our special loop exit indicator function *) - () + (* TODO: Implement check for our special loop exit indicator function *) + if !AnalysisState.postsolving then + match f.vname, arglist with + "__goblint_bounded", [Lval (Var x, NoOffset)] -> + let () = print_endline "schpecial" in + let is_bounded = check_bounded ctx x in + let loop_statement = VarToStmt.find x !loop_counters in + ctx.sideg () (G.add (`Lifted loop_statement) is_bounded (ctx.global ())); + () + | _ -> () + else () + *) (** Checks whether a new thread was spawned some time. We want to discard * any knowledge about termination then (see query function) *) From 0d79d2ace1a5d38931d8204e58a6e844637e96cf Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Thu, 6 Jul 2023 14:06:17 +0200 Subject: [PATCH 0270/1312] Widen recursion test --- .../75-recursion_termination/02-simple-nonterminating.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression/75-recursion_termination/02-simple-nonterminating.c b/tests/regression/75-recursion_termination/02-simple-nonterminating.c index 0dc3cbcf63..d20adea294 100644 --- a/tests/regression/75-recursion_termination/02-simple-nonterminating.c +++ b/tests/regression/75-recursion_termination/02-simple-nonterminating.c @@ -1,4 +1,4 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --enable ana.context.widen #include void recursiveFunction(int n) { From 1190d63212e2a887633438651e6b4844e6669340 Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Thu, 6 Jul 2023 14:23:36 +0200 Subject: [PATCH 0271/1312] Adapted test parameter --- .../30-goto-out-of-inner-loop-terminating.c | 2 +- .../35-goto-out-of-inner-loop-with-print-terminating.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c b/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c index 090f3830d5..15ce6e395e 100644 --- a/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c +++ b/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c @@ -9,7 +9,7 @@ int main() { for (int i = 1; i <= rows; i++) { // Inner loop for columns for (int j = 1; j <= columns; j++) { - if (j == 3) { // Apron is not able to detect this + if (j == 3) { goto outer_loop; // Jump to the label "outer_loop" } printf("(%d, %d) ", i, j); diff --git a/tests/regression/74-loop_termination/35-goto-out-of-inner-loop-with-print-terminating.c b/tests/regression/74-loop_termination/35-goto-out-of-inner-loop-with-print-terminating.c index b418257c56..29a8033fdb 100644 --- a/tests/regression/74-loop_termination/35-goto-out-of-inner-loop-with-print-terminating.c +++ b/tests/regression/74-loop_termination/35-goto-out-of-inner-loop-with-print-terminating.c @@ -1,4 +1,4 @@ -// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set "ana.activated[+]" apron --enable ana.int.interval --set ana.apron.domain polyhedra --set sem.int.signed_overflow assume_none #include int main() { @@ -9,7 +9,7 @@ int main() { for (int i = 1; i <= rows; i++) { // Inner loop for columns for (int j = 1; j <= columns; j++) { - if (j == 3) { // Apron is not able to detect this + if (j == 3) { goto outer_loop; // Jump to the label "outer_loop" } printf("(%d, %d) ", i, j); From 1bff8fb0222ffe6ac77616bfe1a713aa7ec70f6d Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 8 Jul 2023 10:05:40 +0200 Subject: [PATCH 0272/1312] Add InvalidMemoryDeallocation message category --- src/util/messageCategory.ml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/util/messageCategory.ml b/src/util/messageCategory.ml index ddf91dba0b..5452225c26 100644 --- a/src/util/messageCategory.ml +++ b/src/util/messageCategory.ml @@ -12,6 +12,7 @@ type undefined_behavior = | NullPointerDereference | UseAfterFree | DoubleFree + | InvalidMemoryDeallocation | Uninitialized | DoubleLocking | Other @@ -65,6 +66,7 @@ struct let nullpointer_dereference: category = create @@ NullPointerDereference let use_after_free: category = create @@ UseAfterFree let double_free: category = create @@ DoubleFree + let invalid_memory_deallocation: category = create @@ InvalidMemoryDeallocation let uninitialized: category = create @@ Uninitialized let double_locking: category = create @@ DoubleLocking let other: category = create @@ Other @@ -102,6 +104,7 @@ struct | "nullpointer_dereference" -> nullpointer_dereference | "use_after_free" -> use_after_free | "double_free" -> double_free + | "invalid_memory_deallocation" -> invalid_memory_deallocation | "uninitialized" -> uninitialized | "double_locking" -> double_locking | "other" -> other @@ -113,6 +116,7 @@ struct | NullPointerDereference -> ["NullPointerDereference"] | UseAfterFree -> ["UseAfterFree"] | DoubleFree -> ["DoubleFree"] + | InvalidMemoryDeallocation -> ["InvalidMemoryDeallocation"] | Uninitialized -> ["Uninitialized"] | DoubleLocking -> ["DoubleLocking"] | Other -> ["Other"] @@ -223,6 +227,7 @@ let behaviorName = function |NullPointerDereference -> "NullPointerDereference" |UseAfterFree -> "UseAfterFree" |DoubleFree -> "DoubleFree" + |InvalidMemoryDeallocation -> "InvalidMemoryDeallocation" |Uninitialized -> "Uninitialized" |DoubleLocking -> "DoubleLocking" |Other -> "Other" From f8e8116460f9d8d5b8e91c2aa4b2390d6591477f Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 8 Jul 2023 10:06:02 +0200 Subject: [PATCH 0273/1312] Add checks for deallocation of non-dynamically allocated memory --- src/analyses/base.ml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 72cc6a614f..cdc8e5a7d3 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1997,6 +1997,20 @@ struct let st' = invalidate ~deep:false ~ctx (Analyses.ask_of_ctx ctx) gs st shallow_addrs in invalidate ~deep:true ~ctx (Analyses.ask_of_ctx ctx) gs st' deep_addrs + let check_free_of_non_heap_mem ctx special_fn ptr = + match ctx.ask (Queries.MayPointTo ptr) with + | a when not (Queries.LS.is_top a) -> + let warn_if_not_heap_var special_fn var = + if not (ctx.ask (Queries.IsHeapVar var)) then + M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Free of non-dynamically allocated memory in function %s for pointer %a" special_fn.vname d_exp ptr + in + let pointed_to_vars = + Queries.LS.elements a + |> List.map fst + in + List.iter (warn_if_not_heap_var special_fn) pointed_to_vars + | _ -> () + let special ctx (lv:lval option) (f: varinfo) (args: exp list) = let invalidate_ret_lv st = match lv with | Some lv -> @@ -2277,6 +2291,8 @@ struct | _ -> st end | Realloc { ptr = p; size }, _ -> + (* Realloc shouldn't be passed non-dynamically allocated memory *) + check_free_of_non_heap_mem ctx f p; begin match lv with | Some lv -> let ask = Analyses.ask_of_ctx ctx in @@ -2308,6 +2324,10 @@ struct | None -> st end + | Free ptr, _ -> + (* Free shouldn't be passed non-dynamically allocated memory *) + check_free_of_non_heap_mem ctx f ptr; + st | Assert { exp; refine; _ }, _ -> assert_fn ctx exp refine | Setjmp { env }, _ -> let ask = Analyses.ask_of_ctx ctx in From 25462b84432580f0a703a2d0a898eeb60f9a205c Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 8 Jul 2023 10:06:26 +0200 Subject: [PATCH 0274/1312] Add regression tests for invalid memory deallocation --- .../01-invalid-dealloc-simple.c | 14 +++++++++++ .../02-invalid-dealloc-struct.c | 14 +++++++++++ .../03-invalid-dealloc-array.c | 25 +++++++++++++++++++ .../75-invalid_dealloc/04-invalid-realloc.c | 25 +++++++++++++++++++ 4 files changed, 78 insertions(+) create mode 100644 tests/regression/75-invalid_dealloc/01-invalid-dealloc-simple.c create mode 100644 tests/regression/75-invalid_dealloc/02-invalid-dealloc-struct.c create mode 100644 tests/regression/75-invalid_dealloc/03-invalid-dealloc-array.c create mode 100644 tests/regression/75-invalid_dealloc/04-invalid-realloc.c diff --git a/tests/regression/75-invalid_dealloc/01-invalid-dealloc-simple.c b/tests/regression/75-invalid_dealloc/01-invalid-dealloc-simple.c new file mode 100644 index 0000000000..16fbd593f4 --- /dev/null +++ b/tests/regression/75-invalid_dealloc/01-invalid-dealloc-simple.c @@ -0,0 +1,14 @@ +#include + +int main(int argc, char const *argv[]) +{ + int a; + int *p = &a; + free(p); //WARN + + char b = 'b'; + char *p2 = &b; + free(p2); //WARN + + return 0; +} diff --git a/tests/regression/75-invalid_dealloc/02-invalid-dealloc-struct.c b/tests/regression/75-invalid_dealloc/02-invalid-dealloc-struct.c new file mode 100644 index 0000000000..6768103976 --- /dev/null +++ b/tests/regression/75-invalid_dealloc/02-invalid-dealloc-struct.c @@ -0,0 +1,14 @@ +#include + +typedef struct custom_t { + int x; + int y; +} custom_t; + +int main(int argc, char const *argv[]) +{ + custom_t *var; + free(var); //WARN + + return 0; +} diff --git a/tests/regression/75-invalid_dealloc/03-invalid-dealloc-array.c b/tests/regression/75-invalid_dealloc/03-invalid-dealloc-array.c new file mode 100644 index 0000000000..c023b5fc53 --- /dev/null +++ b/tests/regression/75-invalid_dealloc/03-invalid-dealloc-array.c @@ -0,0 +1,25 @@ +#include + +typedef struct custom_t { + int x; + int y; +} custom_t; + +#define MAX_SIZE 5000 + +int main(int argc, char const *argv[]) +{ + custom_t custom_arr[MAX_SIZE]; + free(custom_arr); //WARN + + int int_arr[MAX_SIZE]; + free(int_arr); //WARN + + char char_arr[MAX_SIZE]; + free(char_arr); //WARN + + char char_arr2[1]; + free(char_arr2); //WARN + + return 0; +} diff --git a/tests/regression/75-invalid_dealloc/04-invalid-realloc.c b/tests/regression/75-invalid_dealloc/04-invalid-realloc.c new file mode 100644 index 0000000000..94cbf031c2 --- /dev/null +++ b/tests/regression/75-invalid_dealloc/04-invalid-realloc.c @@ -0,0 +1,25 @@ +#include + +typedef struct custom_t { + int x; + int y; +} custom_t; + +#define MAX_SIZE 5000 + +int main(int argc, char const *argv[]) +{ + custom_t custom_arr[10]; + realloc(custom_arr, MAX_SIZE); //WARN + + int int_arr[100]; + realloc(int_arr, MAX_SIZE); //WARN + + char char_arr[1000]; + realloc(char_arr, MAX_SIZE); //WARN + + char char_arr2[1]; + realloc(char_arr2, MAX_SIZE); //WARN + + return 0; +} From acb1ca83d00ae5f09167e1b30b775a465736ecd1 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 8 Jul 2023 10:59:19 +0200 Subject: [PATCH 0275/1312] Warn once for invalid de-/realloc for entire points-to set This includes the case where the points-to-set is top --- src/analyses/base.ml | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index cdc8e5a7d3..556aa4440e 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1998,18 +1998,14 @@ struct invalidate ~deep:true ~ctx (Analyses.ask_of_ctx ctx) gs st' deep_addrs let check_free_of_non_heap_mem ctx special_fn ptr = - match ctx.ask (Queries.MayPointTo ptr) with - | a when not (Queries.LS.is_top a) -> - let warn_if_not_heap_var special_fn var = - if not (ctx.ask (Queries.IsHeapVar var)) then - M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Free of non-dynamically allocated memory in function %s for pointer %a" special_fn.vname d_exp ptr - in - let pointed_to_vars = - Queries.LS.elements a - |> List.map fst - in - List.iter (warn_if_not_heap_var special_fn) pointed_to_vars - | _ -> () + let points_to_set = ctx.ask (Queries.MayPointTo ptr) in + let exists_non_heap_var = + Queries.LS.elements points_to_set + |> List.map fst + |> List.exists (fun var -> not (ctx.ask (Queries.IsHeapVar var))) + in + if exists_non_heap_var then + M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Free of non-dynamically allocated memory in function %s for pointer %a" special_fn.vname d_exp ptr let special ctx (lv:lval option) (f: varinfo) (args: exp list) = let invalidate_ret_lv st = match lv with From ee80fd1761a7a780e46c2e9af25389bd268af65e Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 8 Jul 2023 11:30:04 +0200 Subject: [PATCH 0276/1312] Handle case when points-to set is top --- src/analyses/base.ml | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 556aa4440e..a4a674cf2d 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1999,13 +1999,17 @@ struct let check_free_of_non_heap_mem ctx special_fn ptr = let points_to_set = ctx.ask (Queries.MayPointTo ptr) in - let exists_non_heap_var = - Queries.LS.elements points_to_set - |> List.map fst - |> List.exists (fun var -> not (ctx.ask (Queries.IsHeapVar var))) - in - if exists_non_heap_var then - M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Free of non-dynamically allocated memory in function %s for pointer %a" special_fn.vname d_exp ptr + begin try + let exists_non_heap_var = + (* elements throws Unsupported if the points-to set is top *) + Queries.LS.elements points_to_set + |> List.map fst + |> List.exists (fun var -> not (ctx.ask (Queries.IsHeapVar var))) + in + if exists_non_heap_var then + M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Free of non-dynamically allocated memory in function %s for pointer %a" special_fn.vname d_exp ptr + with _ -> M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Points-to set for pointer %a in function %s is top. Potential free of non-dynamically allocated memory may occur" d_exp ptr special_fn.vname + end let special ctx (lv:lval option) (f: varinfo) (args: exp list) = let invalidate_ret_lv st = match lv with From 4d5e0dcabe77afa11102f07c50971b12f18b1037 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 8 Jul 2023 16:26:07 +0200 Subject: [PATCH 0277/1312] Rewrite the majority of the logic and checks performed in memOutOfBounds --- src/analyses/memOutOfBounds.ml | 208 +++++++++++++++++++++------------ 1 file changed, 136 insertions(+), 72 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 9648f35610..d96c59e105 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -15,15 +15,12 @@ struct (* HELPER FUNCTIONS *) - let exp_points_to_heap ctx (exp:exp) = - match ctx.ask (Queries.MayPointTo exp) with - | a when not (Queries.LS.is_top a) && not (Queries.LS.mem (dummyFunDec.svar, `NoOffset) a) -> - Queries.LS.elements a - |> List.map fst - |> List.exists (fun x -> ctx.ask (Queries.IsHeapVar x)) - | _ -> false - let lval_points_to_heap ctx (lval:lval) = exp_points_to_heap ctx (mkAddrOf lval) + (* A safe way to call [cilint_to_int] without having to worry about exceptions *) + let cilint_to_int_wrapper i = + try + Some (cilint_to_int i) + with _ -> None let rec exp_contains_a_ptr (exp:exp) = match exp with @@ -47,90 +44,131 @@ struct | StartOf lval -> lval_contains_a_ptr lval and lval_contains_a_ptr (lval:lval) = - let lval_is_of_ptr_type lval = - match typeOfLval lval with - | TPtr _ -> true - | _ -> false - in let (host, offset) = lval in - let host_contains_a_ptr h = - match h with + let host_contains_a_ptr = function | Var v -> isPointerType v.vtype | Mem e -> exp_contains_a_ptr e in - let rec offset_contains_a_ptr o = - match o with + let rec offset_contains_a_ptr = function | NoOffset -> false | Index (e, o) -> exp_contains_a_ptr e || offset_contains_a_ptr o | Field (f, o) -> isPointerType f.ftype || offset_contains_a_ptr o in - lval_is_of_ptr_type lval || host_contains_a_ptr host || offset_contains_a_ptr offset + host_contains_a_ptr host || offset_contains_a_ptr offset - let calc_lval_type_size (lval:lval) = - begin try - let t = typeOfLval lval in - match sizeOf t with - | Const (CInt(i, _, _)) -> Some (cilint_to_int i) - | _ -> None - with _ -> None - end - - let get_ptr_size_for_lval ctx (lval:lval) = - match lval_points_to_heap ctx lval with - | true -> (* We're dealing with a ptr that points to the heap *) - begin match ctx.ask (Queries.BlobSize (mkAddrOf lval)) with - | a when not (Queries.ID.is_top a) -> - begin match Queries.ID.to_int a with - | Some i -> Some (IntOps.BigIntOps.to_int i) - | None -> None - end - | _ -> None + let lval_is_ptr_var (lval:lval) = + let (host, _) = lval in + match host with + | Var v -> isPointerType v.vtype + (* Intuition: If the lval has a Mem host, then it's not a direct ptr which is what we're looking for here *) + | Mem e -> false + + let exp_points_to_heap ctx (exp:exp) = + match ctx.ask (Queries.MayPointTo exp) with + | a when not (Queries.LS.is_top a) && not (Queries.LS.mem (dummyFunDec.svar, `NoOffset) a) -> + Queries.LS.elements a + |> List.map fst + |> List.exists (fun x -> ctx.ask (Queries.IsHeapVar x)) + | _ -> false (* TODO: Is this sound? Maybe not quite. *) + + let get_size_for_heap_ptr ctx (exp:exp) = + (* TODO: + BlobSize always seems to respond with top when it's passed an Lval exp of a ptr var which in turn contains mem allocated via malloc. + Am I doing smth wrong here? + *) + match ctx.ask (Queries.BlobSize exp) with + | a when not (Queries.ID.is_top a) -> + begin match Queries.ID.to_int a with + | Some i -> Some (IntOps.BigIntOps.to_int i) + | None -> None end - (* Assumption here is that if it's not a heap ptr, then it's a stack ptr and the ptr size would be the lval type's size *) - | false -> calc_lval_type_size lval + | _ -> None + + (* TODO: Here we assume that the given exp is a Lval exp *) + let get_size_for_stack_ptr ctx (exp:exp) = + match exp with + | Lval lval -> + if lval_is_ptr_var lval then + let (host, _) = lval in + begin match host with + | Var v -> + begin match sizeOf v.vtype with + | Const (CInt (i, _, _)) -> cilint_to_int_wrapper i + | _ -> None + end + | _ -> None + end + else None + | _ -> None + + let get_ptr_size_for_exp ctx (exp:exp) = + match exp_points_to_heap ctx exp with + (* We're dealing with a ptr that points to the heap *) + | true -> get_size_for_heap_ptr ctx exp + (* Assumption here is that if it doesn't point to the heap, then it points to the stack *) + | false -> get_size_for_stack_ptr ctx exp - let rec get_offset_size ctx offset = - match offset with + (** + * If we get [None], then the offset's size/value is unknown + * In the case [NoOffset], [Some 0] indicates that this offset type simply has value 0 + *) + let rec get_offset_size = function | NoOffset -> Some 0 | Index (e, o) -> - begin match ctx.ask (Queries.EvalInt e) with - | a when not (Queries.ID.is_top a) -> - begin match Queries.ID.to_int a with - | Some i -> - begin match get_offset_size ctx o with - | Some os -> Some (IntOps.BigIntOps.to_int i + os) - | None -> None - end - | None -> None - end + let exp_val = begin match constFold true e with + | Const (CInt (i, _, _)) -> cilint_to_int_wrapper i | _ -> None end - | Field (f, o) -> - let f_size = - begin match sizeOf f.ftype with - | Const (CInt (i, _, _)) -> Some (cilint_to_int i) - | _ -> None - end in - begin match f_size, get_offset_size ctx o with - | Some fs, Some os -> Some (fs + os) - | _ -> None + begin match exp_val, get_offset_size o with + | Some ei, Some oi -> Some (ei + oi) + | _, _ -> None + end + | Field (f, o) -> + begin match get_offset_size o, sizeOf f.ftype with + | Some oi, Const (CInt (i, _, _)) -> + begin match cilint_to_int_wrapper i with + | Some i -> Some (oi + i) + | None -> None + end + | _, _ -> None end - let check_lval_for_oob_access ctx (lval:lval) = + let rec check_lval_for_oob_access ctx (lval:lval) = match lval_contains_a_ptr lval with - | false -> () + | false -> () (* Nothing to do here *) | true -> - let (_, offset) = lval in - begin match get_ptr_size_for_lval ctx lval, get_offset_size ctx offset with - | _, None -> M.warn "Offset unknown, potential out-of-bounds access for lval %a" CilType.Lval.pretty lval - | None, _ -> M.warn "Pointer size unknown, potential out-of-bounds access for lval %a" CilType.Lval.pretty lval - | Some pi, Some oi -> - if oi > pi then - M.warn "Must out-of-bounds memory access: Offset size (%d) is larger than pointer's memory size (%d) for lval %a" oi pi CilType.Lval.pretty lval - end + let (host, offset) = lval in + match host, get_offset_size offset with + | _, None -> M.warn "Offset size for lval %a not known. May have a memory out-of-bounds access" CilType.Lval.pretty lval + | Var v, Some oi -> + begin match sizeOf v.vtype with + | Const (CInt (i, _, _)) -> + begin match cilint_to_int_wrapper i with + | Some i -> + if i < oi then + M.warn "Offset bigger than var type's size for lval %a. A memory out-of-bounds access must occur" CilType.Lval.pretty lval + | _ -> M.warn "Unknown size of var %a for lval %a. A memory out-of-bounds access might occur" CilType.Varinfo.pretty v CilType.Lval.pretty lval + end + | _ -> M.warn "Unknown size of var %a for lval %a. A memory out-of-bounds access might occur" CilType.Varinfo.pretty v CilType.Lval.pretty lval + end + | Mem e, Some oi -> + check_exp_for_oob_access ctx e; + (* TODO: + * Not sure if we actually need these checks below. + * They never seem to apply + * I.e., for ptrs, it always seems to be the case that we have a binop which adds the offset to the ptr + instead of having the offset represented as an offset of the lval + * For this reason, the checks below are currently commented out + *) + (* begin match get_ptr_size_for_exp ctx e with + | Some ei -> + if ei < oi then + M.warn "Offset bigger than size of pointer memory, denoted by expression %a in lval %a" d_exp e CilType.Lval.pretty lval + | _ -> M.warn "Unknown size of pointer memory, denoted by exp %a for lval %a" d_exp e CilType.Lval.pretty lval + end *) - let rec check_exp_for_oob_access ctx (exp:exp) = + and check_exp_for_oob_access ctx (exp:exp) = match exp with | Const _ | SizeOf _ @@ -143,7 +181,8 @@ struct | AlignOfE e | UnOp (_, e, _) | CastE (_, e) -> check_exp_for_oob_access ctx e - | BinOp (_, e1, e2, _) -> + | BinOp (bop, e1, e2, _) -> + check_binop_exp ctx bop e1 e2; check_exp_for_oob_access ctx e1; check_exp_for_oob_access ctx e2 | Question (e1, e2, e3, _) -> @@ -154,10 +193,35 @@ struct | StartOf lval | AddrOf lval -> check_lval_for_oob_access ctx lval + and check_binop_exp ctx (binop:binop) (e1:exp) (e2:exp) = + match binop with + | PlusPI + | IndexPI + | MinusPI -> + let ptr_size = get_ptr_size_for_exp ctx e1 in + let offset_size = eval_ptr_offset_in_binop e2 in + begin match ptr_size, offset_size with + | Some pi, Some oi -> + if pi < oi then + M.warn "Pointer size in expression %a %a %a is smaller than offset for pointer arithmetic" d_exp e1 CilType.Binop.pretty binop d_exp e2 + | None, _ -> M.warn "Pointer (%a) size in expression %a %a %a not known" d_exp e1 d_exp e1 CilType.Binop.pretty binop d_exp e2 + | _, None -> M.warn "Operand value for pointer arithmetic in expression %a %a %a not known" d_exp e1 CilType.Binop.pretty binop d_exp e2 + end + | _ -> () + + and eval_ptr_offset_in_binop (exp:exp) = + match constFold true exp with + | Const (CInt (i, _, _)) -> cilint_to_int_wrapper i + | _ -> None (* TODO: Maybe try to also Eval the exp via Queries and not rely only on constFold *) + (* TRANSFER FUNCTIONS *) let assign ctx (lval:lval) (rval:exp) : D.t = + let (host,_) = lval in + match host with + | Mem e -> ignore (Pretty.printf "Lval host is Mem e and e is %a\n" d_plainexp e); + | _ -> (); check_lval_for_oob_access ctx lval; check_exp_for_oob_access ctx rval; ctx.local From 098501b62a5f87d764eaf33a9468d576bbcb1435 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 8 Jul 2023 16:28:18 +0200 Subject: [PATCH 0278/1312] Remove printf calls --- src/analyses/memOutOfBounds.ml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index d96c59e105..58da08c7bc 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -218,10 +218,6 @@ struct (* TRANSFER FUNCTIONS *) let assign ctx (lval:lval) (rval:exp) : D.t = - let (host,_) = lval in - match host with - | Mem e -> ignore (Pretty.printf "Lval host is Mem e and e is %a\n" d_plainexp e); - | _ -> (); check_lval_for_oob_access ctx lval; check_exp_for_oob_access ctx rval; ctx.local From 6496c61ea12a9c686f3b09c2609c2ea88e9b0c0d Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 8 Jul 2023 16:30:43 +0200 Subject: [PATCH 0279/1312] Improve warning messages a bit --- src/analyses/memOutOfBounds.ml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 58da08c7bc..d11d876f5b 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -140,7 +140,7 @@ struct | true -> let (host, offset) = lval in match host, get_offset_size offset with - | _, None -> M.warn "Offset size for lval %a not known. May have a memory out-of-bounds access" CilType.Lval.pretty lval + | _, None -> M.warn "Offset size for lval %a not known. A memory out-of-bounds access may occur" CilType.Lval.pretty lval | Var v, Some oi -> begin match sizeOf v.vtype with | Const (CInt (i, _, _)) -> @@ -203,9 +203,9 @@ struct begin match ptr_size, offset_size with | Some pi, Some oi -> if pi < oi then - M.warn "Pointer size in expression %a %a %a is smaller than offset for pointer arithmetic" d_exp e1 CilType.Binop.pretty binop d_exp e2 - | None, _ -> M.warn "Pointer (%a) size in expression %a %a %a not known" d_exp e1 d_exp e1 CilType.Binop.pretty binop d_exp e2 - | _, None -> M.warn "Operand value for pointer arithmetic in expression %a %a %a not known" d_exp e1 CilType.Binop.pretty binop d_exp e2 + M.warn "Pointer size in expression %a %a %a is smaller than offset for pointer arithmetic. Memory out-of-bounds access must occur" d_exp e1 CilType.Binop.pretty binop d_exp e2 + | None, _ -> M.warn "Pointer (%a) size in expression %a %a %a not known. Memory out-of-bounds access might occur" d_exp e1 d_exp e1 CilType.Binop.pretty binop d_exp e2 + | _, None -> M.warn "Operand value for pointer arithmetic in expression %a %a %a not known. Memory out-of-bounds access might occur" d_exp e1 CilType.Binop.pretty binop d_exp e2 end | _ -> () From a796a0b4963cfae32177d33af27f1e49935ab234 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 8 Jul 2023 16:37:17 +0200 Subject: [PATCH 0280/1312] Add message category for memory OOB access --- src/analyses/memOutOfBounds.ml | 17 ++++++++++------- src/util/messageCategory.ml | 5 +++++ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index d11d876f5b..7ea11e8ef1 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -1,5 +1,6 @@ open GoblintCil open Analyses +open MessageCategory module Spec = struct @@ -135,22 +136,23 @@ struct end let rec check_lval_for_oob_access ctx (lval:lval) = + let undefined_behavior = Undefined MemoryOutOfBoundsAccess in match lval_contains_a_ptr lval with | false -> () (* Nothing to do here *) | true -> let (host, offset) = lval in match host, get_offset_size offset with - | _, None -> M.warn "Offset size for lval %a not known. A memory out-of-bounds access may occur" CilType.Lval.pretty lval + | _, None -> M.warn ~category:(Behavior undefined_behavior) "Offset size for lval %a not known. A memory out-of-bounds access may occur" CilType.Lval.pretty lval | Var v, Some oi -> begin match sizeOf v.vtype with | Const (CInt (i, _, _)) -> begin match cilint_to_int_wrapper i with | Some i -> if i < oi then - M.warn "Offset bigger than var type's size for lval %a. A memory out-of-bounds access must occur" CilType.Lval.pretty lval - | _ -> M.warn "Unknown size of var %a for lval %a. A memory out-of-bounds access might occur" CilType.Varinfo.pretty v CilType.Lval.pretty lval + M.warn ~category:(Behavior undefined_behavior) "Offset bigger than var type's size for lval %a. A memory out-of-bounds access must occur" CilType.Lval.pretty lval + | _ -> M.warn ~category:(Behavior undefined_behavior) "Unknown size of var %a for lval %a. A memory out-of-bounds access might occur" CilType.Varinfo.pretty v CilType.Lval.pretty lval end - | _ -> M.warn "Unknown size of var %a for lval %a. A memory out-of-bounds access might occur" CilType.Varinfo.pretty v CilType.Lval.pretty lval + | _ -> M.warn ~category:(Behavior undefined_behavior) "Unknown size of var %a for lval %a. A memory out-of-bounds access might occur" CilType.Varinfo.pretty v CilType.Lval.pretty lval end | Mem e, Some oi -> check_exp_for_oob_access ctx e; @@ -194,6 +196,7 @@ struct | AddrOf lval -> check_lval_for_oob_access ctx lval and check_binop_exp ctx (binop:binop) (e1:exp) (e2:exp) = + let undefined_behavior = Undefined MemoryOutOfBoundsAccess in match binop with | PlusPI | IndexPI @@ -203,9 +206,9 @@ struct begin match ptr_size, offset_size with | Some pi, Some oi -> if pi < oi then - M.warn "Pointer size in expression %a %a %a is smaller than offset for pointer arithmetic. Memory out-of-bounds access must occur" d_exp e1 CilType.Binop.pretty binop d_exp e2 - | None, _ -> M.warn "Pointer (%a) size in expression %a %a %a not known. Memory out-of-bounds access might occur" d_exp e1 d_exp e1 CilType.Binop.pretty binop d_exp e2 - | _, None -> M.warn "Operand value for pointer arithmetic in expression %a %a %a not known. Memory out-of-bounds access might occur" d_exp e1 CilType.Binop.pretty binop d_exp e2 + M.warn ~category:(Behavior undefined_behavior) "Pointer size in expression %a %a %a is smaller than offset for pointer arithmetic. Memory out-of-bounds access must occur" d_exp e1 CilType.Binop.pretty binop d_exp e2 + | None, _ -> M.warn ~category:(Behavior undefined_behavior) "Pointer (%a) size in expression %a %a %a not known. Memory out-of-bounds access might occur" d_exp e1 d_exp e1 CilType.Binop.pretty binop d_exp e2 + | _, None -> M.warn ~category:(Behavior undefined_behavior) "Operand value for pointer arithmetic in expression %a %a %a not known. Memory out-of-bounds access might occur" d_exp e1 CilType.Binop.pretty binop d_exp e2 end | _ -> () diff --git a/src/util/messageCategory.ml b/src/util/messageCategory.ml index ef8ee5d6a9..8190f79294 100644 --- a/src/util/messageCategory.ml +++ b/src/util/messageCategory.ml @@ -11,6 +11,7 @@ type undefined_behavior = | ArrayOutOfBounds of array_oob | NullPointerDereference | UseAfterFree + | MemoryOutOfBoundsAccess | Uninitialized | Other [@@deriving eq, ord, hash] @@ -62,6 +63,7 @@ struct let array_out_of_bounds e: category = create @@ ArrayOutOfBounds e let nullpointer_dereference: category = create @@ NullPointerDereference let use_after_free: category = create @@ UseAfterFree + let memory_out_of_bounds_access: category = create @@ MemoryOutOfBoundsAccess let uninitialized: category = create @@ Uninitialized let other: category = create @@ Other @@ -97,6 +99,7 @@ struct | "array_out_of_bounds" -> ArrayOutOfBounds.from_string_list t | "nullpointer_dereference" -> nullpointer_dereference | "use_after_free" -> use_after_free + | "memory_out_of_bounds_access" -> memory_out_of_bounds_access | "uninitialized" -> uninitialized | "other" -> other | _ -> Unknown @@ -106,6 +109,7 @@ struct | ArrayOutOfBounds e -> "ArrayOutOfBounds" :: ArrayOutOfBounds.path_show e | NullPointerDereference -> ["NullPointerDereference"] | UseAfterFree -> ["UseAfterFree"] + | MemoryOutOfBoundsAccess -> ["MemoryOutOfBoundsAccess"] | Uninitialized -> ["Uninitialized"] | Other -> ["Other"] end @@ -214,6 +218,7 @@ let behaviorName = function |Undefined u -> match u with |NullPointerDereference -> "NullPointerDereference" |UseAfterFree -> "UseAfterFree" + |MemoryOutOfBoundsAccess -> "MemoryOutOfBoundsAccess" |Uninitialized -> "Uninitialized" |Other -> "Other" | ArrayOutOfBounds aob -> match aob with From 84e80b1b7f97a0077dae1b63f974c8b0c62d8108 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 8 Jul 2023 16:42:08 +0200 Subject: [PATCH 0281/1312] Add CWE number 823 for memory OOB access warnings --- src/analyses/memOutOfBounds.ml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 7ea11e8ef1..ac2b909d6a 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -137,6 +137,7 @@ struct let rec check_lval_for_oob_access ctx (lval:lval) = let undefined_behavior = Undefined MemoryOutOfBoundsAccess in + let cwe_number = 823 in match lval_contains_a_ptr lval with | false -> () (* Nothing to do here *) | true -> @@ -149,10 +150,10 @@ struct begin match cilint_to_int_wrapper i with | Some i -> if i < oi then - M.warn ~category:(Behavior undefined_behavior) "Offset bigger than var type's size for lval %a. A memory out-of-bounds access must occur" CilType.Lval.pretty lval - | _ -> M.warn ~category:(Behavior undefined_behavior) "Unknown size of var %a for lval %a. A memory out-of-bounds access might occur" CilType.Varinfo.pretty v CilType.Lval.pretty lval + M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Offset bigger than var type's size for lval %a. A memory out-of-bounds access must occur" CilType.Lval.pretty lval + | _ -> M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Unknown size of var %a for lval %a. A memory out-of-bounds access might occur" CilType.Varinfo.pretty v CilType.Lval.pretty lval end - | _ -> M.warn ~category:(Behavior undefined_behavior) "Unknown size of var %a for lval %a. A memory out-of-bounds access might occur" CilType.Varinfo.pretty v CilType.Lval.pretty lval + | _ -> M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Unknown size of var %a for lval %a. A memory out-of-bounds access might occur" CilType.Varinfo.pretty v CilType.Lval.pretty lval end | Mem e, Some oi -> check_exp_for_oob_access ctx e; @@ -197,6 +198,7 @@ struct and check_binop_exp ctx (binop:binop) (e1:exp) (e2:exp) = let undefined_behavior = Undefined MemoryOutOfBoundsAccess in + let cwe_number = 823 in match binop with | PlusPI | IndexPI @@ -206,9 +208,9 @@ struct begin match ptr_size, offset_size with | Some pi, Some oi -> if pi < oi then - M.warn ~category:(Behavior undefined_behavior) "Pointer size in expression %a %a %a is smaller than offset for pointer arithmetic. Memory out-of-bounds access must occur" d_exp e1 CilType.Binop.pretty binop d_exp e2 - | None, _ -> M.warn ~category:(Behavior undefined_behavior) "Pointer (%a) size in expression %a %a %a not known. Memory out-of-bounds access might occur" d_exp e1 d_exp e1 CilType.Binop.pretty binop d_exp e2 - | _, None -> M.warn ~category:(Behavior undefined_behavior) "Operand value for pointer arithmetic in expression %a %a %a not known. Memory out-of-bounds access might occur" d_exp e1 CilType.Binop.pretty binop d_exp e2 + M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Pointer size in expression %a %a %a is smaller than offset for pointer arithmetic. Memory out-of-bounds access must occur" d_exp e1 CilType.Binop.pretty binop d_exp e2 + | None, _ -> M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Pointer (%a) size in expression %a %a %a not known. Memory out-of-bounds access might occur" d_exp e1 d_exp e1 CilType.Binop.pretty binop d_exp e2 + | _, None -> M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Operand value for pointer arithmetic in expression %a %a %a not known. Memory out-of-bounds access might occur" d_exp e1 CilType.Binop.pretty binop d_exp e2 end | _ -> () From a07b6903ada4aed962efdba4619657d41273bfe8 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 8 Jul 2023 16:59:44 +0200 Subject: [PATCH 0282/1312] Add a global variable that indicates whether an invalid pointer deref happened --- src/analyses/memOutOfBounds.ml | 24 +++++++++++++++++++----- src/util/goblintutil.ml | 3 +++ 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index ac2b909d6a..415934b52d 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -2,6 +2,8 @@ open GoblintCil open Analyses open MessageCategory +module GU = Goblintutil + module Spec = struct include Analyses.IdentitySpec @@ -143,17 +145,24 @@ struct | true -> let (host, offset) = lval in match host, get_offset_size offset with - | _, None -> M.warn ~category:(Behavior undefined_behavior) "Offset size for lval %a not known. A memory out-of-bounds access may occur" CilType.Lval.pretty lval + | _, None -> + GU.may_invalid_deref := true; + M.warn ~category:(Behavior undefined_behavior) "Offset size for lval %a not known. A memory out-of-bounds access may occur" CilType.Lval.pretty lval | Var v, Some oi -> begin match sizeOf v.vtype with | Const (CInt (i, _, _)) -> begin match cilint_to_int_wrapper i with | Some i -> if i < oi then + GU.may_invalid_deref := true; M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Offset bigger than var type's size for lval %a. A memory out-of-bounds access must occur" CilType.Lval.pretty lval - | _ -> M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Unknown size of var %a for lval %a. A memory out-of-bounds access might occur" CilType.Varinfo.pretty v CilType.Lval.pretty lval + | _ -> + GU.may_invalid_deref := true; + M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Unknown size of var %a for lval %a. A memory out-of-bounds access might occur" CilType.Varinfo.pretty v CilType.Lval.pretty lval end - | _ -> M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Unknown size of var %a for lval %a. A memory out-of-bounds access might occur" CilType.Varinfo.pretty v CilType.Lval.pretty lval + | _ -> + GU.may_invalid_deref := true; + M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Unknown size of var %a for lval %a. A memory out-of-bounds access might occur" CilType.Varinfo.pretty v CilType.Lval.pretty lval end | Mem e, Some oi -> check_exp_for_oob_access ctx e; @@ -208,9 +217,14 @@ struct begin match ptr_size, offset_size with | Some pi, Some oi -> if pi < oi then + GU.may_invalid_deref := true; M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Pointer size in expression %a %a %a is smaller than offset for pointer arithmetic. Memory out-of-bounds access must occur" d_exp e1 CilType.Binop.pretty binop d_exp e2 - | None, _ -> M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Pointer (%a) size in expression %a %a %a not known. Memory out-of-bounds access might occur" d_exp e1 d_exp e1 CilType.Binop.pretty binop d_exp e2 - | _, None -> M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Operand value for pointer arithmetic in expression %a %a %a not known. Memory out-of-bounds access might occur" d_exp e1 CilType.Binop.pretty binop d_exp e2 + | None, _ -> + GU.may_invalid_deref := true; + M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Pointer (%a) size in expression %a %a %a not known. Memory out-of-bounds access might occur" d_exp e1 d_exp e1 CilType.Binop.pretty binop d_exp e2 + | _, None -> + GU.may_invalid_deref := true; + M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Operand value for pointer arithmetic in expression %a %a %a not known. Memory out-of-bounds access might occur" d_exp e1 CilType.Binop.pretty binop d_exp e2 end | _ -> () diff --git a/src/util/goblintutil.ml b/src/util/goblintutil.ml index 2c49395915..ecaa38f338 100644 --- a/src/util/goblintutil.ml +++ b/src/util/goblintutil.ml @@ -14,6 +14,9 @@ let should_warn = ref false (** Whether signed overflow or underflow happened *) let svcomp_may_overflow = ref false +(** Whether an invalid pointer dereference happened *) +let may_invalid_deref = ref false + (** The file where everything is output *) let out = ref stdout From dbe3684a53f6a70367289673a95c9e2529f93b67 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Sun, 9 Jul 2023 13:53:55 +0200 Subject: [PATCH 0283/1312] inlined Groupable C to Map --- runningGob.sh | 11 ++++++++--- src/framework/analyses.ml | 22 ++++++++++------------ 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/runningGob.sh b/runningGob.sh index 11173bee1f..61dbad53b6 100755 --- a/runningGob.sh +++ b/runningGob.sh @@ -8,7 +8,12 @@ options_apron="--set ana.activated[+] apron --enable ana.int.interval --set ana. options_signs="--set "ana.activated[+]" signs --enable warn.debug" options_term="--set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra" -cfile_loops="tests/regression/74-loop_termination/03-nested-loop-terminating.c" +cfile_loop30="tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c" +cfile_loop26="tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c" +cfile_loop28="tests/regression/74-loop_termination/28-do-while-continue-terminating.c" +cfile_loop7="tests/regression/74-loop_termination/07-nested-for-loop-terminating.c" +cfile_loop5="tests/regression/74-loop_termination/05-for-loop-terminating.c" +cfile_loop1="tests/regression/74-loop_termination/01-simple-loop-terminating.c" cfile_signs="tests/regression/99-tutorials/01-first.c" cfile_goto="tests/incremental/02-cfg-comparison/01-added-return-stmt.c" @@ -17,8 +22,8 @@ cfile_goto="tests/incremental/02-cfg-comparison/01-added-return-stmt.c" #./goblint $cfile_loops $options_apron --html # run analysis, write cil output to file and enable visualization via html -#./goblint -v $cfile_loops $options_term --enable justcil > output.txt -./goblint $cfile_loops $options_term --html +./goblint $cfile_loop30 $options_term --enable justcil > output.txt +./goblint -v $cfile_loop30 $options_term --html # set up server to see visualizatino python3 -m http.server --directory result 8080 diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 9f25a67a0d..faf7c456ca 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -130,20 +130,18 @@ struct BatPrintf.fprintf f "\n\n" end - (* Make the given module Goupable*) - module C_Printable (C: Printable.S) = - struct - include Printable.Std (* To make it Groupable *) - include C - let printXml f c = BatPrintf.fprintf f - "\n - callee_context\n%a\n\n - " printXml c - end - module CMap = struct - include MapDomain.MapBot (C_Printable (C)) (CSet) + include MapDomain.MapBot ( + struct + include Printable.Std (* To make it Groupable *) + include C + let printXml f c = BatPrintf.fprintf f + "\n + callee_context\n%a\n\n + " printXml c + end + ) (CSet) let printXml f c = BatPrintf.fprintf f " ContextTupleMap\n %a\n\n From 68bf1031e9efef23fb5605c79ae8473207b55bda Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Sun, 9 Jul 2023 17:05:53 +0200 Subject: [PATCH 0284/1312] Rename query MustTermProg to MustTermAllLoops I find the name MustTermProg confusing because it does not consider recursion. --- src/analyses/loopTermination.ml | 2 +- src/domains/queries.ml | 10 +++++----- src/framework/constraints.ml | 16 ++++++++-------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/analyses/loopTermination.ml b/src/analyses/loopTermination.ml index 921aef28ee..38823d3039 100644 --- a/src/analyses/loopTermination.ml +++ b/src/analyses/loopTermination.ml @@ -106,7 +106,7 @@ struct Some b -> b | None -> false) && must_be_single_threaded_since_start ctx - | Queries.MustTermProg -> + | Queries.MustTermAllLoops -> G.for_all (fun _ term_info -> term_info) (ctx.global ()) && no_upjumping_gotos () && must_be_single_threaded_since_start ctx diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 2810f07342..8ff37b6d2c 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -118,7 +118,7 @@ type _ t = | MayBeTainted: LS.t t | MayBeModifiedSinceSetjmp: JmpBufDomain.BufferEntry.t -> VS.t t | MustTermLoop: stmt -> MustBool.t t - | MustTermProg: MustBool.t t + | MustTermAllLoops: MustBool.t t | IsEverMultiThreaded: MayBool.t t type 'a result = 'a @@ -185,7 +185,7 @@ struct | MayBeTainted -> (module LS) | MayBeModifiedSinceSetjmp _ -> (module VS) | MustTermLoop _ -> (module MustBool) - | MustTermProg -> (module MustBool) + | MustTermAllLoops -> (module MustBool) | IsEverMultiThreaded -> (module MayBool) (** Get bottom result for query. *) @@ -251,7 +251,7 @@ struct | MayBeTainted -> LS.top () | MayBeModifiedSinceSetjmp _ -> VS.top () | MustTermLoop _ -> MustBool.top () - | MustTermProg -> MustBool.top () + | MustTermAllLoops -> MustBool.top () | IsEverMultiThreaded -> MayBool.top () end @@ -314,7 +314,7 @@ struct | Any ThreadCreateIndexedNode -> 51 | Any ThreadsJoinedCleanly -> 52 | Any (MustTermLoop _) -> 53 - | Any MustTermProg -> 54 + | Any MustTermAllLoops -> 54 | Any IsEverMultiThreaded -> 55 let rec compare a b = @@ -459,7 +459,7 @@ struct | Any DYojson -> Pretty.dprintf "DYojson" | Any MayBeModifiedSinceSetjmp buf -> Pretty.dprintf "MayBeModifiedSinceSetjmp %a" JmpBufDomain.BufferEntry.pretty buf | Any (MustTermLoop s) -> Pretty.dprintf "MustTermLoop %a" CilType.Stmt.pretty s - | Any MustTermProg -> Pretty.dprintf "MustTermProg" + | Any MustTermAllLoops -> Pretty.dprintf "MustTermAllLoops" | Any IsEverMultiThreaded -> Pretty.dprintf "IsEverMultiThreaded" end diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 55763c5852..27edc4f520 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1701,13 +1701,13 @@ module RecursionTermLifter (S: Spec) and module C = S.C = (* two global invariants: - - V -> G - Needed to store the previously built global invariants - - fundec -> Map (S.C) (Set (fundec * S.C)) - The second global invariant maps from the callee fundec to a map, containing the callee context and the caller fundec and context. - This structure therefore stores the context-sensitive call graph. - For example: - let the function f in context c call function g in context c'. + - V -> G + Needed to store the previously built global invariants + - fundec -> Map (S.C) (Set (fundec * S.C)) + The second global invariant maps from the callee fundec to a map, containing the callee context and the caller fundec and context. + This structure therefore stores the context-sensitive call graph. + For example: + let the function f in context c call function g in context c'. In the global invariant structure it would be stored like this: g -> {c' -> {(f, c)}} *) @@ -1769,7 +1769,7 @@ struct match q with | WarnGlobal v -> (* check result of loop analysis *) - if not (ctx.ask Queries.MustTermProg) then + if not (ctx.ask Queries.MustTermAllLoops) then (AnalysisState.svcomp_may_not_terminate := true; let msgs = [ From fbc2966201c3af0648d36c71b1e09f7597a7a791 Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Sun, 9 Jul 2023 18:16:57 +0200 Subject: [PATCH 0285/1312] Test moved and renamed --- .../01-simple-loop-terminating.c | 0 .../02-simple-loop-nonterminating.c | 0 .../03-nested-loop-terminating.c | 0 .../04-nested-loop-nonterminating.c | 0 .../05-for-loop-terminating.c | 0 .../06-for-loop-nonterminating.c | 0 .../07-nested-for-loop-terminating.c | 0 .../08-nested-for-loop-nonterminating.c | 0 .../09-complex-for-loop-terminating.c | 0 .../10-complex-loop-terminating.c | 0 .../11-loopless-termination.c | 0 .../12-do-while-instant-terminating.c | 0 .../13-do-while-terminating.c | 0 .../14-do-while-nonterminating.c | 0 .../15-complex-loop-combination-terminating.c | 0 .../16-nested-loop-nontrivial-nonterminating.c | 0 .../{74-loop_termination => 75-termination}/17-goto-terminating.c | 0 .../18-goto-nonterminating.c | 0 .../{74-loop_termination => 75-termination}/19-rand-terminating.c | 0 .../20-rand-nonterminating.c | 0 .../21-no-exit-on-rand-unproofable.c | 0 .../22-exit-on-rand-unproofable.c | 0 .../23-exit-on-rand-terminating.c | 0 .../24-upjumping-goto-loopless-terminating.c | 0 .../25-leave-loop-goto-terminating.c | 0 .../26-enter-loop-goto-terminating.c | 0 .../27-upjumping-goto-nonterminating.c | 0 .../28-do-while-continue-terminating.c | 0 .../29-do-while-continue-nonterminating.c | 0 .../30-goto-out-of-inner-loop-terminating.c | 0 .../31-goto-out-of-inner-loop-nonterminating.c | 0 .../32-multithread-terminating.c | 0 .../33-multithread-nonterminating.c | 0 .../34-nested-for-loop-nonterminating.c | 0 .../35-goto-out-of-inner-loop-with-print-terminating.c | 0 .../36-recursion-terminating.c} | 0 .../37-recursion-nonterminating.c} | 0 .../38-recursion-nested-terminating.c} | 0 .../39-recursion-nested-nonterminating.c} | 0 39 files changed, 0 insertions(+), 0 deletions(-) rename tests/regression/{74-loop_termination => 75-termination}/01-simple-loop-terminating.c (100%) rename tests/regression/{74-loop_termination => 75-termination}/02-simple-loop-nonterminating.c (100%) rename tests/regression/{74-loop_termination => 75-termination}/03-nested-loop-terminating.c (100%) rename tests/regression/{74-loop_termination => 75-termination}/04-nested-loop-nonterminating.c (100%) rename tests/regression/{74-loop_termination => 75-termination}/05-for-loop-terminating.c (100%) rename tests/regression/{74-loop_termination => 75-termination}/06-for-loop-nonterminating.c (100%) rename tests/regression/{74-loop_termination => 75-termination}/07-nested-for-loop-terminating.c (100%) rename tests/regression/{74-loop_termination => 75-termination}/08-nested-for-loop-nonterminating.c (100%) rename tests/regression/{74-loop_termination => 75-termination}/09-complex-for-loop-terminating.c (100%) rename tests/regression/{74-loop_termination => 75-termination}/10-complex-loop-terminating.c (100%) rename tests/regression/{74-loop_termination => 75-termination}/11-loopless-termination.c (100%) rename tests/regression/{74-loop_termination => 75-termination}/12-do-while-instant-terminating.c (100%) rename tests/regression/{74-loop_termination => 75-termination}/13-do-while-terminating.c (100%) rename tests/regression/{74-loop_termination => 75-termination}/14-do-while-nonterminating.c (100%) rename tests/regression/{74-loop_termination => 75-termination}/15-complex-loop-combination-terminating.c (100%) rename tests/regression/{74-loop_termination => 75-termination}/16-nested-loop-nontrivial-nonterminating.c (100%) rename tests/regression/{74-loop_termination => 75-termination}/17-goto-terminating.c (100%) rename tests/regression/{74-loop_termination => 75-termination}/18-goto-nonterminating.c (100%) rename tests/regression/{74-loop_termination => 75-termination}/19-rand-terminating.c (100%) rename tests/regression/{74-loop_termination => 75-termination}/20-rand-nonterminating.c (100%) rename tests/regression/{74-loop_termination => 75-termination}/21-no-exit-on-rand-unproofable.c (100%) rename tests/regression/{74-loop_termination => 75-termination}/22-exit-on-rand-unproofable.c (100%) rename tests/regression/{74-loop_termination => 75-termination}/23-exit-on-rand-terminating.c (100%) rename tests/regression/{74-loop_termination => 75-termination}/24-upjumping-goto-loopless-terminating.c (100%) rename tests/regression/{74-loop_termination => 75-termination}/25-leave-loop-goto-terminating.c (100%) rename tests/regression/{74-loop_termination => 75-termination}/26-enter-loop-goto-terminating.c (100%) rename tests/regression/{74-loop_termination => 75-termination}/27-upjumping-goto-nonterminating.c (100%) rename tests/regression/{74-loop_termination => 75-termination}/28-do-while-continue-terminating.c (100%) rename tests/regression/{74-loop_termination => 75-termination}/29-do-while-continue-nonterminating.c (100%) rename tests/regression/{74-loop_termination => 75-termination}/30-goto-out-of-inner-loop-terminating.c (100%) rename tests/regression/{74-loop_termination => 75-termination}/31-goto-out-of-inner-loop-nonterminating.c (100%) rename tests/regression/{74-loop_termination => 75-termination}/32-multithread-terminating.c (100%) rename tests/regression/{74-loop_termination => 75-termination}/33-multithread-nonterminating.c (100%) rename tests/regression/{74-loop_termination => 75-termination}/34-nested-for-loop-nonterminating.c (100%) rename tests/regression/{74-loop_termination => 75-termination}/35-goto-out-of-inner-loop-with-print-terminating.c (100%) rename tests/regression/{75-recursion_termination/01-simple-terminating.c => 75-termination/36-recursion-terminating.c} (100%) rename tests/regression/{75-recursion_termination/02-simple-nonterminating.c => 75-termination/37-recursion-nonterminating.c} (100%) rename tests/regression/{75-recursion_termination/03-nested-terminating.c => 75-termination/38-recursion-nested-terminating.c} (100%) rename tests/regression/{75-recursion_termination/04-nested-nonterminating.c => 75-termination/39-recursion-nested-nonterminating.c} (100%) diff --git a/tests/regression/74-loop_termination/01-simple-loop-terminating.c b/tests/regression/75-termination/01-simple-loop-terminating.c similarity index 100% rename from tests/regression/74-loop_termination/01-simple-loop-terminating.c rename to tests/regression/75-termination/01-simple-loop-terminating.c diff --git a/tests/regression/74-loop_termination/02-simple-loop-nonterminating.c b/tests/regression/75-termination/02-simple-loop-nonterminating.c similarity index 100% rename from tests/regression/74-loop_termination/02-simple-loop-nonterminating.c rename to tests/regression/75-termination/02-simple-loop-nonterminating.c diff --git a/tests/regression/74-loop_termination/03-nested-loop-terminating.c b/tests/regression/75-termination/03-nested-loop-terminating.c similarity index 100% rename from tests/regression/74-loop_termination/03-nested-loop-terminating.c rename to tests/regression/75-termination/03-nested-loop-terminating.c diff --git a/tests/regression/74-loop_termination/04-nested-loop-nonterminating.c b/tests/regression/75-termination/04-nested-loop-nonterminating.c similarity index 100% rename from tests/regression/74-loop_termination/04-nested-loop-nonterminating.c rename to tests/regression/75-termination/04-nested-loop-nonterminating.c diff --git a/tests/regression/74-loop_termination/05-for-loop-terminating.c b/tests/regression/75-termination/05-for-loop-terminating.c similarity index 100% rename from tests/regression/74-loop_termination/05-for-loop-terminating.c rename to tests/regression/75-termination/05-for-loop-terminating.c diff --git a/tests/regression/74-loop_termination/06-for-loop-nonterminating.c b/tests/regression/75-termination/06-for-loop-nonterminating.c similarity index 100% rename from tests/regression/74-loop_termination/06-for-loop-nonterminating.c rename to tests/regression/75-termination/06-for-loop-nonterminating.c diff --git a/tests/regression/74-loop_termination/07-nested-for-loop-terminating.c b/tests/regression/75-termination/07-nested-for-loop-terminating.c similarity index 100% rename from tests/regression/74-loop_termination/07-nested-for-loop-terminating.c rename to tests/regression/75-termination/07-nested-for-loop-terminating.c diff --git a/tests/regression/74-loop_termination/08-nested-for-loop-nonterminating.c b/tests/regression/75-termination/08-nested-for-loop-nonterminating.c similarity index 100% rename from tests/regression/74-loop_termination/08-nested-for-loop-nonterminating.c rename to tests/regression/75-termination/08-nested-for-loop-nonterminating.c diff --git a/tests/regression/74-loop_termination/09-complex-for-loop-terminating.c b/tests/regression/75-termination/09-complex-for-loop-terminating.c similarity index 100% rename from tests/regression/74-loop_termination/09-complex-for-loop-terminating.c rename to tests/regression/75-termination/09-complex-for-loop-terminating.c diff --git a/tests/regression/74-loop_termination/10-complex-loop-terminating.c b/tests/regression/75-termination/10-complex-loop-terminating.c similarity index 100% rename from tests/regression/74-loop_termination/10-complex-loop-terminating.c rename to tests/regression/75-termination/10-complex-loop-terminating.c diff --git a/tests/regression/74-loop_termination/11-loopless-termination.c b/tests/regression/75-termination/11-loopless-termination.c similarity index 100% rename from tests/regression/74-loop_termination/11-loopless-termination.c rename to tests/regression/75-termination/11-loopless-termination.c diff --git a/tests/regression/74-loop_termination/12-do-while-instant-terminating.c b/tests/regression/75-termination/12-do-while-instant-terminating.c similarity index 100% rename from tests/regression/74-loop_termination/12-do-while-instant-terminating.c rename to tests/regression/75-termination/12-do-while-instant-terminating.c diff --git a/tests/regression/74-loop_termination/13-do-while-terminating.c b/tests/regression/75-termination/13-do-while-terminating.c similarity index 100% rename from tests/regression/74-loop_termination/13-do-while-terminating.c rename to tests/regression/75-termination/13-do-while-terminating.c diff --git a/tests/regression/74-loop_termination/14-do-while-nonterminating.c b/tests/regression/75-termination/14-do-while-nonterminating.c similarity index 100% rename from tests/regression/74-loop_termination/14-do-while-nonterminating.c rename to tests/regression/75-termination/14-do-while-nonterminating.c diff --git a/tests/regression/74-loop_termination/15-complex-loop-combination-terminating.c b/tests/regression/75-termination/15-complex-loop-combination-terminating.c similarity index 100% rename from tests/regression/74-loop_termination/15-complex-loop-combination-terminating.c rename to tests/regression/75-termination/15-complex-loop-combination-terminating.c diff --git a/tests/regression/74-loop_termination/16-nested-loop-nontrivial-nonterminating.c b/tests/regression/75-termination/16-nested-loop-nontrivial-nonterminating.c similarity index 100% rename from tests/regression/74-loop_termination/16-nested-loop-nontrivial-nonterminating.c rename to tests/regression/75-termination/16-nested-loop-nontrivial-nonterminating.c diff --git a/tests/regression/74-loop_termination/17-goto-terminating.c b/tests/regression/75-termination/17-goto-terminating.c similarity index 100% rename from tests/regression/74-loop_termination/17-goto-terminating.c rename to tests/regression/75-termination/17-goto-terminating.c diff --git a/tests/regression/74-loop_termination/18-goto-nonterminating.c b/tests/regression/75-termination/18-goto-nonterminating.c similarity index 100% rename from tests/regression/74-loop_termination/18-goto-nonterminating.c rename to tests/regression/75-termination/18-goto-nonterminating.c diff --git a/tests/regression/74-loop_termination/19-rand-terminating.c b/tests/regression/75-termination/19-rand-terminating.c similarity index 100% rename from tests/regression/74-loop_termination/19-rand-terminating.c rename to tests/regression/75-termination/19-rand-terminating.c diff --git a/tests/regression/74-loop_termination/20-rand-nonterminating.c b/tests/regression/75-termination/20-rand-nonterminating.c similarity index 100% rename from tests/regression/74-loop_termination/20-rand-nonterminating.c rename to tests/regression/75-termination/20-rand-nonterminating.c diff --git a/tests/regression/74-loop_termination/21-no-exit-on-rand-unproofable.c b/tests/regression/75-termination/21-no-exit-on-rand-unproofable.c similarity index 100% rename from tests/regression/74-loop_termination/21-no-exit-on-rand-unproofable.c rename to tests/regression/75-termination/21-no-exit-on-rand-unproofable.c diff --git a/tests/regression/74-loop_termination/22-exit-on-rand-unproofable.c b/tests/regression/75-termination/22-exit-on-rand-unproofable.c similarity index 100% rename from tests/regression/74-loop_termination/22-exit-on-rand-unproofable.c rename to tests/regression/75-termination/22-exit-on-rand-unproofable.c diff --git a/tests/regression/74-loop_termination/23-exit-on-rand-terminating.c b/tests/regression/75-termination/23-exit-on-rand-terminating.c similarity index 100% rename from tests/regression/74-loop_termination/23-exit-on-rand-terminating.c rename to tests/regression/75-termination/23-exit-on-rand-terminating.c diff --git a/tests/regression/74-loop_termination/24-upjumping-goto-loopless-terminating.c b/tests/regression/75-termination/24-upjumping-goto-loopless-terminating.c similarity index 100% rename from tests/regression/74-loop_termination/24-upjumping-goto-loopless-terminating.c rename to tests/regression/75-termination/24-upjumping-goto-loopless-terminating.c diff --git a/tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c b/tests/regression/75-termination/25-leave-loop-goto-terminating.c similarity index 100% rename from tests/regression/74-loop_termination/25-leave-loop-goto-terminating.c rename to tests/regression/75-termination/25-leave-loop-goto-terminating.c diff --git a/tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c b/tests/regression/75-termination/26-enter-loop-goto-terminating.c similarity index 100% rename from tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c rename to tests/regression/75-termination/26-enter-loop-goto-terminating.c diff --git a/tests/regression/74-loop_termination/27-upjumping-goto-nonterminating.c b/tests/regression/75-termination/27-upjumping-goto-nonterminating.c similarity index 100% rename from tests/regression/74-loop_termination/27-upjumping-goto-nonterminating.c rename to tests/regression/75-termination/27-upjumping-goto-nonterminating.c diff --git a/tests/regression/74-loop_termination/28-do-while-continue-terminating.c b/tests/regression/75-termination/28-do-while-continue-terminating.c similarity index 100% rename from tests/regression/74-loop_termination/28-do-while-continue-terminating.c rename to tests/regression/75-termination/28-do-while-continue-terminating.c diff --git a/tests/regression/74-loop_termination/29-do-while-continue-nonterminating.c b/tests/regression/75-termination/29-do-while-continue-nonterminating.c similarity index 100% rename from tests/regression/74-loop_termination/29-do-while-continue-nonterminating.c rename to tests/regression/75-termination/29-do-while-continue-nonterminating.c diff --git a/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c b/tests/regression/75-termination/30-goto-out-of-inner-loop-terminating.c similarity index 100% rename from tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c rename to tests/regression/75-termination/30-goto-out-of-inner-loop-terminating.c diff --git a/tests/regression/74-loop_termination/31-goto-out-of-inner-loop-nonterminating.c b/tests/regression/75-termination/31-goto-out-of-inner-loop-nonterminating.c similarity index 100% rename from tests/regression/74-loop_termination/31-goto-out-of-inner-loop-nonterminating.c rename to tests/regression/75-termination/31-goto-out-of-inner-loop-nonterminating.c diff --git a/tests/regression/74-loop_termination/32-multithread-terminating.c b/tests/regression/75-termination/32-multithread-terminating.c similarity index 100% rename from tests/regression/74-loop_termination/32-multithread-terminating.c rename to tests/regression/75-termination/32-multithread-terminating.c diff --git a/tests/regression/74-loop_termination/33-multithread-nonterminating.c b/tests/regression/75-termination/33-multithread-nonterminating.c similarity index 100% rename from tests/regression/74-loop_termination/33-multithread-nonterminating.c rename to tests/regression/75-termination/33-multithread-nonterminating.c diff --git a/tests/regression/74-loop_termination/34-nested-for-loop-nonterminating.c b/tests/regression/75-termination/34-nested-for-loop-nonterminating.c similarity index 100% rename from tests/regression/74-loop_termination/34-nested-for-loop-nonterminating.c rename to tests/regression/75-termination/34-nested-for-loop-nonterminating.c diff --git a/tests/regression/74-loop_termination/35-goto-out-of-inner-loop-with-print-terminating.c b/tests/regression/75-termination/35-goto-out-of-inner-loop-with-print-terminating.c similarity index 100% rename from tests/regression/74-loop_termination/35-goto-out-of-inner-loop-with-print-terminating.c rename to tests/regression/75-termination/35-goto-out-of-inner-loop-with-print-terminating.c diff --git a/tests/regression/75-recursion_termination/01-simple-terminating.c b/tests/regression/75-termination/36-recursion-terminating.c similarity index 100% rename from tests/regression/75-recursion_termination/01-simple-terminating.c rename to tests/regression/75-termination/36-recursion-terminating.c diff --git a/tests/regression/75-recursion_termination/02-simple-nonterminating.c b/tests/regression/75-termination/37-recursion-nonterminating.c similarity index 100% rename from tests/regression/75-recursion_termination/02-simple-nonterminating.c rename to tests/regression/75-termination/37-recursion-nonterminating.c diff --git a/tests/regression/75-recursion_termination/03-nested-terminating.c b/tests/regression/75-termination/38-recursion-nested-terminating.c similarity index 100% rename from tests/regression/75-recursion_termination/03-nested-terminating.c rename to tests/regression/75-termination/38-recursion-nested-terminating.c diff --git a/tests/regression/75-recursion_termination/04-nested-nonterminating.c b/tests/regression/75-termination/39-recursion-nested-nonterminating.c similarity index 100% rename from tests/regression/75-recursion_termination/04-nested-nonterminating.c rename to tests/regression/75-termination/39-recursion-nested-nonterminating.c From 4a6714c1662a26f4aa639c2203bc3c65512f0c88 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Sun, 9 Jul 2023 18:18:48 +0200 Subject: [PATCH 0286/1312] added comments to explain why test 74/28 does fail and why 30 and 35 might behave differently; also restructured module G in the recursionTermLifter --- src/framework/analyses.ml | 54 -------------- src/framework/constraints.ml | 74 +++++++++++++++++-- .../28-do-while-continue-terminating.c | 72 ++++++++++++++++++ .../30-goto-out-of-inner-loop-terminating.c | 8 ++ ...out-of-inner-loop-with-print-terminating.c | 12 +++ 5 files changed, 160 insertions(+), 60 deletions(-) diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index faf7c456ca..cd611a9faf 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -118,60 +118,6 @@ struct | x -> BatPrintf.fprintf f "%a" printXml x end -module GVarGSet (G: Lattice.S) (C: Printable.S) (Base: Printable.S) = -struct - module CSet = - struct - include SetDomain.Make (Base) (* Set of Tuples*) - let name () = "contexts" - let printXml f a = - BatPrintf.fprintf f "\n"; - iter (Base.printXml f) a; - BatPrintf.fprintf f "\n\n" - end - - module CMap = - struct - include MapDomain.MapBot ( - struct - include Printable.Std (* To make it Groupable *) - include C - let printXml f c = BatPrintf.fprintf f - "\n - callee_context\n%a\n\n - " printXml c - end - ) (CSet) - let printXml f c = BatPrintf.fprintf f " - ContextTupleMap\n - %a\n\n - " printXml c - end - - include Lattice.Lift2 (G) (CMap) (Printable.DefaultNames) - - let spec = function - | `Bot -> G.bot () - | `Lifted1 x -> x - | _ -> failwith "GVarGSet.spec" - let contexts = function - | `Bot -> CSet.bot () - | `Lifted2 x -> x - | _ -> failwith "GVarGSet.contexts" - let create_spec spec = `Lifted1 spec - let create_contexts contexts = `Lifted2 contexts - - let printXml f = function - | `Lifted1 x -> G.printXml f x - | `Lifted2 x -> BatPrintf.fprintf f "%a" CMap.printXml x - | x -> BatPrintf.fprintf f "%a" printXml x - - let base2 instance = - match instance with - | `Lifted2 n -> Some n - | _ -> None -end - exception Deadcode (** [Dom (D)] produces D lifted where bottom means dead-code *) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index d744937d53..047bdf7e38 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1713,9 +1713,71 @@ module RecursionTermLifter (S: Spec) struct include S + + (* contains all the callee fundecs*) module V = GVarF(S.V) - module G = GVarGSet (S.G) (S.C) (Printable.Prod (CilType.Fundec) (S.C)) + (* Tuple containing the fundec and context of the caller *) + module CallGraphTuple = + struct + include Printable.Prod (CilType.Fundec) (S.C) + end + + (* Set containing multiple caller tuples *) + module CallGraphSet = + struct + include SetDomain.Make (CallGraphTuple) + let name () = "callerInfo" + let printXml f a = + BatPrintf.fprintf f "\n"; + iter (CallGraphTuple.printXml f) a; + BatPrintf.fprintf f "\n\n" + end + + (* Mapping from the callee context to the set of all caller tuples*) + module CallGraphMap = + struct + include MapDomain.MapBot ( + struct + include Printable.Std (* To make it Groupable *) + include S.C + let printXml f c = BatPrintf.fprintf f + "\n + callee_context\n%a\n\n + " printXml c + end + ) (CallGraphSet) + let printXml f c = BatPrintf.fprintf f " + ContextTupleMap\n + %a\n\n + " printXml c + end + + module G = + struct + include Lattice.Lift2 (G) (CallGraphMap) (Printable.DefaultNames) + + let spec = function + | `Bot -> G.bot () + | `Lifted1 x -> x + | _ -> failwith "RecursionTermLifter.spec" + let callGraph = function + | `Bot -> CallGraphMap.bot () + | `Lifted2 x -> x + | _ -> failwith "RecursionTermLifter.callGraph" + let create_spec spec = `Lifted1 spec + let create_callGraph callGraph = `Lifted2 callGraph + + let printXml f = function + | `Lifted1 x -> G.printXml f x + | `Lifted2 x -> BatPrintf.fprintf f "%a" CallGraphMap.printXml x + | x -> BatPrintf.fprintf f "%a" printXml x + + let base2 instance = + match instance with + | `Lifted2 n -> Some n + | _ -> None + end let name () = "RecursionTermLifter (" ^ S.name () ^ ")" @@ -1749,8 +1811,8 @@ struct let fundec_e_typeV: V.t = V.relift (`Right fundec_e) in let gmap_opt = G.base2 (ctx.global (fundec_e_typeV)) in let gmap = Option.get (gmap_opt) in (*might be empty*) - let callers: G.CSet.t = G.CMap.find (context_e) gmap in - G.CSet.iter (fun to_call -> + let callers: CallGraphSet.t = CallGraphMap.find (context_e) gmap in + CallGraphSet.iter (fun to_call -> iter_call new_path_visited_calls to_call ) callers; with Invalid_argument _ -> () (* path ended: no cycle*) @@ -1759,7 +1821,7 @@ struct try let gmap_opt = G.base2 (ctx.global (v)) in let gmap = Option.get (gmap_opt) in - G.CMap.iter(fun key value -> + CallGraphMap.iter(fun key value -> let call = (v', key) in iter_call LS.empty call ) gmap (* try all fundec + context pairs that are in the map *) @@ -1802,7 +1864,7 @@ struct *) let side_context sideg f c t = if !AnalysisState.postsolving then - sideg (V.contexts f) (G.create_contexts (G.CMap.singleton (c) (t))) + sideg (V.contexts f) (G.create_callGraph (CallGraphMap.singleton (c) (t))) let enter ctx = S.enter (conv ctx) let paths_as_set ctx = S.paths_as_set (conv ctx) @@ -1816,7 +1878,7 @@ struct let c_e: S.C.t = Option.get fc in (*Callee context*) let fd_e : fundec = f in (*Callee fundec*) let tup: (fundec * S.C.t) = (fd_r, c_r) in - let t = G.CSet.singleton (tup) in + let t = CallGraphSet.singleton (tup) in side_context ctx.sideg fd_e (c_e) t; S.combine_env (conv ctx) r fe f args fc es f_ask else diff --git a/tests/regression/74-loop_termination/28-do-while-continue-terminating.c b/tests/regression/74-loop_termination/28-do-while-continue-terminating.c index 7756b51071..f05fa4f315 100644 --- a/tests/regression/74-loop_termination/28-do-while-continue-terminating.c +++ b/tests/regression/74-loop_termination/28-do-while-continue-terminating.c @@ -19,3 +19,75 @@ int main() printf("Exited the loop\n"); return 0; } + +/* +NOTE: +Test 28: does not terminate but should terminate (test case "28-do-while-continue-terminating.c") +Reason: upjumping goto + +If one has a look at the generated CIL output (attached at the bottom of this file), one can see that the "continue" is +translated in a "goto" with a corresponding label "__Cont". This label points to the loop-exit condition. Since the condition +is part of the loop, its location is evaluated to 8-17. +The location of the goto "goto __Cont" is located in line 15. +To provide soundness for the analysis, the preprocessing detects upjumping gotos with the help of its location. +In case such a goto is detected, the program is classified as non-terminating. +Due to this inserted goto (which is a result of the "continue"), an upjumping goto is located, which makes this program non-terminating. + +It should be noted that this issue happens when "do while"-loops and "continues" are combined. +If one combines "while"-loops and "continues", the analysis can still classify the loop as terminating. +The reason for that can be seen in the second CIL output, where the "do while"-loop is replaced by a "while"-loop. +Instead of creating a new label, the "while-continue" label of the loop is reused. Also, this goto statement is not specified as a goto, +but as a Continue statement. Hence, it is not analyzed for the upjumping gotos, which does not lead to the problem as with the "do while". + + +------------------- SHORTENED CIL output for Test 28 (DO WHILE): ------------------- +int main(void) +{{{{ + #line 8 + while (1) { + while_continue: ; + #line 12 + if (i % 2 == 0) { + #line 15 + goto __Cont; + } + __Cont: + #line 8 + if (! (i <= 5)) { + #line 8 + goto while_break; + } + } + + while_break: + }} + #line 20 + return (0); +}} + + +------------------- SHORTENED CIL output for Test 28 (WHILE): ------------------- +Test 28: replacing DO WHILE with WHILE: +int main(void) +{{{{ + #line 8 + while (1) { + while_continue: ; + #line 8 + if (! (i <= 5)) { + #line 8 + goto while_break; + } + #line 12 + if (i % 2 == 0) { + #line 15 + goto while_continue; + } + } + while_break: ; + }} + #line 20 + return (0); +}} + +*/ diff --git a/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c b/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c index 15ce6e395e..a92dcd2bc8 100644 --- a/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c +++ b/tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c @@ -21,3 +21,11 @@ int main() { return 0; } + +/* +NOTE: In case we do NOT assume no-overflow: +Test 30: terminates (test case "30-goto-out-of-inner-loop-terminating.c") +Test 35: does not terminate (test case "35-goto-out-of-inner-loop-with-print-terminating.c") + +The reason is explained in "35-goto-out-of-inner-loop-with-print-terminating.c" +*/ diff --git a/tests/regression/74-loop_termination/35-goto-out-of-inner-loop-with-print-terminating.c b/tests/regression/74-loop_termination/35-goto-out-of-inner-loop-with-print-terminating.c index 29a8033fdb..bb8bbacbf0 100644 --- a/tests/regression/74-loop_termination/35-goto-out-of-inner-loop-with-print-terminating.c +++ b/tests/regression/74-loop_termination/35-goto-out-of-inner-loop-with-print-terminating.c @@ -20,3 +20,15 @@ int main() { return 0; } + +/* +NOTE: In case we do NOT assume no-overflow: +Test 30: terminates (test case "30-goto-out-of-inner-loop-terminating.c") +Test 35: does not terminate (test case "35-goto-out-of-inner-loop-with-print-terminating.c") + +The only difference between Test 30 and Test 35 is line 17. Test 30 has an additional statement, and Test 35 continues already with the label. +This difference in Test 35 leads to an overflow in line 11, and hence to the non-termination. +This overflow is created by a WPoint Issue. By enabling the no-overflow option this issue can be fixed and, both test cases are correctly detected as terminating. + +(The overflow also happens without the termination analysis enabled.) +*/ From cb13d1497b77e26595bac26017afb5ec627bd39c Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Sun, 9 Jul 2023 18:25:33 +0200 Subject: [PATCH 0287/1312] Tests moved --- .../09-complex-for-loop-terminating.c | 0 .../10-complex-loop-terminating.c | 0 .../15-complex-loop-combination-terminating.c | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename tests/regression/{75-termination => 76-termination-complex-cases}/09-complex-for-loop-terminating.c (100%) rename tests/regression/{75-termination => 76-termination-complex-cases}/10-complex-loop-terminating.c (100%) rename tests/regression/{75-termination => 76-termination-complex-cases}/15-complex-loop-combination-terminating.c (100%) diff --git a/tests/regression/75-termination/09-complex-for-loop-terminating.c b/tests/regression/76-termination-complex-cases/09-complex-for-loop-terminating.c similarity index 100% rename from tests/regression/75-termination/09-complex-for-loop-terminating.c rename to tests/regression/76-termination-complex-cases/09-complex-for-loop-terminating.c diff --git a/tests/regression/75-termination/10-complex-loop-terminating.c b/tests/regression/76-termination-complex-cases/10-complex-loop-terminating.c similarity index 100% rename from tests/regression/75-termination/10-complex-loop-terminating.c rename to tests/regression/76-termination-complex-cases/10-complex-loop-terminating.c diff --git a/tests/regression/75-termination/15-complex-loop-combination-terminating.c b/tests/regression/76-termination-complex-cases/15-complex-loop-combination-terminating.c similarity index 100% rename from tests/regression/75-termination/15-complex-loop-combination-terminating.c rename to tests/regression/76-termination-complex-cases/15-complex-loop-combination-terminating.c From c6e36e4cbad76128d5a1de1eb601e74653bbdd7a Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Sun, 9 Jul 2023 18:39:11 +0200 Subject: [PATCH 0288/1312] cleaned module G from an unnecessary function; addded missing comment to analysisState --- src/framework/analysisState.ml | 2 +- src/framework/constraints.ml | 40 +++++++++++++--------------------- 2 files changed, 16 insertions(+), 26 deletions(-) diff --git a/src/framework/analysisState.ml b/src/framework/analysisState.ml index 3f577d79f4..c2d977af9c 100644 --- a/src/framework/analysisState.ml +++ b/src/framework/analysisState.ml @@ -7,7 +7,7 @@ let should_warn = ref false (** Whether signed overflow or underflow happened *) let svcomp_may_overflow = ref false -(** TODO:**) +(** Whether the termination analysis detectes the program as non-terminating**) let svcomp_may_not_terminate = ref false (** A hack to see if we are currently doing global inits *) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 6aa0fc01e2..e99f39bd8f 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1773,10 +1773,6 @@ struct | `Lifted2 x -> BatPrintf.fprintf f "%a" CallGraphMap.printXml x | x -> BatPrintf.fprintf f "%a" printXml x - let base2 instance = - match instance with - | `Lifted2 n -> Some n - | _ -> None end let name () = "RecursionTermLifter (" ^ S.name () ^ ")" @@ -1797,35 +1793,29 @@ struct let rec iter_call (path_visited_calls: LS.t) (call:Printable.Prod (CilType.Fundec) (S.C).t) = let ((fundec_e:fundec), (context_e: C.t)) = call in (*unpack tuple for later use*) if LS.mem call path_visited_calls then ( - AnalysisState.svcomp_may_not_terminate := true; + AnalysisState.svcomp_may_not_terminate := true; (*set the indicator for a non-terminating program for the sv comp*) (*Cycle found*) let msgs = [ (Pretty.dprintf "The program might not terminate! (Fundec %a is contained in a call graph cycle)\n" CilType.Fundec.pretty fundec_e, Some (M.Location.CilLocation fundec_e.svar.vdecl)); ] in - M.msg_group Warning ~category:NonTerminating "Recursion cycle" msgs) + M.msg_group Warning ~category:NonTerminating "Recursion cycle" msgs) (* output a warning for non-termination*) else if not (LH.mem global_visited_calls call) then begin - try - LH.replace global_visited_calls call (); - let new_path_visited_calls = LS.add call path_visited_calls in - let fundec_e_typeV: V.t = V.relift (`Right fundec_e) in - let gmap_opt = G.base2 (ctx.global (fundec_e_typeV)) in - let gmap = Option.get (gmap_opt) in (*might be empty*) - let callers: CallGraphSet.t = CallGraphMap.find (context_e) gmap in - CallGraphSet.iter (fun to_call -> - iter_call new_path_visited_calls to_call - ) callers; - with Invalid_argument _ -> () (* path ended: no cycle*) + LH.replace global_visited_calls call (); + let new_path_visited_calls = LS.add call path_visited_calls in + let fundec_e_typeV: V.t = V.relift (`Right fundec_e) in + let gmap = G.callGraph (ctx.global (fundec_e_typeV)) in + let callers: CallGraphSet.t = CallGraphMap.find (context_e) gmap in + CallGraphSet.iter (fun to_call -> + iter_call new_path_visited_calls to_call + ) callers; end in - try - let gmap_opt = G.base2 (ctx.global (v)) in - let gmap = Option.get (gmap_opt) in - CallGraphMap.iter(fun key value -> - let call = (v', key) in - iter_call LS.empty call - ) gmap (* try all fundec + context pairs that are in the map *) - with Invalid_argument _ -> () (* path ended: no cycle*) + let gmap = G.callGraph (ctx.global (v)) in + CallGraphMap.iter(fun key value -> + let call = (v', key) in + iter_call LS.empty call + ) gmap (* try all fundec + context pairs that are in the map *) let query ctx (type a) (q: a Queries.t): a Queries.result = match q with From 9d6684666060ffc5d5bb9d427a3da846c60040b2 Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Sun, 9 Jul 2023 20:09:08 +0200 Subject: [PATCH 0289/1312] Complex test adapted --- .../09-complex-for-loop-terminating.c | 23 +---------- .../10-complex-loop-terminating.c | 14 +------ .../15-complex-loop-combination-terminating.c | 37 +----------------- .../75-termination/40-complex-conditions.c | 39 +++++++++++++++++++ .../regression/75-termination/41-more-tests.c | 30 ++++++++++++++ 5 files changed, 72 insertions(+), 71 deletions(-) rename tests/regression/{76-termination-complex-cases => 75-termination}/09-complex-for-loop-terminating.c (73%) rename tests/regression/{76-termination-complex-cases => 75-termination}/10-complex-loop-terminating.c (84%) rename tests/regression/{76-termination-complex-cases => 75-termination}/15-complex-loop-combination-terminating.c (68%) create mode 100644 tests/regression/75-termination/40-complex-conditions.c create mode 100644 tests/regression/75-termination/41-more-tests.c diff --git a/tests/regression/76-termination-complex-cases/09-complex-for-loop-terminating.c b/tests/regression/75-termination/09-complex-for-loop-terminating.c similarity index 73% rename from tests/regression/76-termination-complex-cases/09-complex-for-loop-terminating.c rename to tests/regression/75-termination/09-complex-for-loop-terminating.c index 30ca32a70a..90591c7554 100644 --- a/tests/regression/76-termination-complex-cases/09-complex-for-loop-terminating.c +++ b/tests/regression/75-termination/09-complex-for-loop-terminating.c @@ -1,4 +1,4 @@ -// SKIP TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --set sem.int.signed_overflow assume_none // Goblint does not finish this test #include @@ -76,27 +76,6 @@ int main() } printf("\n"); - // Loop with a continue statement - for (i = 1; i <= 10; i++) - { - if (i % 2 == 0) - { - continue; - } - printf("%d ", i); - } - printf("\n"); - - // Loop with complex conditions - for (i = 1; i <= 10; i++) - { - if (i > 5 && i % 2 == 0) - { - printf("%d ", i); - } - } - printf("\n"); - // Loop with multiple variables int a, b, c; for (a = 1, b = 2, c = 3; a <= 10; a++, b += 2, c += 3) diff --git a/tests/regression/76-termination-complex-cases/10-complex-loop-terminating.c b/tests/regression/75-termination/10-complex-loop-terminating.c similarity index 84% rename from tests/regression/76-termination-complex-cases/10-complex-loop-terminating.c rename to tests/regression/75-termination/10-complex-loop-terminating.c index 8970223c6e..e33139cced 100644 --- a/tests/regression/76-termination-complex-cases/10-complex-loop-terminating.c +++ b/tests/regression/75-termination/10-complex-loop-terminating.c @@ -1,4 +1,4 @@ -// SKIP TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --set sem.int.signed_overflow assume_none // Goblint does not finish this test #include @@ -108,18 +108,6 @@ int main() } printf("\n"); - // Loop with complex conditions - i = 1; - while (i <= 10) - { - if (i > 5 && i % 2 == 0) - { - printf("%d ", i); - } - i++; - } - printf("\n"); - // Loop with multiple variables int a = 1; int b = 2; diff --git a/tests/regression/76-termination-complex-cases/15-complex-loop-combination-terminating.c b/tests/regression/75-termination/15-complex-loop-combination-terminating.c similarity index 68% rename from tests/regression/76-termination-complex-cases/15-complex-loop-combination-terminating.c rename to tests/regression/75-termination/15-complex-loop-combination-terminating.c index 099203d13f..64afaf30e1 100644 --- a/tests/regression/76-termination-complex-cases/15-complex-loop-combination-terminating.c +++ b/tests/regression/75-termination/15-complex-loop-combination-terminating.c @@ -1,4 +1,4 @@ -// SKIP TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --set sem.int.signed_overflow assume_none // Goblint does not finish this test #include @@ -88,31 +88,6 @@ int main() n++; } - // Loop with a continue statement - for (int r = 1; r <= 10; r++) - { - if (r % 3 == 0) - { - continue; - } - printf("Loop with Continue: %d\n", r); - } - - // Loop with multiple conditions - int s = 1; - while (s <= 10 && s % 2 == 0) - { - printf("Loop with Multiple Conditions: %d\n", s); - s++; - } - - // Loop with multiple variables - int t, u; - for (t = 1, u = 10; t <= 5 && u >= 5; t++, u--) - { - printf("Loop with Multiple Variables: %d %d\n", t, u); - } - // Loop with nested conditions for (int v = 1; v <= 10; v++) { @@ -131,15 +106,5 @@ int main() } } - // Loop with a label and goto statement - int w = 1; -start: - if (w <= 5) - { - printf("Loop with Label and Goto: %d\n", w); - w++; - goto start; - } - return 0; } diff --git a/tests/regression/75-termination/40-complex-conditions.c b/tests/regression/75-termination/40-complex-conditions.c new file mode 100644 index 0000000000..2f342e89a0 --- /dev/null +++ b/tests/regression/75-termination/40-complex-conditions.c @@ -0,0 +1,39 @@ +// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() { + int i; + + // Loop with a continue statement + for (i = 1; i <= 10; i++) + { + if (i % 2 == 0) + { + continue; + } + printf("%d ", i); + } + printf("\n"); + + // Loop with complex conditions + for (i = 1; i <= 10; i++) + { + if (i > 5 && i % 2 == 0) + { + printf("%d ", i); + } + } + printf("\n"); + + // Loop with complex conditions + i = 1; + while (i <= 10) + { + if (i > 5 && i % 2 == 0) + { + printf("%d ", i); + } + i++; + } + printf("\n"); +} \ No newline at end of file diff --git a/tests/regression/75-termination/41-more-tests.c b/tests/regression/75-termination/41-more-tests.c new file mode 100644 index 0000000000..112f95c3ee --- /dev/null +++ b/tests/regression/75-termination/41-more-tests.c @@ -0,0 +1,30 @@ +// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() { + + // Loop with a continue statement + for (int r = 1; r <= 10; r++) + { + if (r % 3 == 0) + { + continue; + } + printf("Loop with Continue: %d\n", r); + } + + // Loop with multiple conditions + int s = 1; + while (s <= 10 && s % 2 == 0) + { + printf("Loop with Multiple Conditions: %d\n", s); + s++; + } + + // Loop with multiple variables + int t, u; + for (t = 1, u = 10; t <= 5 && u >= 5; t++, u--) + { + printf("Loop with Multiple Variables: %d %d\n", t, u); + } + } \ No newline at end of file From 09ee872f53413b4a7f0790bd2b58735878a8b5e1 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Mon, 10 Jul 2023 08:37:51 +0200 Subject: [PATCH 0290/1312] moved warnings to loop analysis --- src/analyses/loopTermination.ml | 16 ++++++++++++++++ src/framework/constraints.ml | 8 +------- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/analyses/loopTermination.ml b/src/analyses/loopTermination.ml index 38823d3039..ecb48a5284 100644 --- a/src/analyses/loopTermination.ml +++ b/src/analyses/loopTermination.ml @@ -61,6 +61,17 @@ struct let startstate _ = () let exitstate = startstate + let finalize () = + if not (no_upjumping_gotos ()) then ( + List.iter + (fun x -> + let msgs = + [(Pretty.dprintf "The program might not terminate! (Upjumping Goto)\n", Some (M.Location.CilLocation x));] in + M.msg_group Warning ~category:NonTerminating "Possibly non terminating loops" msgs) + (!upjumping_gotos) + ); + () + let assign ctx (lval : lval) (rval : exp) = if !AnalysisState.postsolving then (* Detect assignment to loop counter variable *) @@ -71,6 +82,11 @@ struct let is_bounded = check_bounded ctx x in let loop_statement = VarToStmt.find x !loop_counters in ctx.sideg () (G.add (`Lifted loop_statement) is_bounded (ctx.global ())); + (* In case the loop is not bounded, a warning is created*) + if not (is_bounded) then ( + let msgs = + [(Pretty.dprintf "The program might not terminate! (Loop analysis)\n", Some (M.Location.CilLocation (Cilfacade.get_stmtLoc loop_statement)));] in + M.msg_group Warning ~category:NonTerminating "Possibly non terminating loops" msgs); () | _ -> () else () diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index e99f39bd8f..6114f30adb 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1822,13 +1822,7 @@ struct | WarnGlobal v -> (* check result of loop analysis *) if not (ctx.ask Queries.MustTermAllLoops) then - (AnalysisState.svcomp_may_not_terminate := true; - let msgs = - [ - (Pretty.dprintf "The program might not terminate! (Loop analysis)\n", Some (M.Location.CilLocation locUnknown)); - ] in - M.msg_group Warning ~category:NonTerminating "Possibly non terminating loops" msgs - ); + AnalysisState.svcomp_may_not_terminate := true; let v: V.t = Obj.obj v in begin match v with | `Left v' -> From d6c11b63f8896d00c7eb14958b306efdc1536098 Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Mon, 10 Jul 2023 14:34:01 +0200 Subject: [PATCH 0291/1312] Test refactoring & parameter correction --- .../01-simple-loop-terminating.c | 18 +- .../02-simple-loop-nonterminating.c | 14 +- .../03-nested-loop-terminating.c | 37 ++-- .../04-nested-loop-nonterminating.c | 29 ++- .../75-termination/05-for-loop-terminating.c | 16 +- .../06-for-loop-nonterminating.c | 10 +- .../07-nested-for-loop-terminating.c | 25 +-- .../08-nested-for-loop-nonterminating.c | 23 +-- .../09-complex-for-loop-terminating.c | 122 +++++------ .../10-complex-loop-terminating.c | 190 ++++++++---------- .../75-termination/11-loopless-termination.c | 6 +- .../12-do-while-instant-terminating.c | 18 +- .../75-termination/13-do-while-terminating.c | 20 +- .../14-do-while-nonterminating.c | 20 +- .../15-complex-loop-combination-terminating.c | 166 +++++++-------- ...16-nested-loop-nontrivial-nonterminating.c | 29 ++- .../75-termination/17-goto-terminating.c | 21 +- .../75-termination/18-goto-nonterminating.c | 15 +- .../75-termination/19-rand-terminating.c | 38 ++-- .../75-termination/20-rand-nonterminating.c | 36 ++-- .../21-no-exit-on-rand-unproofable.c | 26 +-- .../22-exit-on-rand-unproofable.c | 18 +- .../23-exit-on-rand-terminating.c | 23 +-- .../24-upjumping-goto-loopless-terminating.c | 16 +- .../25-leave-loop-goto-terminating.c | 28 +-- .../26-enter-loop-goto-terminating.c | 32 +-- .../27-upjumping-goto-nonterminating.c | 18 +- .../28-do-while-continue-terminating.c | 76 +++---- .../29-do-while-continue-nonterminating.c | 28 ++- .../30-goto-out-of-inner-loop-terminating.c | 39 ++-- ...31-goto-out-of-inner-loop-nonterminating.c | 32 +-- .../32-multithread-terminating.c | 34 ++-- .../33-multithread-nonterminating.c | 53 ++--- .../34-nested-for-loop-nonterminating.c | 23 +-- ...out-of-inner-loop-with-print-terminating.c | 44 ++-- .../75-termination/36-recursion-terminating.c | 24 +-- .../37-recursion-nonterminating.c | 24 +-- .../38-recursion-nested-terminating.c | 40 ++-- .../39-recursion-nested-nonterminating.c | 24 +-- .../75-termination/40-complex-conditions.c | 52 +++-- .../regression/75-termination/41-more-tests.c | 44 ++-- 41 files changed, 718 insertions(+), 833 deletions(-) diff --git a/tests/regression/75-termination/01-simple-loop-terminating.c b/tests/regression/75-termination/01-simple-loop-terminating.c index a80084868a..66b6585f67 100644 --- a/tests/regression/75-termination/01-simple-loop-terminating.c +++ b/tests/regression/75-termination/01-simple-loop-terminating.c @@ -1,15 +1,13 @@ -// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() -{ - int i = 1; +int main() { + int i = 1; - while (i <= 10) - { - printf("%d\n", i); - i++; - } + while (i <= 10) { + printf("%d\n", i); + i++; + } - return 0; + return 0; } diff --git a/tests/regression/75-termination/02-simple-loop-nonterminating.c b/tests/regression/75-termination/02-simple-loop-nonterminating.c index eef9f81ea3..6fe8816da4 100644 --- a/tests/regression/75-termination/02-simple-loop-nonterminating.c +++ b/tests/regression/75-termination/02-simple-loop-nonterminating.c @@ -1,12 +1,10 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() -{ - while (1) - { - continue; - } +int main() { + while (1) { + continue; + } - return 0; + return 0; } diff --git a/tests/regression/75-termination/03-nested-loop-terminating.c b/tests/regression/75-termination/03-nested-loop-terminating.c index 5e72ec3284..4e3fafabcf 100644 --- a/tests/regression/75-termination/03-nested-loop-terminating.c +++ b/tests/regression/75-termination/03-nested-loop-terminating.c @@ -1,27 +1,24 @@ -// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() -{ - int rows = 3; - int columns = 4; - int i = 1; +int main() { + int rows = 3; + int columns = 4; + int i = 1; - // Outer while loop for rows - while (i <= rows) - { - int j = 1; + // Outer while loop for rows + while (i <= rows) { + int j = 1; - // Inner while loop for columns - while (j <= columns) - { - printf("(%d, %d) ", i, j); - j++; - } - - printf("\n"); - i++; + // Inner while loop for columns + while (j <= columns) { + printf("(%d, %d) ", i, j); + j++; } - return 0; + printf("\n"); + i++; + } + + return 0; } diff --git a/tests/regression/75-termination/04-nested-loop-nonterminating.c b/tests/regression/75-termination/04-nested-loop-nonterminating.c index 1fb5ada507..00c2554ed2 100644 --- a/tests/regression/75-termination/04-nested-loop-nonterminating.c +++ b/tests/regression/75-termination/04-nested-loop-nonterminating.c @@ -1,23 +1,20 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() -{ - int outerCount = 1; +int main() { + int outerCount = 1; - while (outerCount <= 3) - { - int innerCount = 1; + while (outerCount <= 3) { + int innerCount = 1; - while (1) - { - printf("(%d, %d) ", outerCount, innerCount); - innerCount++; - } - - printf("\n"); - outerCount++; + while (1) { + printf("(%d, %d) ", outerCount, innerCount); + innerCount++; } - return 0; + printf("\n"); + outerCount++; + } + + return 0; } diff --git a/tests/regression/75-termination/05-for-loop-terminating.c b/tests/regression/75-termination/05-for-loop-terminating.c index cf71fa5135..fe07200e5b 100644 --- a/tests/regression/75-termination/05-for-loop-terminating.c +++ b/tests/regression/75-termination/05-for-loop-terminating.c @@ -1,14 +1,12 @@ -// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() -{ - int i; +int main() { + int i; - for (i = 1; i <= 10; i++) - { - printf("%d\n", i); - } + for (i = 1; i <= 10; i++) { + printf("%d\n", i); + } - return 0; + return 0; } diff --git a/tests/regression/75-termination/06-for-loop-nonterminating.c b/tests/regression/75-termination/06-for-loop-nonterminating.c index 8c1500cfb1..374cd3e59f 100644 --- a/tests/regression/75-termination/06-for-loop-nonterminating.c +++ b/tests/regression/75-termination/06-for-loop-nonterminating.c @@ -1,10 +1,10 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { - for (;;) { - printf("This loop does not terminate.\n"); - } + for (;;) { + printf("This loop does not terminate.\n"); + } - return 0; + return 0; } diff --git a/tests/regression/75-termination/07-nested-for-loop-terminating.c b/tests/regression/75-termination/07-nested-for-loop-terminating.c index 4b3395bd11..a94f3f360c 100644 --- a/tests/regression/75-termination/07-nested-for-loop-terminating.c +++ b/tests/regression/75-termination/07-nested-for-loop-terminating.c @@ -1,20 +1,17 @@ -// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() -{ - int rows = 3; - int columns = 4; +int main() { + int rows = 3; + int columns = 4; - // Nested loop to iterate over rows and columns - for (int i = 1; i <= rows; i++) - { - for (int j = 1; j <= columns; j++) - { - printf("(%d, %d) ", i, j); - } - printf("\n"); + // Nested loop to iterate over rows and columns + for (int i = 1; i <= rows; i++) { + for (int j = 1; j <= columns; j++) { + printf("(%d, %d) ", i, j); } + printf("\n"); + } - return 0; + return 0; } diff --git a/tests/regression/75-termination/08-nested-for-loop-nonterminating.c b/tests/regression/75-termination/08-nested-for-loop-nonterminating.c index 818146e456..e78e819cc0 100644 --- a/tests/regression/75-termination/08-nested-for-loop-nonterminating.c +++ b/tests/regression/75-termination/08-nested-for-loop-nonterminating.c @@ -1,19 +1,16 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() -{ - int outerCount, innerCount; +int main() { + int outerCount, innerCount; - for (outerCount = 1; outerCount <= 3; outerCount++) - { - for (innerCount = 1;; innerCount++) - { - printf("(%d, %d) ", outerCount, innerCount); - } - - printf("\n"); + for (outerCount = 1; outerCount <= 3; outerCount++) { + for (innerCount = 1;; innerCount++) { + printf("(%d, %d) ", outerCount, innerCount); } - return 0; + printf("\n"); + } + + return 0; } diff --git a/tests/regression/75-termination/09-complex-for-loop-terminating.c b/tests/regression/75-termination/09-complex-for-loop-terminating.c index 90591c7554..018fba6822 100644 --- a/tests/regression/75-termination/09-complex-for-loop-terminating.c +++ b/tests/regression/75-termination/09-complex-for-loop-terminating.c @@ -1,87 +1,67 @@ -// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --set sem.int.signed_overflow assume_none -// Goblint does not finish this test +// TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --set sem.int.signed_overflow assume_none #include -int main() -{ - int i, j, k; +int main() { + int i, j, k; - // Outer loop - for (i = 1; i <= 5; i++) - { - // Inner loop 1 - for (j = 1; j <= i; j++) - { - printf("%d ", j); - } - printf("\n"); - - // Inner loop 2 - for (k = i; k >= 1; k--) - { - printf("%d ", k); - } - printf("\n"); + // Outer loop + for (i = 1; i <= 5; i++) { + // Inner loop 1 + for (j = 1; j <= i; j++) { + printf("%d ", j); } + printf("\n"); - // Additional loop - for (i = 5; i >= 1; i--) - { - for (j = i; j >= 1; j--) - { - printf("%d ", j); - } - printf("\n"); + // Inner loop 2 + for (k = i; k >= 1; k--) { + printf("%d ", k); } + printf("\n"); + } - // Loop with conditions - for (i = 1; i <= 10; i++) - { - if (i % 2 == 0) - { - printf("%d is even\n", i); - } - else - { - printf("%d is odd\n", i); - } + // Additional loop + for (i = 5; i >= 1; i--) { + for (j = i; j >= 1; j--) { + printf("%d ", j); } + printf("\n"); + } - // Loop with nested conditions - for (i = 1; i <= 10; i++) - { - printf("Number: %d - ", i); - if (i < 5) - { - printf("Less than 5\n"); - } - else if (i > 5) - { - printf("Greater than 5\n"); - } - else - { - printf("Equal to 5\n"); - } + // Loop with conditions + for (i = 1; i <= 10; i++) { + if (i % 2 == 0) { + printf("%d is even\n", i); + } else { + printf("%d is odd\n", i); } + } - // Loop with a break statement - for (i = 1; i <= 10; i++) - { - printf("%d ", i); - if (i == 5) - { - break; - } + // Loop with nested conditions + for (i = 1; i <= 10; i++) { + printf("Number: %d - ", i); + if (i < 5) { + printf("Less than 5\n"); + } else if (i > 5) { + printf("Greater than 5\n"); + } else { + printf("Equal to 5\n"); } - printf("\n"); + } - // Loop with multiple variables - int a, b, c; - for (a = 1, b = 2, c = 3; a <= 10; a++, b += 2, c += 3) - { - printf("%d %d %d\n", a, b, c); + // Loop with a break statement + for (i = 1; i <= 10; i++) { + printf("%d ", i); + if (i == 5) { + break; } + } + printf("\n"); + + // Loop with multiple variables + int a, b, c; + for (a = 1, b = 2, c = 3; a <= 10; a++, b += 2, c += 3) { + printf("%d %d %d\n", a, b, c); + } - return 0; + return 0; } diff --git a/tests/regression/75-termination/10-complex-loop-terminating.c b/tests/regression/75-termination/10-complex-loop-terminating.c index e33139cced..88bf6a4565 100644 --- a/tests/regression/75-termination/10-complex-loop-terminating.c +++ b/tests/regression/75-termination/10-complex-loop-terminating.c @@ -1,124 +1,102 @@ -// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --set sem.int.signed_overflow assume_none -// Goblint does not finish this test +// TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --set sem.int.signed_overflow assume_none #include -int main() -{ - int i = 1; - int j = 1; - int k = 5; +int main() { + int i = 1; + int j = 1; + int k = 5; - // Outer while loop - while (i <= 5) - { - // Inner while loop 1 - while (j <= i) - { - printf("%d ", j); - j++; - } - printf("\n"); - j = 1; - - // Inner while loop 2 - while (k >= 1) - { - printf("%d ", k); - k--; - } - printf("\n"); - k = 5; - - i++; + // Outer while loop + while (i <= 5) { + // Inner while loop 1 + while (j <= i) { + printf("%d ", j); + j++; } + printf("\n"); + j = 1; - // Additional while loop - i = 5; - while (i >= 1) - { - j = i; - while (j >= 1) - { - printf("%d ", j); - j--; - } - printf("\n"); - i--; + // Inner while loop 2 + while (k >= 1) { + printf("%d ", k); + k--; } + printf("\n"); + k = 5; + + i++; + } - // Loop with conditions - i = 1; - while (i <= 10) - { - if (i % 2 == 0) - { - printf("%d is even\n", i); - } - else - { - printf("%d is odd\n", i); - } - i++; + // Additional while loop + i = 5; + while (i >= 1) { + j = i; + while (j >= 1) { + printf("%d ", j); + j--; } + printf("\n"); + i--; + } - // Loop with nested conditions - i = 1; - while (i <= 10) - { - printf("Number: %d - ", i); - if (i < 5) - { - printf("Less than 5\n"); - } - else if (i > 5) - { - printf("Greater than 5\n"); - } - else - { - printf("Equal to 5\n"); - } - i++; + // Loop with conditions + i = 1; + while (i <= 10) { + if (i % 2 == 0) { + printf("%d is even\n", i); + } else { + printf("%d is odd\n", i); } + i++; + } - // Loop with a break statement - i = 1; - while (i <= 10) - { - printf("%d ", i); - if (i == 5) - { - break; - } - i++; + // Loop with nested conditions + i = 1; + while (i <= 10) { + printf("Number: %d - ", i); + if (i < 5) { + printf("Less than 5\n"); + } else if (i > 5) { + printf("Greater than 5\n"); + } else { + printf("Equal to 5\n"); } - printf("\n"); + i++; + } - // Loop with a continue statement - i = 1; - while (i <= 10) - { - if (i % 2 == 0) - { - i++; - continue; - } - printf("%d ", i); - i++; + // Loop with a break statement + i = 1; + while (i <= 10) { + printf("%d ", i); + if (i == 5) { + break; } - printf("\n"); + i++; + } + printf("\n"); - // Loop with multiple variables - int a = 1; - int b = 2; - int c = 3; - while (a <= 10) - { - printf("%d %d %d\n", a, b, c); - a++; - b += 2; - c += 3; + // Loop with a continue statement + i = 1; + while (i <= 10) { + if (i % 2 == 0) { + i++; + continue; } + printf("%d ", i); + i++; + } + printf("\n"); + + // Loop with multiple variables + int a = 1; + int b = 2; + int c = 3; + while (a <= 10) { + printf("%d %d %d\n", a, b, c); + a++; + b += 2; + c += 3; + } - return 0; + return 0; } diff --git a/tests/regression/75-termination/11-loopless-termination.c b/tests/regression/75-termination/11-loopless-termination.c index 01f9a953e0..a1846905fc 100644 --- a/tests/regression/75-termination/11-loopless-termination.c +++ b/tests/regression/75-termination/11-loopless-termination.c @@ -1,7 +1,7 @@ -// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { - printf("Terminating code without a loop\n"); - return 0; + printf("Terminating code without a loop\n"); + return 0; } diff --git a/tests/regression/75-termination/12-do-while-instant-terminating.c b/tests/regression/75-termination/12-do-while-instant-terminating.c index b34dff3f5f..087b88f1f5 100644 --- a/tests/regression/75-termination/12-do-while-instant-terminating.c +++ b/tests/regression/75-termination/12-do-while-instant-terminating.c @@ -1,15 +1,13 @@ -// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() -{ - int i = 0; +int main() { + int i = 0; - do - { - printf("Inside the do-while loop\n"); - } while (i > 0); + do { + printf("Inside the do-while loop\n"); + } while (i > 0); - printf("Exited the loop\n"); - return 0; + printf("Exited the loop\n"); + return 0; } diff --git a/tests/regression/75-termination/13-do-while-terminating.c b/tests/regression/75-termination/13-do-while-terminating.c index 651acb8fd8..34343d6ba6 100644 --- a/tests/regression/75-termination/13-do-while-terminating.c +++ b/tests/regression/75-termination/13-do-while-terminating.c @@ -1,16 +1,14 @@ -// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() -{ - int i = 1; +int main() { + int i = 1; - do - { - printf("Inside the do-while loop\n"); - i++; - } while (i <= 5); + do { + printf("Inside the do-while loop\n"); + i++; + } while (i <= 5); - printf("Exited the loop\n"); - return 0; + printf("Exited the loop\n"); + return 0; } diff --git a/tests/regression/75-termination/14-do-while-nonterminating.c b/tests/regression/75-termination/14-do-while-nonterminating.c index 1e05e2be6e..6473fdc20d 100644 --- a/tests/regression/75-termination/14-do-while-nonterminating.c +++ b/tests/regression/75-termination/14-do-while-nonterminating.c @@ -1,16 +1,14 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() -{ - int i = 1; +int main() { + int i = 1; - do - { - printf("Inside the do-while loop\n"); - i++; - } while (i >= 2); + do { + printf("Inside the do-while loop\n"); + i++; + } while (i >= 2); - printf("Exited the loop\n"); - return 0; + printf("Exited the loop\n"); + return 0; } diff --git a/tests/regression/75-termination/15-complex-loop-combination-terminating.c b/tests/regression/75-termination/15-complex-loop-combination-terminating.c index 64afaf30e1..23282d24b1 100644 --- a/tests/regression/75-termination/15-complex-loop-combination-terminating.c +++ b/tests/regression/75-termination/15-complex-loop-combination-terminating.c @@ -1,110 +1,90 @@ -// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --set sem.int.signed_overflow assume_none -// Goblint does not finish this test +// TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --set sem.int.signed_overflow assume_none #include -int main() -{ - // Non-nested loops - int i; +int main() { + // Non-nested loops + int i; - // for loop - for (i = 1; i <= 10; i++) - { - printf("For loop iteration: %d\n", i); - } + // for loop + for (i = 1; i <= 10; i++) { + printf("For loop iteration: %d\n", i); + } - // while loop - int j = 1; - while (j <= 10) - { - printf("While loop iteration: %d\n", j); - j++; - } + // while loop + int j = 1; + while (j <= 10) { + printf("While loop iteration: %d\n", j); + j++; + } - // do-while loop - int k = 1; - do - { - printf("Do-While loop iteration: %d\n", k); - k++; - } while (k <= 10); + // do-while loop + int k = 1; + do { + printf("Do-While loop iteration: %d\n", k); + k++; + } while (k <= 10); - // Nested loops - int a, b; + // Nested loops + int a, b; - // Nested for and while loop - for (a = 1; a <= 5; a++) - { - int c = 1; - while (c <= a) - { - printf("Nested For-While loop: %d\n", c); - c++; - } + // Nested for and while loop + for (a = 1; a <= 5; a++) { + int c = 1; + while (c <= a) { + printf("Nested For-While loop: %d\n", c); + c++; } + } - // Nested while and do-while loop - int x = 1; - while (x <= 5) - { - int y = 1; - do - { - printf("Nested While-Do-While loop: %d\n", y); - y++; - } while (y <= x); - x++; - } + // Nested while and do-while loop + int x = 1; + while (x <= 5) { + int y = 1; + do { + printf("Nested While-Do-While loop: %d\n", y); + y++; + } while (y <= x); + x++; + } - // Nested do-while and for loop - int p = 1; - do - { - for (int q = 1; q <= p; q++) - { - printf("Nested Do-While-For loop: %d\n", q); - } - p++; - } while (p <= 5); + // Nested do-while and for loop + int p = 1; + do { + for (int q = 1; q <= p; q++) { + printf("Nested Do-While-For loop: %d\n", q); + } + p++; + } while (p <= 5); - // Additional loops - int m; + // Additional loops + int m; - // Nested while loop with a break statement - int n = 1; - while (n <= 5) - { - printf("Outer While loop iteration: %d\n", n); - m = 1; - while (1) - { - printf("Inner While loop iteration: %d\n", m); - m++; - if (m == 4) - { - break; - } - } - n++; + // Nested while loop with a break statement + int n = 1; + while (n <= 5) { + printf("Outer While loop iteration: %d\n", n); + m = 1; + while (1) { + printf("Inner While loop iteration: %d\n", m); + m++; + if (m == 4) { + break; + } } + n++; + } - // Loop with nested conditions - for (int v = 1; v <= 10; v++) - { - printf("Loop with Nested Conditions: %d - ", v); - if (v < 5) - { - printf("Less than 5\n"); - } - else if (v > 5) - { - printf("Greater than 5\n"); - } - else - { - printf("Equal to 5\n"); - } + // Loop with nested conditions + for (int v = 1; v <= 10; v++) { + printf("Loop with Nested Conditions: %d - ", v); + if (v < 5) { + printf("Less than 5\n"); + } else if (v > 5) { + printf("Greater than 5\n"); + } else { + printf("Equal to 5\n"); } + } - return 0; + return 0; } diff --git a/tests/regression/75-termination/16-nested-loop-nontrivial-nonterminating.c b/tests/regression/75-termination/16-nested-loop-nontrivial-nonterminating.c index b9ccea76af..f89e28d91a 100644 --- a/tests/regression/75-termination/16-nested-loop-nontrivial-nonterminating.c +++ b/tests/regression/75-termination/16-nested-loop-nontrivial-nonterminating.c @@ -1,23 +1,20 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() -{ - int outerCount = 1; +int main() { + int outerCount = 1; - while (outerCount <= 3) - { - int innerCount = 1; + while (outerCount <= 3) { + int innerCount = 1; - while (outerCount < 3 || innerCount > 0) - { - printf("(%d, %d) ", outerCount, innerCount); - innerCount++; - } - - printf("\n"); - outerCount++; + while (outerCount < 3 || innerCount > 0) { + printf("(%d, %d) ", outerCount, innerCount); + innerCount++; } - return 0; + printf("\n"); + outerCount++; + } + + return 0; } diff --git a/tests/regression/75-termination/17-goto-terminating.c b/tests/regression/75-termination/17-goto-terminating.c index c4ba717784..3ad01cbd79 100644 --- a/tests/regression/75-termination/17-goto-terminating.c +++ b/tests/regression/75-termination/17-goto-terminating.c @@ -1,18 +1,17 @@ -// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() -{ - int num = 1; +int main() { + int num = 1; loop: - printf("Current number: %d\n", num); - num++; + printf("Current number: %d\n", num); + num++; - if (num <= 10) - { - goto loop; // We are not able to detect up-jumping gotos as terminating, we just warn about them might being nonterminating. - } + if (num <= 10) { + goto loop; // We are not able to detect up-jumping gotos as terminating, we + // just warn about them might being nonterminating. + } - return 0; + return 0; } diff --git a/tests/regression/75-termination/18-goto-nonterminating.c b/tests/regression/75-termination/18-goto-nonterminating.c index aab37803aa..e26f02ec11 100644 --- a/tests/regression/75-termination/18-goto-nonterminating.c +++ b/tests/regression/75-termination/18-goto-nonterminating.c @@ -1,15 +1,14 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() -{ - int num = 1; +int main() { + int num = 1; loop: - printf("Current number: %d\n", num); - num++; + printf("Current number: %d\n", num); + num++; - goto loop; + goto loop; - return 0; + return 0; } diff --git a/tests/regression/75-termination/19-rand-terminating.c b/tests/regression/75-termination/19-rand-terminating.c index 5d3cde9f3d..fc5d6ee7b7 100644 --- a/tests/regression/75-termination/19-rand-terminating.c +++ b/tests/regression/75-termination/19-rand-terminating.c @@ -1,31 +1,25 @@ -// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include #include #include -int main() -{ - // Seed the random number generator - srand(time(NULL)); +int main() { + // Seed the random number generator + srand(time(NULL)); - if (rand()) - { - // Loop inside the if part - for (int i = 1; i <= 5; i++) - { - printf("Loop inside if part: %d\n", i); - } + if (rand()) { + // Loop inside the if part + for (int i = 1; i <= 5; i++) { + printf("Loop inside if part: %d\n", i); } - else - { - // Loop inside the else part - int j = 1; - while (j <= 5) - { - printf("Loop inside else part: %d\n", j); - j++; - } + } else { + // Loop inside the else part + int j = 1; + while (j <= 5) { + printf("Loop inside else part: %d\n", j); + j++; } + } - return 0; + return 0; } diff --git a/tests/regression/75-termination/20-rand-nonterminating.c b/tests/regression/75-termination/20-rand-nonterminating.c index 124a19d0f8..e74c15c948 100644 --- a/tests/regression/75-termination/20-rand-nonterminating.c +++ b/tests/regression/75-termination/20-rand-nonterminating.c @@ -1,30 +1,24 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include #include #include -int main() -{ - // Seed the random number generator - srand(time(NULL)); +int main() { + // Seed the random number generator + srand(time(NULL)); - if (rand()) - { - // Loop inside the if part - for (int i = 1; i >= 0; i++) - { - printf("Loop inside if part: %d\n", i); - } + if (rand()) { + // Loop inside the if part + for (int i = 1; i >= 0; i++) { + printf("Loop inside if part: %d\n", i); } - else - { - // Loop inside the else part - int j = 1; - while (j > 0) - { - printf("Loop inside else part: %d\n", j); - } + } else { + // Loop inside the else part + int j = 1; + while (j > 0) { + printf("Loop inside else part: %d\n", j); } + } - return 0; + return 0; } diff --git a/tests/regression/75-termination/21-no-exit-on-rand-unproofable.c b/tests/regression/75-termination/21-no-exit-on-rand-unproofable.c index 3bf479b6f9..10774e3420 100644 --- a/tests/regression/75-termination/21-no-exit-on-rand-unproofable.c +++ b/tests/regression/75-termination/21-no-exit-on-rand-unproofable.c @@ -1,20 +1,16 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() -{ - int forever, i = 0; +int main() { + int forever, i = 0; -// This loop is not provable, therefore it should throw a warning - while (i < 4 || forever == 1) - { - i++; - if (i == 4) - { - if (rand()) - { - forever = 1; - } - } + // This loop is not provable, therefore it should throw a warning + while (i < 4 || forever == 1) { + i++; + if (i == 4) { + if (rand()) { + forever = 1; + } } + } } \ No newline at end of file diff --git a/tests/regression/75-termination/22-exit-on-rand-unproofable.c b/tests/regression/75-termination/22-exit-on-rand-unproofable.c index 1f1a9bbd89..3f76f05aa9 100644 --- a/tests/regression/75-termination/22-exit-on-rand-unproofable.c +++ b/tests/regression/75-termination/22-exit-on-rand-unproofable.c @@ -1,16 +1,14 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() -{ - int forever = 1; +int main() { + int forever = 1; -// This loop is not provable, therefore it should throw a warning - while (forever == 1) + // This loop is not provable, therefore it should throw a warning + while (forever == 1) { + if (rand()) // May exit, may not { - if (rand()) //May exit, may not - { - forever = 0; - } + forever = 0; } + } } \ No newline at end of file diff --git a/tests/regression/75-termination/23-exit-on-rand-terminating.c b/tests/regression/75-termination/23-exit-on-rand-terminating.c index 226f46b16e..080b3c8871 100644 --- a/tests/regression/75-termination/23-exit-on-rand-terminating.c +++ b/tests/regression/75-termination/23-exit-on-rand-terminating.c @@ -1,17 +1,16 @@ -// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra -#include +// TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include +#include -int main() -{ - int short_run, i = 0; +int main() { + int short_run, i = 0; - while (i < 90 && short_run != 1) // Currently not able to detect this as terminating - { - i++; - if (rand()) - { - short_run = 1; - } + while (i < 90 && + short_run != 1) // Currently not able to detect this as terminating + { + i++; + if (rand()) { + short_run = 1; } + } } \ No newline at end of file diff --git a/tests/regression/75-termination/24-upjumping-goto-loopless-terminating.c b/tests/regression/75-termination/24-upjumping-goto-loopless-terminating.c index 1dc261d06a..1a4ef63ff7 100644 --- a/tests/regression/75-termination/24-upjumping-goto-loopless-terminating.c +++ b/tests/regression/75-termination/24-upjumping-goto-loopless-terminating.c @@ -1,19 +1,19 @@ -// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { // Currently not able to detect up-jumping loop free gotos - goto mark2; + goto mark2; mark1: - printf("This is mark1\n"); - goto mark3; + printf("This is mark1\n"); + goto mark3; mark2: - printf("This is mark2\n"); - goto mark1; + printf("This is mark2\n"); + goto mark1; mark3: - printf("This is mark3\n"); + printf("This is mark3\n"); - return 0; + return 0; } diff --git a/tests/regression/75-termination/25-leave-loop-goto-terminating.c b/tests/regression/75-termination/25-leave-loop-goto-terminating.c index cbbb115868..35edf86938 100644 --- a/tests/regression/75-termination/25-leave-loop-goto-terminating.c +++ b/tests/regression/75-termination/25-leave-loop-goto-terminating.c @@ -1,25 +1,25 @@ -// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { - int counter = 0; + int counter = 0; - while (1) { - counter++; + while (1) { + counter++; - // Dummy code - printf("Iteration %d\n", counter); - int result = counter * 2; - printf("Result: %d\n", result); + // Dummy code + printf("Iteration %d\n", counter); + int result = counter * 2; + printf("Result: %d\n", result); - // Condition to terminate the loop - if (result >= 10) { // Apron is not able to detect this - goto end; - } + // Condition to terminate the loop + if (result >= 10) { // Apron is not able to detect this + goto end; } + } end: - printf("Loop exited. Result is greater than or equal to 10.\n"); + printf("Loop exited. Result is greater than or equal to 10.\n"); - return 0; + return 0; } diff --git a/tests/regression/75-termination/26-enter-loop-goto-terminating.c b/tests/regression/75-termination/26-enter-loop-goto-terminating.c index 17220a589b..97b46f66ca 100644 --- a/tests/regression/75-termination/26-enter-loop-goto-terminating.c +++ b/tests/regression/75-termination/26-enter-loop-goto-terminating.c @@ -1,28 +1,28 @@ -// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { - int counter = 0; + int counter = 0; - goto jump_point; + goto jump_point; - while (1) { - counter++; + while (1) { + counter++; - // Dummy code - printf("Iteration %d\n", counter); - int result = counter * 2; - jump_point: - printf("Result: %d\n", result); + // Dummy code + printf("Iteration %d\n", counter); + int result = counter * 2; + jump_point: + printf("Result: %d\n", result); - // Condition to terminate the loop - if (result >= 10) { // Apron is not able to detect this - goto end; - } + // Condition to terminate the loop + if (result >= 10) { // Apron is not able to detect this + goto end; } + } end: - printf("Loop exited. Result is greater than or equal to 10.\n"); + printf("Loop exited. Result is greater than or equal to 10.\n"); - return 0; + return 0; } diff --git a/tests/regression/75-termination/27-upjumping-goto-nonterminating.c b/tests/regression/75-termination/27-upjumping-goto-nonterminating.c index 52ad7ea820..a6621dd986 100644 --- a/tests/regression/75-termination/27-upjumping-goto-nonterminating.c +++ b/tests/regression/75-termination/27-upjumping-goto-nonterminating.c @@ -1,20 +1,20 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { - goto mark2; + goto mark2; mark1: - printf("This is mark1\n"); - goto mark3; + printf("This is mark1\n"); + goto mark3; mark2: - printf("This is mark2\n"); - goto mark1; + printf("This is mark2\n"); + goto mark1; mark3: - printf("This is mark3\n"); - goto mark1; + printf("This is mark3\n"); + goto mark1; - return 0; + return 0; } diff --git a/tests/regression/75-termination/28-do-while-continue-terminating.c b/tests/regression/75-termination/28-do-while-continue-terminating.c index f05fa4f315..b55aaf28c2 100644 --- a/tests/regression/75-termination/28-do-while-continue-terminating.c +++ b/tests/regression/75-termination/28-do-while-continue-terminating.c @@ -1,47 +1,52 @@ -// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() -{ - int i = 1; +int main() { + int i = 1; - do - { - i++; - printf("Inside the do-while loop\n"); - if (i % 2 == 0) { + do { + i++; + printf("Inside the do-while loop\n"); + if (i % 2 == 0) { - printf("Skipping %i is even\n", i); - continue; // This is handled as an goto to line 8 and there an up-jumping goto - } - } while (i <= 5); + printf("Skipping %i is even\n", i); + continue; // This is handled as an goto to line 8 and there an up-jumping + // goto + } + } while (i <= 5); - printf("Exited the loop\n"); - return 0; + printf("Exited the loop\n"); + return 0; } /* -NOTE: -Test 28: does not terminate but should terminate (test case "28-do-while-continue-terminating.c") -Reason: upjumping goto +NOTE: +Test 28: does not terminate but should terminate (test case +"28-do-while-continue-terminating.c") Reason: upjumping goto -If one has a look at the generated CIL output (attached at the bottom of this file), one can see that the "continue" is -translated in a "goto" with a corresponding label "__Cont". This label points to the loop-exit condition. Since the condition -is part of the loop, its location is evaluated to 8-17. -The location of the goto "goto __Cont" is located in line 15. -To provide soundness for the analysis, the preprocessing detects upjumping gotos with the help of its location. -In case such a goto is detected, the program is classified as non-terminating. -Due to this inserted goto (which is a result of the "continue"), an upjumping goto is located, which makes this program non-terminating. +If one has a look at the generated CIL output (attached at the bottom of this +file), one can see that the "continue" is translated in a "goto" with a +corresponding label "__Cont". This label points to the loop-exit condition. +Since the condition is part of the loop, its location is evaluated to 8-17. The +location of the goto "goto __Cont" is located in line 15. To provide soundness +for the analysis, the preprocessing detects upjumping gotos with the help of its +location. In case such a goto is detected, the program is classified as +non-terminating. Due to this inserted goto (which is a result of the +"continue"), an upjumping goto is located, which makes this program +non-terminating. -It should be noted that this issue happens when "do while"-loops and "continues" are combined. -If one combines "while"-loops and "continues", the analysis can still classify the loop as terminating. -The reason for that can be seen in the second CIL output, where the "do while"-loop is replaced by a "while"-loop. -Instead of creating a new label, the "while-continue" label of the loop is reused. Also, this goto statement is not specified as a goto, -but as a Continue statement. Hence, it is not analyzed for the upjumping gotos, which does not lead to the problem as with the "do while". +It should be noted that this issue happens when "do while"-loops and "continues" +are combined. If one combines "while"-loops and "continues", the analysis can +still classify the loop as terminating. The reason for that can be seen in the +second CIL output, where the "do while"-loop is replaced by a "while"-loop. +Instead of creating a new label, the "while-continue" label of the loop is +reused. Also, this goto statement is not specified as a goto, but as a Continue +statement. Hence, it is not analyzed for the upjumping gotos, which does not +lead to the problem as with the "do while". -------------------- SHORTENED CIL output for Test 28 (DO WHILE): ------------------- -int main(void) +------------------- SHORTENED CIL output for Test 28 (DO WHILE): +------------------- int main(void) {{{{ #line 8 while (1) { @@ -51,7 +56,7 @@ int main(void) #line 15 goto __Cont; } - __Cont: + __Cont: #line 8 if (! (i <= 5)) { #line 8 @@ -66,9 +71,8 @@ int main(void) }} -------------------- SHORTENED CIL output for Test 28 (WHILE): ------------------- -Test 28: replacing DO WHILE with WHILE: -int main(void) +------------------- SHORTENED CIL output for Test 28 (WHILE): +------------------- Test 28: replacing DO WHILE with WHILE: int main(void) {{{{ #line 8 while (1) { diff --git a/tests/regression/75-termination/29-do-while-continue-nonterminating.c b/tests/regression/75-termination/29-do-while-continue-nonterminating.c index 896d8fea95..be3e7e12de 100644 --- a/tests/regression/75-termination/29-do-while-continue-nonterminating.c +++ b/tests/regression/75-termination/29-do-while-continue-nonterminating.c @@ -1,21 +1,19 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() -{ - int i = 1; +int main() { + int i = 1; - do - { - printf("Inside the do-while loop\n"); - i++; + do { + printf("Inside the do-while loop\n"); + i++; - if(i%2) { - printf("Continue as %i is odd\n", i); - continue; - } - } while (i >= 2); + if (i % 2) { + printf("Continue as %i is odd\n", i); + continue; + } + } while (i >= 2); - printf("Exited the loop\n"); - return 0; + printf("Exited the loop\n"); + return 0; } diff --git a/tests/regression/75-termination/30-goto-out-of-inner-loop-terminating.c b/tests/regression/75-termination/30-goto-out-of-inner-loop-terminating.c index a92dcd2bc8..e2eff29c8b 100644 --- a/tests/regression/75-termination/30-goto-out-of-inner-loop-terminating.c +++ b/tests/regression/75-termination/30-goto-out-of-inner-loop-terminating.c @@ -1,31 +1,32 @@ -// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { - int rows = 5; - int columns = 5; + int rows = 5; + int columns = 5; - // Outer loop for rows - for (int i = 1; i <= rows; i++) { - // Inner loop for columns - for (int j = 1; j <= columns; j++) { - if (j == 3) { - goto outer_loop; // Jump to the label "outer_loop" - } - printf("(%d, %d) ", i, j); - } - printf("Not Skipped?\n"); - outer_loop:; // Label for the outer loop - printf("Skipped!\n"); + // Outer loop for rows + for (int i = 1; i <= rows; i++) { + // Inner loop for columns + for (int j = 1; j <= columns; j++) { + if (j == 3) { + goto outer_loop; // Jump to the label "outer_loop" + } + printf("(%d, %d) ", i, j); } + printf("Not Skipped?\n"); + outer_loop:; // Label for the outer loop + printf("Skipped!\n"); + } - return 0; + return 0; } -/* +/* NOTE: In case we do NOT assume no-overflow: Test 30: terminates (test case "30-goto-out-of-inner-loop-terminating.c") -Test 35: does not terminate (test case "35-goto-out-of-inner-loop-with-print-terminating.c") +Test 35: does not terminate (test case +"35-goto-out-of-inner-loop-with-print-terminating.c") -The reason is explained in "35-goto-out-of-inner-loop-with-print-terminating.c" +The reason is explained in "35-goto-out-of-inner-loop-with-print-terminating.c" */ diff --git a/tests/regression/75-termination/31-goto-out-of-inner-loop-nonterminating.c b/tests/regression/75-termination/31-goto-out-of-inner-loop-nonterminating.c index 722694eb88..756a93414b 100644 --- a/tests/regression/75-termination/31-goto-out-of-inner-loop-nonterminating.c +++ b/tests/regression/75-termination/31-goto-out-of-inner-loop-nonterminating.c @@ -1,23 +1,23 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { - int rows = 5; - int columns = 5; + int rows = 5; + int columns = 5; - // Outer loop for rows - for (int i = 1; 1; i++) { - // Inner loop for columns - for (int j = 1; j <= columns; j++) { - if (j == 3) { - printf("Goto as continue for outer loop\n"); - goto outer_loop; // Jump to the label "outer_loop" - } - printf("(%d, %d) ", i, j); - } - printf("\n"); - outer_loop:; // Label for the outer loop + // Outer loop for rows + for (int i = 1; 1; i++) { + // Inner loop for columns + for (int j = 1; j <= columns; j++) { + if (j == 3) { + printf("Goto as continue for outer loop\n"); + goto outer_loop; // Jump to the label "outer_loop" + } + printf("(%d, %d) ", i, j); } + printf("\n"); + outer_loop:; // Label for the outer loop + } - return 0; + return 0; } diff --git a/tests/regression/75-termination/32-multithread-terminating.c b/tests/regression/75-termination/32-multithread-terminating.c index 1f98b88eee..beab8564f5 100644 --- a/tests/regression/75-termination/32-multithread-terminating.c +++ b/tests/regression/75-termination/32-multithread-terminating.c @@ -1,27 +1,27 @@ -// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra -#include +// TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include +#include #include // Thread function -void* printPID(void* arg) { - pid_t pid = getpid(); - pthread_t tid = pthread_self(); - printf("Thread ID: %lu, Process ID: %d\n", (unsigned long)tid, pid); - return NULL; +void *printPID(void *arg) { + pid_t pid = getpid(); + pthread_t tid = pthread_self(); + printf("Thread ID: %lu, Process ID: %d\n", (unsigned long)tid, pid); + return NULL; } int main() { - // Create three threads - pthread_t thread1, thread2, thread3; - pthread_create(&thread1, NULL, printPID, NULL); - pthread_create(&thread2, NULL, printPID, NULL); - pthread_create(&thread3, NULL, printPID, NULL); + // Create three threads + pthread_t thread1, thread2, thread3; + pthread_create(&thread1, NULL, printPID, NULL); + pthread_create(&thread2, NULL, printPID, NULL); + pthread_create(&thread3, NULL, printPID, NULL); - // Wait for all threads to finish - pthread_join(thread1, NULL); - pthread_join(thread2, NULL); - pthread_join(thread3, NULL); + // Wait for all threads to finish + pthread_join(thread1, NULL); + pthread_join(thread2, NULL); + pthread_join(thread3, NULL); - return 0; + return 0; } diff --git a/tests/regression/75-termination/33-multithread-nonterminating.c b/tests/regression/75-termination/33-multithread-nonterminating.c index 007af3b57b..278c107821 100644 --- a/tests/regression/75-termination/33-multithread-nonterminating.c +++ b/tests/regression/75-termination/33-multithread-nonterminating.c @@ -1,36 +1,37 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra -#include +// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -#include -#include +#include #include +#include +#include // Thread function -void* printPID(void* arg) { - pid_t pid = getpid(); - pthread_t tid = pthread_self(); - while(1) { - printf("Thread ID: %lu, Process ID: %d\n", (unsigned long)tid, pid); - struct timespec sleepTime; - sleepTime.tv_sec = 1; // Seconds - sleepTime.tv_nsec = 100000000 + (rand() % 200000000); // Nanoseconds (0.1 seconds + rand) - printf("Sleep for %ld nsec\n", sleepTime.tv_nsec); - nanosleep(&sleepTime, NULL); - } - return NULL; +void *printPID(void *arg) { + pid_t pid = getpid(); + pthread_t tid = pthread_self(); + while (1) { + printf("Thread ID: %lu, Process ID: %d\n", (unsigned long)tid, pid); + struct timespec sleepTime; + sleepTime.tv_sec = 1; // Seconds + sleepTime.tv_nsec = + 100000000 + (rand() % 200000000); // Nanoseconds (0.1 seconds + rand) + printf("Sleep for %ld nsec\n", sleepTime.tv_nsec); + nanosleep(&sleepTime, NULL); + } + return NULL; } int main() { - // Create three threads - pthread_t thread1, thread2, thread3; - pthread_create(&thread1, NULL, printPID, NULL); - pthread_create(&thread2, NULL, printPID, NULL); - pthread_create(&thread3, NULL, printPID, NULL); + // Create three threads + pthread_t thread1, thread2, thread3; + pthread_create(&thread1, NULL, printPID, NULL); + pthread_create(&thread2, NULL, printPID, NULL); + pthread_create(&thread3, NULL, printPID, NULL); - // Wait for all threads to finish - pthread_join(thread1, NULL); - pthread_join(thread2, NULL); - pthread_join(thread3, NULL); + // Wait for all threads to finish + pthread_join(thread1, NULL); + pthread_join(thread2, NULL); + pthread_join(thread3, NULL); - return 0; + return 0; } diff --git a/tests/regression/75-termination/34-nested-for-loop-nonterminating.c b/tests/regression/75-termination/34-nested-for-loop-nonterminating.c index 29e4ff3835..3384ed0f60 100644 --- a/tests/regression/75-termination/34-nested-for-loop-nonterminating.c +++ b/tests/regression/75-termination/34-nested-for-loop-nonterminating.c @@ -1,19 +1,16 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() -{ - int outerCount, innerCount; +int main() { + int outerCount, innerCount; - for (outerCount = 1; outerCount <= 3; outerCount++) - { - for (innerCount = 1; innerCount > 0; innerCount++) - { - printf("(%d, %d) ", outerCount, innerCount); - } - - printf("\n"); + for (outerCount = 1; outerCount <= 3; outerCount++) { + for (innerCount = 1; innerCount > 0; innerCount++) { + printf("(%d, %d) ", outerCount, innerCount); } - return 0; + printf("\n"); + } + + return 0; } diff --git a/tests/regression/75-termination/35-goto-out-of-inner-loop-with-print-terminating.c b/tests/regression/75-termination/35-goto-out-of-inner-loop-with-print-terminating.c index bb8bbacbf0..646f39111a 100644 --- a/tests/regression/75-termination/35-goto-out-of-inner-loop-with-print-terminating.c +++ b/tests/regression/75-termination/35-goto-out-of-inner-loop-with-print-terminating.c @@ -1,34 +1,38 @@ -// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set "ana.activated[+]" apron --enable ana.int.interval --set ana.apron.domain polyhedra --set sem.int.signed_overflow assume_none +// TERM PARAM: --set "ana.activated[+]" termination --set "ana.activated[+]" apron --enable ana.int.interval --set ana.apron.domain polyhedra --set sem.int.signed_overflow assume_none #include int main() { - int rows = 5; - int columns = 5; + int rows = 5; + int columns = 5; - // Outer loop for rows - for (int i = 1; i <= rows; i++) { - // Inner loop for columns - for (int j = 1; j <= columns; j++) { - if (j == 3) { - goto outer_loop; // Jump to the label "outer_loop" - } - printf("(%d, %d) ", i, j); - } - outer_loop:; // Label for the outer loop - printf("\n"); + // Outer loop for rows + for (int i = 1; i <= rows; i++) { + // Inner loop for columns + for (int j = 1; j <= columns; j++) { + if (j == 3) { + goto outer_loop; // Jump to the label "outer_loop" + } + printf("(%d, %d) ", i, j); } + outer_loop:; // Label for the outer loop + printf("\n"); + } - return 0; + return 0; } -/* +/* NOTE: In case we do NOT assume no-overflow: Test 30: terminates (test case "30-goto-out-of-inner-loop-terminating.c") -Test 35: does not terminate (test case "35-goto-out-of-inner-loop-with-print-terminating.c") +Test 35: does not terminate (test case +"35-goto-out-of-inner-loop-with-print-terminating.c") -The only difference between Test 30 and Test 35 is line 17. Test 30 has an additional statement, and Test 35 continues already with the label. -This difference in Test 35 leads to an overflow in line 11, and hence to the non-termination. -This overflow is created by a WPoint Issue. By enabling the no-overflow option this issue can be fixed and, both test cases are correctly detected as terminating. +The only difference between Test 30 and Test 35 is line 17. Test 30 has an +additional statement, and Test 35 continues already with the label. This +difference in Test 35 leads to an overflow in line 11, and hence to the +non-termination. This overflow is created by a WPoint Issue. By enabling the +no-overflow option this issue can be fixed and, both test cases are correctly +detected as terminating. (The overflow also happens without the termination analysis enabled.) */ diff --git a/tests/regression/75-termination/36-recursion-terminating.c b/tests/regression/75-termination/36-recursion-terminating.c index 583f8ccca1..533778332f 100644 --- a/tests/regression/75-termination/36-recursion-terminating.c +++ b/tests/regression/75-termination/36-recursion-terminating.c @@ -1,22 +1,22 @@ -// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include void recursiveFunction(int n) { - // Base case: When n reaches 0, stop recursion - if (n == 0) { - printf("Terminating recursion\n"); - return; - } + // Base case: When n reaches 0, stop recursion + if (n == 0) { + printf("Terminating recursion\n"); + return; + } - printf("Recursive call with n = %d\n", n); + printf("Recursive call with n = %d\n", n); - // Recursive call: Decrement n and call the function again - recursiveFunction(n - 1); + // Recursive call: Decrement n and call the function again + recursiveFunction(n - 1); } int main() { - // Call the recursive function with an initial value - recursiveFunction(5); + // Call the recursive function with an initial value + recursiveFunction(5); - return 0; + return 0; } diff --git a/tests/regression/75-termination/37-recursion-nonterminating.c b/tests/regression/75-termination/37-recursion-nonterminating.c index d20adea294..089a4d3bcc 100644 --- a/tests/regression/75-termination/37-recursion-nonterminating.c +++ b/tests/regression/75-termination/37-recursion-nonterminating.c @@ -1,22 +1,22 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --enable ana.context.widen +// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --enable ana.context.widen #include void recursiveFunction(int n) { - // Base case: When n reaches 0, stop recursion - if (n == 30) { - printf("Terminating recursion\n"); - return; - } + // Base case: When n reaches 0, stop recursion + if (n == 30) { + printf("Terminating recursion\n"); + return; + } - printf("Recursive call with n = %d\n", n); + printf("Recursive call with n = %d\n", n); - // Recursive call: Decrement n and call the function again - recursiveFunction(n - 1); + // Recursive call: Decrement n and call the function again + recursiveFunction(n - 1); } int main() { - // Call the recursive function with an initial value - recursiveFunction(5); + // Call the recursive function with an initial value + recursiveFunction(5); - return 0; + return 0; } diff --git a/tests/regression/75-termination/38-recursion-nested-terminating.c b/tests/regression/75-termination/38-recursion-nested-terminating.c index 4cede747f2..eace365a44 100644 --- a/tests/regression/75-termination/38-recursion-nested-terminating.c +++ b/tests/regression/75-termination/38-recursion-nested-terminating.c @@ -1,36 +1,36 @@ -// TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include void innerRecursiveFunction(int n) { - if (n == 0) { - printf("Terminating inner recursion\n"); - return; - } + if (n == 0) { + printf("Terminating inner recursion\n"); + return; + } - printf("Inner recursive call with n = %d\n", n); + printf("Inner recursive call with n = %d\n", n); - // Recursive call to the innerRecursiveFunction - innerRecursiveFunction(n - 1); + // Recursive call to the innerRecursiveFunction + innerRecursiveFunction(n - 1); } void outerRecursiveFunction(int n) { - if (n == 0) { - printf("Terminating outer recursion\n"); - return; - } + if (n == 0) { + printf("Terminating outer recursion\n"); + return; + } - printf("Outer recursive call with n = %d\n", n); + printf("Outer recursive call with n = %d\n", n); - // Recursive call to the outerRecursiveFunction - outerRecursiveFunction(n - 1); + // Recursive call to the outerRecursiveFunction + outerRecursiveFunction(n - 1); - // Call to the innerRecursiveFunction - innerRecursiveFunction(n); + // Call to the innerRecursiveFunction + innerRecursiveFunction(n); } int main() { - // Call the outerRecursiveFunction with an initial value - outerRecursiveFunction(3); + // Call the outerRecursiveFunction with an initial value + outerRecursiveFunction(3); - return 0; + return 0; } diff --git a/tests/regression/75-termination/39-recursion-nested-nonterminating.c b/tests/regression/75-termination/39-recursion-nested-nonterminating.c index 2d3239f371..8b57f83857 100644 --- a/tests/regression/75-termination/39-recursion-nested-nonterminating.c +++ b/tests/regression/75-termination/39-recursion-nested-nonterminating.c @@ -1,26 +1,26 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include void innerRecursiveFunction() { - printf("Nested recursive call\n"); + printf("Nested recursive call\n"); - // Recursive call to the innerRecursiveFunction - innerRecursiveFunction(); + // Recursive call to the innerRecursiveFunction + innerRecursiveFunction(); } void outerRecursiveFunction() { - printf("Outer recursive call\n"); + printf("Outer recursive call\n"); - // Recursive call to the outerRecursiveFunction - outerRecursiveFunction(); + // Recursive call to the outerRecursiveFunction + outerRecursiveFunction(); - // Call to the innerRecursiveFunction - innerRecursiveFunction(); + // Call to the innerRecursiveFunction + innerRecursiveFunction(); } int main() { - // Call the outerRecursiveFunction - outerRecursiveFunction(); + // Call the outerRecursiveFunction + outerRecursiveFunction(); - return 0; + return 0; } diff --git a/tests/regression/75-termination/40-complex-conditions.c b/tests/regression/75-termination/40-complex-conditions.c index 2f342e89a0..d5fe6b808a 100644 --- a/tests/regression/75-termination/40-complex-conditions.c +++ b/tests/regression/75-termination/40-complex-conditions.c @@ -1,39 +1,33 @@ -// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { - int i; + int i; - // Loop with a continue statement - for (i = 1; i <= 10; i++) - { - if (i % 2 == 0) - { - continue; - } - printf("%d ", i); + // Loop with a continue statement + for (i = 1; i <= 10; i++) { + if (i % 2 == 0) { + continue; } - printf("\n"); + printf("%d ", i); + } + printf("\n"); - // Loop with complex conditions - for (i = 1; i <= 10; i++) - { - if (i > 5 && i % 2 == 0) - { - printf("%d ", i); - } + // Loop with complex conditions + for (i = 1; i <= 10; i++) { + if (i > 5 && i % 2 == 0) { + printf("%d ", i); } - printf("\n"); + } + printf("\n"); - // Loop with complex conditions - i = 1; - while (i <= 10) - { - if (i > 5 && i % 2 == 0) - { - printf("%d ", i); - } - i++; + // Loop with complex conditions + i = 1; + while (i <= 10) { + if (i > 5 && i % 2 == 0) { + printf("%d ", i); } - printf("\n"); + i++; + } + printf("\n"); } \ No newline at end of file diff --git a/tests/regression/75-termination/41-more-tests.c b/tests/regression/75-termination/41-more-tests.c index 112f95c3ee..272be43293 100644 --- a/tests/regression/75-termination/41-more-tests.c +++ b/tests/regression/75-termination/41-more-tests.c @@ -1,30 +1,26 @@ -// TODO TERM PARAM: --set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra -#include +// TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include int main() { - // Loop with a continue statement - for (int r = 1; r <= 10; r++) - { - if (r % 3 == 0) - { - continue; - } - printf("Loop with Continue: %d\n", r); + // Loop with a continue statement + for (int r = 1; r <= 10; r++) { + if (r % 3 == 0) { + continue; } + printf("Loop with Continue: %d\n", r); + } - // Loop with multiple conditions - int s = 1; - while (s <= 10 && s % 2 == 0) - { - printf("Loop with Multiple Conditions: %d\n", s); - s++; - } + // Loop with multiple conditions + int s = 1; + while (s <= 10 && s % 2 == 0) { + printf("Loop with Multiple Conditions: %d\n", s); + s++; + } - // Loop with multiple variables - int t, u; - for (t = 1, u = 10; t <= 5 && u >= 5; t++, u--) - { - printf("Loop with Multiple Variables: %d %d\n", t, u); - } - } \ No newline at end of file + // Loop with multiple variables + int t, u; + for (t = 1, u = 10; t <= 5 && u >= 5; t++, u--) { + printf("Loop with Multiple Variables: %d %d\n", t, u); + } +} \ No newline at end of file From 1faf7290699344def397dbd6ade78da546e494d5 Mon Sep 17 00:00:00 2001 From: Isidor Zweckstetter Date: Mon, 10 Jul 2023 14:51:01 +0200 Subject: [PATCH 0292/1312] added autoTune for termination analysis in mainGoblint.ml --- src/maingoblint.ml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/maingoblint.ml b/src/maingoblint.ml index 8944d87ea0..f9abd9637b 100644 --- a/src/maingoblint.ml +++ b/src/maingoblint.ml @@ -159,7 +159,12 @@ let check_arguments () = ^ String.concat " and " @@ List.map (fun s -> "'" ^ s ^ "'") imprecise_options) ); if get_bool "solvers.td3.space" && get_bool "solvers.td3.remove-wpoint" then fail "solvers.td3.space is incompatible with solvers.td3.remove-wpoint"; - if get_bool "solvers.td3.space" && get_string "solvers.td3.side_widen" = "sides-local" then fail "solvers.td3.space is incompatible with solvers.td3.side_widen = 'sides-local'" + if get_bool "solvers.td3.space" && get_string "solvers.td3.side_widen" = "sides-local" then fail "solvers.td3.space is incompatible with solvers.td3.side_widen = 'sides-local'"; + if List.mem "termination" @@ get_string_list "ana.activated" then ( + set_list "ana.activated" (GobConfig.get_list "ana.activated" @ [`String ("evermultithreaded")]); + set_string "sem.int.signed_overflow" "assume_none"; + warn "termination analysis implicitly activates evermultithreaded analysis and set sem.int.signed_overflow to assume_none" + ) (** Initialize some globals in other modules. *) let handle_flags () = From 43bf64daf294ad4c0798a63fd7738d6c32235c82 Mon Sep 17 00:00:00 2001 From: Isidor Zweckstetter Date: Mon, 10 Jul 2023 16:10:56 +0200 Subject: [PATCH 0293/1312] temporarly removed sem.int.signed_overflow assume_none autotuning --- src/maingoblint.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/maingoblint.ml b/src/maingoblint.ml index f9abd9637b..371deb989a 100644 --- a/src/maingoblint.ml +++ b/src/maingoblint.ml @@ -162,7 +162,7 @@ let check_arguments () = if get_bool "solvers.td3.space" && get_string "solvers.td3.side_widen" = "sides-local" then fail "solvers.td3.space is incompatible with solvers.td3.side_widen = 'sides-local'"; if List.mem "termination" @@ get_string_list "ana.activated" then ( set_list "ana.activated" (GobConfig.get_list "ana.activated" @ [`String ("evermultithreaded")]); - set_string "sem.int.signed_overflow" "assume_none"; + (*set_string "sem.int.signed_overflow" "assume_none";*) warn "termination analysis implicitly activates evermultithreaded analysis and set sem.int.signed_overflow to assume_none" ) From 5ef2664537eed087c99da0b8b70b7d9667fc21f5 Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Mon, 10 Jul 2023 16:12:13 +0200 Subject: [PATCH 0294/1312] Test indentation --- .../01-simple-loop-terminating.c | 6 +- .../02-simple-loop-nonterminating.c | 6 +- .../03-nested-loop-terminating.c | 9 ++- .../04-nested-loop-nonterminating.c | 9 ++- .../75-termination/05-for-loop-terminating.c | 6 +- .../06-for-loop-nonterminating.c | 6 +- .../07-nested-for-loop-terminating.c | 9 ++- .../08-nested-for-loop-nonterminating.c | 9 ++- .../09-complex-for-loop-terminating.c | 51 +++++++++++------ .../10-complex-loop-terminating.c | 57 +++++++++++++------ .../75-termination/11-loopless-termination.c | 3 +- .../12-do-while-instant-terminating.c | 6 +- .../75-termination/13-do-while-terminating.c | 6 +- .../14-do-while-nonterminating.c | 6 +- .../15-complex-loop-combination-terminating.c | 53 +++++++++++------ ...16-nested-loop-nontrivial-nonterminating.c | 9 ++- .../75-termination/17-goto-terminating.c | 6 +- .../75-termination/18-goto-nonterminating.c | 3 +- .../75-termination/19-rand-terminating.c | 16 ++++-- .../75-termination/20-rand-nonterminating.c | 16 ++++-- .../21-no-exit-on-rand-unproofable.c | 12 ++-- .../22-exit-on-rand-unproofable.c | 6 +- .../23-exit-on-rand-terminating.c | 6 +- .../24-upjumping-goto-loopless-terminating.c | 3 +- .../25-leave-loop-goto-terminating.c | 9 ++- .../26-enter-loop-goto-terminating.c | 9 ++- .../27-upjumping-goto-nonterminating.c | 3 +- .../28-do-while-continue-terminating.c | 9 ++- .../29-do-while-continue-nonterminating.c | 9 ++- .../30-goto-out-of-inner-loop-terminating.c | 12 ++-- ...31-goto-out-of-inner-loop-nonterminating.c | 12 ++-- .../32-multithread-terminating.c | 6 +- .../33-multithread-nonterminating.c | 9 ++- .../34-nested-for-loop-nonterminating.c | 9 ++- ...out-of-inner-loop-with-print-terminating.c | 12 ++-- .../75-termination/36-recursion-terminating.c | 9 ++- .../37-recursion-nonterminating.c | 9 ++- .../38-recursion-nested-terminating.c | 15 +++-- .../39-recursion-nested-nonterminating.c | 9 ++- .../75-termination/40-complex-conditions.c | 21 ++++--- .../regression/75-termination/41-more-tests.c | 15 +++-- 41 files changed, 334 insertions(+), 162 deletions(-) diff --git a/tests/regression/75-termination/01-simple-loop-terminating.c b/tests/regression/75-termination/01-simple-loop-terminating.c index 66b6585f67..aaa2a7a895 100644 --- a/tests/regression/75-termination/01-simple-loop-terminating.c +++ b/tests/regression/75-termination/01-simple-loop-terminating.c @@ -1,10 +1,12 @@ // TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int i = 1; - while (i <= 10) { + while (i <= 10) + { printf("%d\n", i); i++; } diff --git a/tests/regression/75-termination/02-simple-loop-nonterminating.c b/tests/regression/75-termination/02-simple-loop-nonterminating.c index 6fe8816da4..51fb340f3b 100644 --- a/tests/regression/75-termination/02-simple-loop-nonterminating.c +++ b/tests/regression/75-termination/02-simple-loop-nonterminating.c @@ -1,8 +1,10 @@ // NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { - while (1) { +int main() +{ + while (1) + { continue; } diff --git a/tests/regression/75-termination/03-nested-loop-terminating.c b/tests/regression/75-termination/03-nested-loop-terminating.c index 4e3fafabcf..70327c1016 100644 --- a/tests/regression/75-termination/03-nested-loop-terminating.c +++ b/tests/regression/75-termination/03-nested-loop-terminating.c @@ -1,17 +1,20 @@ // TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int rows = 3; int columns = 4; int i = 1; // Outer while loop for rows - while (i <= rows) { + while (i <= rows) + { int j = 1; // Inner while loop for columns - while (j <= columns) { + while (j <= columns) + { printf("(%d, %d) ", i, j); j++; } diff --git a/tests/regression/75-termination/04-nested-loop-nonterminating.c b/tests/regression/75-termination/04-nested-loop-nonterminating.c index 00c2554ed2..fffc932f36 100644 --- a/tests/regression/75-termination/04-nested-loop-nonterminating.c +++ b/tests/regression/75-termination/04-nested-loop-nonterminating.c @@ -1,13 +1,16 @@ // NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int outerCount = 1; - while (outerCount <= 3) { + while (outerCount <= 3) + { int innerCount = 1; - while (1) { + while (1) + { printf("(%d, %d) ", outerCount, innerCount); innerCount++; } diff --git a/tests/regression/75-termination/05-for-loop-terminating.c b/tests/regression/75-termination/05-for-loop-terminating.c index fe07200e5b..bf58408487 100644 --- a/tests/regression/75-termination/05-for-loop-terminating.c +++ b/tests/regression/75-termination/05-for-loop-terminating.c @@ -1,10 +1,12 @@ // TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int i; - for (i = 1; i <= 10; i++) { + for (i = 1; i <= 10; i++) + { printf("%d\n", i); } diff --git a/tests/regression/75-termination/06-for-loop-nonterminating.c b/tests/regression/75-termination/06-for-loop-nonterminating.c index 374cd3e59f..be876c9741 100644 --- a/tests/regression/75-termination/06-for-loop-nonterminating.c +++ b/tests/regression/75-termination/06-for-loop-nonterminating.c @@ -1,8 +1,10 @@ // NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { - for (;;) { +int main() +{ + for (;;) + { printf("This loop does not terminate.\n"); } diff --git a/tests/regression/75-termination/07-nested-for-loop-terminating.c b/tests/regression/75-termination/07-nested-for-loop-terminating.c index a94f3f360c..1c43eeaada 100644 --- a/tests/regression/75-termination/07-nested-for-loop-terminating.c +++ b/tests/regression/75-termination/07-nested-for-loop-terminating.c @@ -1,13 +1,16 @@ // TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int rows = 3; int columns = 4; // Nested loop to iterate over rows and columns - for (int i = 1; i <= rows; i++) { - for (int j = 1; j <= columns; j++) { + for (int i = 1; i <= rows; i++) + { + for (int j = 1; j <= columns; j++) + { printf("(%d, %d) ", i, j); } printf("\n"); diff --git a/tests/regression/75-termination/08-nested-for-loop-nonterminating.c b/tests/regression/75-termination/08-nested-for-loop-nonterminating.c index e78e819cc0..e360d45d0a 100644 --- a/tests/regression/75-termination/08-nested-for-loop-nonterminating.c +++ b/tests/regression/75-termination/08-nested-for-loop-nonterminating.c @@ -1,11 +1,14 @@ // NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int outerCount, innerCount; - for (outerCount = 1; outerCount <= 3; outerCount++) { - for (innerCount = 1;; innerCount++) { + for (outerCount = 1; outerCount <= 3; outerCount++) + { + for (innerCount = 1;; innerCount++) + { printf("(%d, %d) ", outerCount, innerCount); } diff --git a/tests/regression/75-termination/09-complex-for-loop-terminating.c b/tests/regression/75-termination/09-complex-for-loop-terminating.c index 018fba6822..9767b4bc1c 100644 --- a/tests/regression/75-termination/09-complex-for-loop-terminating.c +++ b/tests/regression/75-termination/09-complex-for-loop-terminating.c @@ -1,57 +1,75 @@ // TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --set sem.int.signed_overflow assume_none #include -int main() { +int main() +{ int i, j, k; // Outer loop - for (i = 1; i <= 5; i++) { + for (i = 1; i <= 5; i++) + { // Inner loop 1 - for (j = 1; j <= i; j++) { + for (j = 1; j <= i; j++) + { printf("%d ", j); } printf("\n"); // Inner loop 2 - for (k = i; k >= 1; k--) { + for (k = i; k >= 1; k--) + { printf("%d ", k); } printf("\n"); } // Additional loop - for (i = 5; i >= 1; i--) { - for (j = i; j >= 1; j--) { + for (i = 5; i >= 1; i--) + { + for (j = i; j >= 1; j--) + { printf("%d ", j); } printf("\n"); } // Loop with conditions - for (i = 1; i <= 10; i++) { - if (i % 2 == 0) { + for (i = 1; i <= 10; i++) + { + if (i % 2 == 0) + { printf("%d is even\n", i); - } else { + } + else + { printf("%d is odd\n", i); } } // Loop with nested conditions - for (i = 1; i <= 10; i++) { + for (i = 1; i <= 10; i++) + { printf("Number: %d - ", i); - if (i < 5) { + if (i < 5) + { printf("Less than 5\n"); - } else if (i > 5) { + } + else if (i > 5) + { printf("Greater than 5\n"); - } else { + } + else + { printf("Equal to 5\n"); } } // Loop with a break statement - for (i = 1; i <= 10; i++) { + for (i = 1; i <= 10; i++) + { printf("%d ", i); - if (i == 5) { + if (i == 5) + { break; } } @@ -59,7 +77,8 @@ int main() { // Loop with multiple variables int a, b, c; - for (a = 1, b = 2, c = 3; a <= 10; a++, b += 2, c += 3) { + for (a = 1, b = 2, c = 3; a <= 10; a++, b += 2, c += 3) + { printf("%d %d %d\n", a, b, c); } diff --git a/tests/regression/75-termination/10-complex-loop-terminating.c b/tests/regression/75-termination/10-complex-loop-terminating.c index 88bf6a4565..19091b1033 100644 --- a/tests/regression/75-termination/10-complex-loop-terminating.c +++ b/tests/regression/75-termination/10-complex-loop-terminating.c @@ -1,15 +1,18 @@ // TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --set sem.int.signed_overflow assume_none #include -int main() { +int main() +{ int i = 1; int j = 1; int k = 5; // Outer while loop - while (i <= 5) { + while (i <= 5) + { // Inner while loop 1 - while (j <= i) { + while (j <= i) + { printf("%d ", j); j++; } @@ -17,7 +20,8 @@ int main() { j = 1; // Inner while loop 2 - while (k >= 1) { + while (k >= 1) + { printf("%d ", k); k--; } @@ -29,9 +33,11 @@ int main() { // Additional while loop i = 5; - while (i >= 1) { + while (i >= 1) + { j = i; - while (j >= 1) { + while (j >= 1) + { printf("%d ", j); j--; } @@ -41,10 +47,14 @@ int main() { // Loop with conditions i = 1; - while (i <= 10) { - if (i % 2 == 0) { + while (i <= 10) + { + if (i % 2 == 0) + { printf("%d is even\n", i); - } else { + } + else + { printf("%d is odd\n", i); } i++; @@ -52,13 +62,19 @@ int main() { // Loop with nested conditions i = 1; - while (i <= 10) { + while (i <= 10) + { printf("Number: %d - ", i); - if (i < 5) { + if (i < 5) + { printf("Less than 5\n"); - } else if (i > 5) { + } + else if (i > 5) + { printf("Greater than 5\n"); - } else { + } + else + { printf("Equal to 5\n"); } i++; @@ -66,9 +82,11 @@ int main() { // Loop with a break statement i = 1; - while (i <= 10) { + while (i <= 10) + { printf("%d ", i); - if (i == 5) { + if (i == 5) + { break; } i++; @@ -77,8 +95,10 @@ int main() { // Loop with a continue statement i = 1; - while (i <= 10) { - if (i % 2 == 0) { + while (i <= 10) + { + if (i % 2 == 0) + { i++; continue; } @@ -91,7 +111,8 @@ int main() { int a = 1; int b = 2; int c = 3; - while (a <= 10) { + while (a <= 10) + { printf("%d %d %d\n", a, b, c); a++; b += 2; diff --git a/tests/regression/75-termination/11-loopless-termination.c b/tests/regression/75-termination/11-loopless-termination.c index a1846905fc..51c0605757 100644 --- a/tests/regression/75-termination/11-loopless-termination.c +++ b/tests/regression/75-termination/11-loopless-termination.c @@ -1,7 +1,8 @@ // TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ printf("Terminating code without a loop\n"); return 0; } diff --git a/tests/regression/75-termination/12-do-while-instant-terminating.c b/tests/regression/75-termination/12-do-while-instant-terminating.c index 087b88f1f5..3767430a51 100644 --- a/tests/regression/75-termination/12-do-while-instant-terminating.c +++ b/tests/regression/75-termination/12-do-while-instant-terminating.c @@ -1,10 +1,12 @@ // TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int i = 0; - do { + do + { printf("Inside the do-while loop\n"); } while (i > 0); diff --git a/tests/regression/75-termination/13-do-while-terminating.c b/tests/regression/75-termination/13-do-while-terminating.c index 34343d6ba6..8faeec1e64 100644 --- a/tests/regression/75-termination/13-do-while-terminating.c +++ b/tests/regression/75-termination/13-do-while-terminating.c @@ -1,10 +1,12 @@ // TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int i = 1; - do { + do + { printf("Inside the do-while loop\n"); i++; } while (i <= 5); diff --git a/tests/regression/75-termination/14-do-while-nonterminating.c b/tests/regression/75-termination/14-do-while-nonterminating.c index 6473fdc20d..30c8349bb5 100644 --- a/tests/regression/75-termination/14-do-while-nonterminating.c +++ b/tests/regression/75-termination/14-do-while-nonterminating.c @@ -1,10 +1,12 @@ // NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int i = 1; - do { + do + { printf("Inside the do-while loop\n"); i++; } while (i >= 2); diff --git a/tests/regression/75-termination/15-complex-loop-combination-terminating.c b/tests/regression/75-termination/15-complex-loop-combination-terminating.c index 23282d24b1..d987397dd7 100644 --- a/tests/regression/75-termination/15-complex-loop-combination-terminating.c +++ b/tests/regression/75-termination/15-complex-loop-combination-terminating.c @@ -1,25 +1,29 @@ // TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --set sem.int.signed_overflow assume_none #include -int main() { +int main() +{ // Non-nested loops int i; // for loop - for (i = 1; i <= 10; i++) { + for (i = 1; i <= 10; i++) + { printf("For loop iteration: %d\n", i); } // while loop int j = 1; - while (j <= 10) { + while (j <= 10) + { printf("While loop iteration: %d\n", j); j++; } // do-while loop int k = 1; - do { + do + { printf("Do-While loop iteration: %d\n", k); k++; } while (k <= 10); @@ -28,9 +32,11 @@ int main() { int a, b; // Nested for and while loop - for (a = 1; a <= 5; a++) { + for (a = 1; a <= 5; a++) + { int c = 1; - while (c <= a) { + while (c <= a) + { printf("Nested For-While loop: %d\n", c); c++; } @@ -38,9 +44,11 @@ int main() { // Nested while and do-while loop int x = 1; - while (x <= 5) { + while (x <= 5) + { int y = 1; - do { + do + { printf("Nested While-Do-While loop: %d\n", y); y++; } while (y <= x); @@ -49,8 +57,10 @@ int main() { // Nested do-while and for loop int p = 1; - do { - for (int q = 1; q <= p; q++) { + do + { + for (int q = 1; q <= p; q++) + { printf("Nested Do-While-For loop: %d\n", q); } p++; @@ -61,13 +71,16 @@ int main() { // Nested while loop with a break statement int n = 1; - while (n <= 5) { + while (n <= 5) + { printf("Outer While loop iteration: %d\n", n); m = 1; - while (1) { + while (1) + { printf("Inner While loop iteration: %d\n", m); m++; - if (m == 4) { + if (m == 4) + { break; } } @@ -75,13 +88,19 @@ int main() { } // Loop with nested conditions - for (int v = 1; v <= 10; v++) { + for (int v = 1; v <= 10; v++) + { printf("Loop with Nested Conditions: %d - ", v); - if (v < 5) { + if (v < 5) + { printf("Less than 5\n"); - } else if (v > 5) { + } + else if (v > 5) + { printf("Greater than 5\n"); - } else { + } + else + { printf("Equal to 5\n"); } } diff --git a/tests/regression/75-termination/16-nested-loop-nontrivial-nonterminating.c b/tests/regression/75-termination/16-nested-loop-nontrivial-nonterminating.c index f89e28d91a..87b4b82ed9 100644 --- a/tests/regression/75-termination/16-nested-loop-nontrivial-nonterminating.c +++ b/tests/regression/75-termination/16-nested-loop-nontrivial-nonterminating.c @@ -1,13 +1,16 @@ // NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int outerCount = 1; - while (outerCount <= 3) { + while (outerCount <= 3) + { int innerCount = 1; - while (outerCount < 3 || innerCount > 0) { + while (outerCount < 3 || innerCount > 0) + { printf("(%d, %d) ", outerCount, innerCount); innerCount++; } diff --git a/tests/regression/75-termination/17-goto-terminating.c b/tests/regression/75-termination/17-goto-terminating.c index 3ad01cbd79..7624ae1ffc 100644 --- a/tests/regression/75-termination/17-goto-terminating.c +++ b/tests/regression/75-termination/17-goto-terminating.c @@ -1,14 +1,16 @@ // TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int num = 1; loop: printf("Current number: %d\n", num); num++; - if (num <= 10) { + if (num <= 10) + { goto loop; // We are not able to detect up-jumping gotos as terminating, we // just warn about them might being nonterminating. } diff --git a/tests/regression/75-termination/18-goto-nonterminating.c b/tests/regression/75-termination/18-goto-nonterminating.c index e26f02ec11..25f79e5b57 100644 --- a/tests/regression/75-termination/18-goto-nonterminating.c +++ b/tests/regression/75-termination/18-goto-nonterminating.c @@ -1,7 +1,8 @@ // NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int num = 1; loop: diff --git a/tests/regression/75-termination/19-rand-terminating.c b/tests/regression/75-termination/19-rand-terminating.c index fc5d6ee7b7..06deac6c34 100644 --- a/tests/regression/75-termination/19-rand-terminating.c +++ b/tests/regression/75-termination/19-rand-terminating.c @@ -3,19 +3,25 @@ #include #include -int main() { +int main() +{ // Seed the random number generator srand(time(NULL)); - if (rand()) { + if (rand()) + { // Loop inside the if part - for (int i = 1; i <= 5; i++) { + for (int i = 1; i <= 5; i++) + { printf("Loop inside if part: %d\n", i); } - } else { + } + else + { // Loop inside the else part int j = 1; - while (j <= 5) { + while (j <= 5) + { printf("Loop inside else part: %d\n", j); j++; } diff --git a/tests/regression/75-termination/20-rand-nonterminating.c b/tests/regression/75-termination/20-rand-nonterminating.c index e74c15c948..83630ed6c4 100644 --- a/tests/regression/75-termination/20-rand-nonterminating.c +++ b/tests/regression/75-termination/20-rand-nonterminating.c @@ -3,19 +3,25 @@ #include #include -int main() { +int main() +{ // Seed the random number generator srand(time(NULL)); - if (rand()) { + if (rand()) + { // Loop inside the if part - for (int i = 1; i >= 0; i++) { + for (int i = 1; i >= 0; i++) + { printf("Loop inside if part: %d\n", i); } - } else { + } + else + { // Loop inside the else part int j = 1; - while (j > 0) { + while (j > 0) + { printf("Loop inside else part: %d\n", j); } } diff --git a/tests/regression/75-termination/21-no-exit-on-rand-unproofable.c b/tests/regression/75-termination/21-no-exit-on-rand-unproofable.c index 10774e3420..3e7a65dfd4 100644 --- a/tests/regression/75-termination/21-no-exit-on-rand-unproofable.c +++ b/tests/regression/75-termination/21-no-exit-on-rand-unproofable.c @@ -1,14 +1,18 @@ // NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int forever, i = 0; // This loop is not provable, therefore it should throw a warning - while (i < 4 || forever == 1) { + while (i < 4 || forever == 1) + { i++; - if (i == 4) { - if (rand()) { + if (i == 4) + { + if (rand()) + { forever = 1; } } diff --git a/tests/regression/75-termination/22-exit-on-rand-unproofable.c b/tests/regression/75-termination/22-exit-on-rand-unproofable.c index 3f76f05aa9..b8d7992bd9 100644 --- a/tests/regression/75-termination/22-exit-on-rand-unproofable.c +++ b/tests/regression/75-termination/22-exit-on-rand-unproofable.c @@ -1,11 +1,13 @@ // NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int forever = 1; // This loop is not provable, therefore it should throw a warning - while (forever == 1) { + while (forever == 1) + { if (rand()) // May exit, may not { forever = 0; diff --git a/tests/regression/75-termination/23-exit-on-rand-terminating.c b/tests/regression/75-termination/23-exit-on-rand-terminating.c index 080b3c8871..24d4980406 100644 --- a/tests/regression/75-termination/23-exit-on-rand-terminating.c +++ b/tests/regression/75-termination/23-exit-on-rand-terminating.c @@ -2,14 +2,16 @@ #include #include -int main() { +int main() +{ int short_run, i = 0; while (i < 90 && short_run != 1) // Currently not able to detect this as terminating { i++; - if (rand()) { + if (rand()) + { short_run = 1; } } diff --git a/tests/regression/75-termination/24-upjumping-goto-loopless-terminating.c b/tests/regression/75-termination/24-upjumping-goto-loopless-terminating.c index 1a4ef63ff7..be5698d1d8 100644 --- a/tests/regression/75-termination/24-upjumping-goto-loopless-terminating.c +++ b/tests/regression/75-termination/24-upjumping-goto-loopless-terminating.c @@ -1,7 +1,8 @@ // TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { // Currently not able to detect up-jumping loop free gotos +int main() +{ // Currently not able to detect up-jumping loop free gotos goto mark2; mark1: diff --git a/tests/regression/75-termination/25-leave-loop-goto-terminating.c b/tests/regression/75-termination/25-leave-loop-goto-terminating.c index 35edf86938..2cda3d3a03 100644 --- a/tests/regression/75-termination/25-leave-loop-goto-terminating.c +++ b/tests/regression/75-termination/25-leave-loop-goto-terminating.c @@ -1,10 +1,12 @@ // TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int counter = 0; - while (1) { + while (1) + { counter++; // Dummy code @@ -13,7 +15,8 @@ int main() { printf("Result: %d\n", result); // Condition to terminate the loop - if (result >= 10) { // Apron is not able to detect this + if (result >= 10) + { // Apron is not able to detect this goto end; } } diff --git a/tests/regression/75-termination/26-enter-loop-goto-terminating.c b/tests/regression/75-termination/26-enter-loop-goto-terminating.c index 97b46f66ca..0de9a95d6c 100644 --- a/tests/regression/75-termination/26-enter-loop-goto-terminating.c +++ b/tests/regression/75-termination/26-enter-loop-goto-terminating.c @@ -1,12 +1,14 @@ // TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int counter = 0; goto jump_point; - while (1) { + while (1) + { counter++; // Dummy code @@ -16,7 +18,8 @@ int main() { printf("Result: %d\n", result); // Condition to terminate the loop - if (result >= 10) { // Apron is not able to detect this + if (result >= 10) + { // Apron is not able to detect this goto end; } } diff --git a/tests/regression/75-termination/27-upjumping-goto-nonterminating.c b/tests/regression/75-termination/27-upjumping-goto-nonterminating.c index a6621dd986..e27d7161d5 100644 --- a/tests/regression/75-termination/27-upjumping-goto-nonterminating.c +++ b/tests/regression/75-termination/27-upjumping-goto-nonterminating.c @@ -1,7 +1,8 @@ // NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ goto mark2; mark1: diff --git a/tests/regression/75-termination/28-do-while-continue-terminating.c b/tests/regression/75-termination/28-do-while-continue-terminating.c index b55aaf28c2..ebd0395218 100644 --- a/tests/regression/75-termination/28-do-while-continue-terminating.c +++ b/tests/regression/75-termination/28-do-while-continue-terminating.c @@ -1,13 +1,16 @@ // TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int i = 1; - do { + do + { i++; printf("Inside the do-while loop\n"); - if (i % 2 == 0) { + if (i % 2 == 0) + { printf("Skipping %i is even\n", i); continue; // This is handled as an goto to line 8 and there an up-jumping diff --git a/tests/regression/75-termination/29-do-while-continue-nonterminating.c b/tests/regression/75-termination/29-do-while-continue-nonterminating.c index be3e7e12de..41f1dbd5bc 100644 --- a/tests/regression/75-termination/29-do-while-continue-nonterminating.c +++ b/tests/regression/75-termination/29-do-while-continue-nonterminating.c @@ -1,14 +1,17 @@ // NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int i = 1; - do { + do + { printf("Inside the do-while loop\n"); i++; - if (i % 2) { + if (i % 2) + { printf("Continue as %i is odd\n", i); continue; } diff --git a/tests/regression/75-termination/30-goto-out-of-inner-loop-terminating.c b/tests/regression/75-termination/30-goto-out-of-inner-loop-terminating.c index e2eff29c8b..5cdadf4396 100644 --- a/tests/regression/75-termination/30-goto-out-of-inner-loop-terminating.c +++ b/tests/regression/75-termination/30-goto-out-of-inner-loop-terminating.c @@ -1,15 +1,19 @@ // TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int rows = 5; int columns = 5; // Outer loop for rows - for (int i = 1; i <= rows; i++) { + for (int i = 1; i <= rows; i++) + { // Inner loop for columns - for (int j = 1; j <= columns; j++) { - if (j == 3) { + for (int j = 1; j <= columns; j++) + { + if (j == 3) + { goto outer_loop; // Jump to the label "outer_loop" } printf("(%d, %d) ", i, j); diff --git a/tests/regression/75-termination/31-goto-out-of-inner-loop-nonterminating.c b/tests/regression/75-termination/31-goto-out-of-inner-loop-nonterminating.c index 756a93414b..cb54b5dd2f 100644 --- a/tests/regression/75-termination/31-goto-out-of-inner-loop-nonterminating.c +++ b/tests/regression/75-termination/31-goto-out-of-inner-loop-nonterminating.c @@ -1,15 +1,19 @@ // NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int rows = 5; int columns = 5; // Outer loop for rows - for (int i = 1; 1; i++) { + for (int i = 1; 1; i++) + { // Inner loop for columns - for (int j = 1; j <= columns; j++) { - if (j == 3) { + for (int j = 1; j <= columns; j++) + { + if (j == 3) + { printf("Goto as continue for outer loop\n"); goto outer_loop; // Jump to the label "outer_loop" } diff --git a/tests/regression/75-termination/32-multithread-terminating.c b/tests/regression/75-termination/32-multithread-terminating.c index beab8564f5..950eaaaa51 100644 --- a/tests/regression/75-termination/32-multithread-terminating.c +++ b/tests/regression/75-termination/32-multithread-terminating.c @@ -4,14 +4,16 @@ #include // Thread function -void *printPID(void *arg) { +void *printPID(void *arg) +{ pid_t pid = getpid(); pthread_t tid = pthread_self(); printf("Thread ID: %lu, Process ID: %d\n", (unsigned long)tid, pid); return NULL; } -int main() { +int main() +{ // Create three threads pthread_t thread1, thread2, thread3; pthread_create(&thread1, NULL, printPID, NULL); diff --git a/tests/regression/75-termination/33-multithread-nonterminating.c b/tests/regression/75-termination/33-multithread-nonterminating.c index 278c107821..dad62aa0f4 100644 --- a/tests/regression/75-termination/33-multithread-nonterminating.c +++ b/tests/regression/75-termination/33-multithread-nonterminating.c @@ -6,10 +6,12 @@ #include // Thread function -void *printPID(void *arg) { +void *printPID(void *arg) +{ pid_t pid = getpid(); pthread_t tid = pthread_self(); - while (1) { + while (1) + { printf("Thread ID: %lu, Process ID: %d\n", (unsigned long)tid, pid); struct timespec sleepTime; sleepTime.tv_sec = 1; // Seconds @@ -21,7 +23,8 @@ void *printPID(void *arg) { return NULL; } -int main() { +int main() +{ // Create three threads pthread_t thread1, thread2, thread3; pthread_create(&thread1, NULL, printPID, NULL); diff --git a/tests/regression/75-termination/34-nested-for-loop-nonterminating.c b/tests/regression/75-termination/34-nested-for-loop-nonterminating.c index 3384ed0f60..709960640f 100644 --- a/tests/regression/75-termination/34-nested-for-loop-nonterminating.c +++ b/tests/regression/75-termination/34-nested-for-loop-nonterminating.c @@ -1,11 +1,14 @@ // NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int outerCount, innerCount; - for (outerCount = 1; outerCount <= 3; outerCount++) { - for (innerCount = 1; innerCount > 0; innerCount++) { + for (outerCount = 1; outerCount <= 3; outerCount++) + { + for (innerCount = 1; innerCount > 0; innerCount++) + { printf("(%d, %d) ", outerCount, innerCount); } diff --git a/tests/regression/75-termination/35-goto-out-of-inner-loop-with-print-terminating.c b/tests/regression/75-termination/35-goto-out-of-inner-loop-with-print-terminating.c index 646f39111a..f564354e51 100644 --- a/tests/regression/75-termination/35-goto-out-of-inner-loop-with-print-terminating.c +++ b/tests/regression/75-termination/35-goto-out-of-inner-loop-with-print-terminating.c @@ -1,15 +1,19 @@ // TERM PARAM: --set "ana.activated[+]" termination --set "ana.activated[+]" apron --enable ana.int.interval --set ana.apron.domain polyhedra --set sem.int.signed_overflow assume_none #include -int main() { +int main() +{ int rows = 5; int columns = 5; // Outer loop for rows - for (int i = 1; i <= rows; i++) { + for (int i = 1; i <= rows; i++) + { // Inner loop for columns - for (int j = 1; j <= columns; j++) { - if (j == 3) { + for (int j = 1; j <= columns; j++) + { + if (j == 3) + { goto outer_loop; // Jump to the label "outer_loop" } printf("(%d, %d) ", i, j); diff --git a/tests/regression/75-termination/36-recursion-terminating.c b/tests/regression/75-termination/36-recursion-terminating.c index 533778332f..7336417c91 100644 --- a/tests/regression/75-termination/36-recursion-terminating.c +++ b/tests/regression/75-termination/36-recursion-terminating.c @@ -1,9 +1,11 @@ // TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -void recursiveFunction(int n) { +void recursiveFunction(int n) +{ // Base case: When n reaches 0, stop recursion - if (n == 0) { + if (n == 0) + { printf("Terminating recursion\n"); return; } @@ -14,7 +16,8 @@ void recursiveFunction(int n) { recursiveFunction(n - 1); } -int main() { +int main() +{ // Call the recursive function with an initial value recursiveFunction(5); diff --git a/tests/regression/75-termination/37-recursion-nonterminating.c b/tests/regression/75-termination/37-recursion-nonterminating.c index 089a4d3bcc..38aaf3de85 100644 --- a/tests/regression/75-termination/37-recursion-nonterminating.c +++ b/tests/regression/75-termination/37-recursion-nonterminating.c @@ -1,9 +1,11 @@ // NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --enable ana.context.widen #include -void recursiveFunction(int n) { +void recursiveFunction(int n) +{ // Base case: When n reaches 0, stop recursion - if (n == 30) { + if (n == 30) + { printf("Terminating recursion\n"); return; } @@ -14,7 +16,8 @@ void recursiveFunction(int n) { recursiveFunction(n - 1); } -int main() { +int main() +{ // Call the recursive function with an initial value recursiveFunction(5); diff --git a/tests/regression/75-termination/38-recursion-nested-terminating.c b/tests/regression/75-termination/38-recursion-nested-terminating.c index eace365a44..bef05eb1a0 100644 --- a/tests/regression/75-termination/38-recursion-nested-terminating.c +++ b/tests/regression/75-termination/38-recursion-nested-terminating.c @@ -1,8 +1,10 @@ // TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -void innerRecursiveFunction(int n) { - if (n == 0) { +void innerRecursiveFunction(int n) +{ + if (n == 0) + { printf("Terminating inner recursion\n"); return; } @@ -13,8 +15,10 @@ void innerRecursiveFunction(int n) { innerRecursiveFunction(n - 1); } -void outerRecursiveFunction(int n) { - if (n == 0) { +void outerRecursiveFunction(int n) +{ + if (n == 0) + { printf("Terminating outer recursion\n"); return; } @@ -28,7 +32,8 @@ void outerRecursiveFunction(int n) { innerRecursiveFunction(n); } -int main() { +int main() +{ // Call the outerRecursiveFunction with an initial value outerRecursiveFunction(3); diff --git a/tests/regression/75-termination/39-recursion-nested-nonterminating.c b/tests/regression/75-termination/39-recursion-nested-nonterminating.c index 8b57f83857..8311d9f573 100644 --- a/tests/regression/75-termination/39-recursion-nested-nonterminating.c +++ b/tests/regression/75-termination/39-recursion-nested-nonterminating.c @@ -1,14 +1,16 @@ // NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -void innerRecursiveFunction() { +void innerRecursiveFunction() +{ printf("Nested recursive call\n"); // Recursive call to the innerRecursiveFunction innerRecursiveFunction(); } -void outerRecursiveFunction() { +void outerRecursiveFunction() +{ printf("Outer recursive call\n"); // Recursive call to the outerRecursiveFunction @@ -18,7 +20,8 @@ void outerRecursiveFunction() { innerRecursiveFunction(); } -int main() { +int main() +{ // Call the outerRecursiveFunction outerRecursiveFunction(); diff --git a/tests/regression/75-termination/40-complex-conditions.c b/tests/regression/75-termination/40-complex-conditions.c index d5fe6b808a..a74c863fb4 100644 --- a/tests/regression/75-termination/40-complex-conditions.c +++ b/tests/regression/75-termination/40-complex-conditions.c @@ -1,12 +1,15 @@ // TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int i; // Loop with a continue statement - for (i = 1; i <= 10; i++) { - if (i % 2 == 0) { + for (i = 1; i <= 10; i++) + { + if (i % 2 == 0) + { continue; } printf("%d ", i); @@ -14,8 +17,10 @@ int main() { printf("\n"); // Loop with complex conditions - for (i = 1; i <= 10; i++) { - if (i > 5 && i % 2 == 0) { + for (i = 1; i <= 10; i++) + { + if (i > 5 && i % 2 == 0) + { printf("%d ", i); } } @@ -23,8 +28,10 @@ int main() { // Loop with complex conditions i = 1; - while (i <= 10) { - if (i > 5 && i % 2 == 0) { + while (i <= 10) + { + if (i > 5 && i % 2 == 0) + { printf("%d ", i); } i++; diff --git a/tests/regression/75-termination/41-more-tests.c b/tests/regression/75-termination/41-more-tests.c index 272be43293..1fb9f83f5d 100644 --- a/tests/regression/75-termination/41-more-tests.c +++ b/tests/regression/75-termination/41-more-tests.c @@ -1,11 +1,14 @@ // TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ // Loop with a continue statement - for (int r = 1; r <= 10; r++) { - if (r % 3 == 0) { + for (int r = 1; r <= 10; r++) + { + if (r % 3 == 0) + { continue; } printf("Loop with Continue: %d\n", r); @@ -13,14 +16,16 @@ int main() { // Loop with multiple conditions int s = 1; - while (s <= 10 && s % 2 == 0) { + while (s <= 10 && s % 2 == 0) + { printf("Loop with Multiple Conditions: %d\n", s); s++; } // Loop with multiple variables int t, u; - for (t = 1, u = 10; t <= 5 && u >= 5; t++, u--) { + for (t = 1, u = 10; t <= 5 && u >= 5; t++, u--) + { printf("Loop with Multiple Variables: %d %d\n", t, u); } } \ No newline at end of file From 71b1eb21eeaecc3bdebdd24fb11b966a5fbb84ff Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Mon, 10 Jul 2023 16:55:45 +0200 Subject: [PATCH 0295/1312] Execute termination tests in CI even if skipped. --- .github/workflows/locked.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/locked.yml b/.github/workflows/locked.yml index 751ade6880..48809d34c1 100644 --- a/.github/workflows/locked.yml +++ b/.github/workflows/locked.yml @@ -64,6 +64,9 @@ jobs: - name: Test apron regression (Mukherjee et. al SAS '17 paper') # skipped by default but CI has apron, so explicitly test group (which ignores skipping -- it's now a feature!) run: ruby scripts/update_suite.rb group apron-mukherjee -s + - name: Test apron termination regression # skipped by default but CI has apron, so explicitly test group (which ignores skipping -- it's now a feature!) + run: ruby scripts/update_suite.rb group termination -s + - name: Test regression cram run: opam exec -- dune runtest tests/regression From 472a76c77d7437e446fed001fac2184fb96bd4bb Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Mon, 10 Jul 2023 17:29:28 +0200 Subject: [PATCH 0296/1312] added warning for multithreaded case --- src/analyses/loopTermination.ml | 26 +++++++++++-------- .../28-do-while-continue-terminating.c | 8 +++--- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/analyses/loopTermination.ml b/src/analyses/loopTermination.ml index ecb48a5284..4242dd2b36 100644 --- a/src/analyses/loopTermination.ml +++ b/src/analyses/loopTermination.ml @@ -61,17 +61,6 @@ struct let startstate _ = () let exitstate = startstate - let finalize () = - if not (no_upjumping_gotos ()) then ( - List.iter - (fun x -> - let msgs = - [(Pretty.dprintf "The program might not terminate! (Upjumping Goto)\n", Some (M.Location.CilLocation x));] in - M.msg_group Warning ~category:NonTerminating "Possibly non terminating loops" msgs) - (!upjumping_gotos) - ); - () - let assign ctx (lval : lval) (rval : exp) = if !AnalysisState.postsolving then (* Detect assignment to loop counter variable *) @@ -126,6 +115,21 @@ struct G.for_all (fun _ term_info -> term_info) (ctx.global ()) && no_upjumping_gotos () && must_be_single_threaded_since_start ctx + | WarnGlobal v -> + (* warning for detected possible non-termination *) + (*upjumping gotos *) + if not (no_upjumping_gotos ()) then ( + List.iter + (fun x -> + let msgs = + [(Pretty.dprintf "The program might not terminate! (Upjumping Goto)\n", Some (M.Location.CilLocation x));] in + M.msg_group Warning ~category:NonTerminating "Possibly non terminating loops" msgs) + (!upjumping_gotos) + ); + (* multithreaded *) + if not (must_be_single_threaded_since_start ctx) then ( + M.warn ~category:NonTerminating "The program might not terminate! (Multithreaded)\n" + ); | _ -> Queries.Result.top q end diff --git a/tests/regression/75-termination/28-do-while-continue-terminating.c b/tests/regression/75-termination/28-do-while-continue-terminating.c index b55aaf28c2..495c6f30b1 100644 --- a/tests/regression/75-termination/28-do-while-continue-terminating.c +++ b/tests/regression/75-termination/28-do-while-continue-terminating.c @@ -45,8 +45,8 @@ statement. Hence, it is not analyzed for the upjumping gotos, which does not lead to the problem as with the "do while". -------------------- SHORTENED CIL output for Test 28 (DO WHILE): -------------------- int main(void) +----- SHORTENED CIL output for Test 28 (DO WHILE): ----- +int main(void) {{{{ #line 8 while (1) { @@ -71,8 +71,8 @@ lead to the problem as with the "do while". }} -------------------- SHORTENED CIL output for Test 28 (WHILE): -------------------- Test 28: replacing DO WHILE with WHILE: int main(void) +----- SHORTENED CIL output for Test 28 (WHILE): ----- +Test 28: replacing DO WHILE with WHILE: int main(void) {{{{ #line 8 while (1) { From 5873e5f8f5f2fce13db34210cece933a1570b5c1 Mon Sep 17 00:00:00 2001 From: Nathan Schmidt Date: Mon, 10 Jul 2023 18:46:39 +0200 Subject: [PATCH 0297/1312] Tackled feedback: minor improvements and logic fix for not_null --- src/analyses/base.ml | 1 + src/cdomains/arrayDomain.ml | 442 ++++++++++--------- src/cdomains/arrayDomain.mli | 9 +- src/cdomains/valueDomain.ml | 22 + tests/regression/73-strings/04-char_arrays.c | 5 +- 5 files changed, 260 insertions(+), 219 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 441444e69a..9ded583c20 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2082,6 +2082,7 @@ struct | _, Array array_s2 when s1_typ = charPtrType -> (* if s1 is string literal, str(n)cpy and str(n)cat are undefined *) if op_addr = None then + (* triggers warning, function only evaluated for side-effects *) let _ = AD.string_writing_defined s1_a in set ~ctx (Analyses.ask_of_ctx ctx) gs st s1_a s1_typ (VD.top_value (unrollType s1_typ)) else diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 2aa7c12976..35f87cee81 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -39,7 +39,7 @@ let get_domain ~varAttr ~typAttr = let can_recover_from_top x = x <> TrivialDomain -module type SMinusDomainAndRet = +module type S0 = sig include Lattice.S type idx @@ -65,7 +65,7 @@ end module type S = sig - include SMinusDomainAndRet + include S0 val domain_of_t: t -> domain val get: ?checkBounds:bool -> VDQ.t -> t -> Basetype.CilExp.t option * idx -> value @@ -73,7 +73,7 @@ end module type Str = sig - include SMinusDomainAndRet + include S0 type ret = Null | NotNull | Top @@ -90,7 +90,7 @@ end module type StrWithDomain = sig include Str - + val domain_of_t: t -> domain val get: ?checkBounds:bool -> VDQ.t -> t -> Basetype.CilExp.t option * idx -> value end @@ -106,9 +106,10 @@ end module type LatticeWithNull = sig include LatticeWithSmartOps - + val null: unit -> t val is_null: t -> bool + val is_not_null: t -> bool val is_int_ikind: t -> Cil.ikind option val zero_of_ikind: Cil.ikind -> t @@ -994,6 +995,53 @@ struct let to_yojson (x, y) = `Assoc [ (Base.name (), Base.to_yojson x); ("length", Idx.to_yojson y) ] end +module HelperFunctionsIndexMustMaySets = +struct + module MustSet = SetDomain.Reverse (SetDomain.ToppedSet (IntDomain.BigInt) (struct let topname = "All indexes" end)) + module MaySet = SetDomain.ToppedSet (IntDomain.BigInt) (struct let topname = "All indexes" end) + + let compute_set len = + List.init (Z.to_int len) (fun i -> i) + |> List.map Z.of_int + |> MustSet.of_list + + let must_nulls_remove i must_nulls_set min_size = + if MustSet.is_bot must_nulls_set then + MustSet.remove i (compute_set min_size) + else + MustSet.remove i must_nulls_set + + let must_nulls_filter cond must_nulls_set min_size = + if MustSet.is_bot must_nulls_set then + MustSet.filter cond (compute_set min_size) + else + MustSet.filter cond must_nulls_set + + let must_nulls_min_elt must_nulls_set = + if MustSet.is_bot must_nulls_set then + Z.zero + else + MustSet.min_elt must_nulls_set + + let may_nulls_remove i may_nulls_set max_size = + if MaySet.is_top may_nulls_set then + MaySet.remove i (compute_set max_size) + else + MaySet.remove i may_nulls_set + + let may_nulls_filter cond may_nulls_set max_size = + if MaySet.is_top may_nulls_set then + MaySet.filter cond (compute_set max_size) + else + MaySet.filter cond may_nulls_set + + let may_nulls_min_elt may_nulls_set = + if MaySet.is_top may_nulls_set then + Z.zero + else + MaySet.min_elt may_nulls_set +end + module NullByte (Val: LatticeWithNull) (Idx: IntDomain.Z): Str with type value = Val.t and type idx = Idx.t = struct module MustNulls = SetDomain.Reverse (SetDomain.ToppedSet (IntDomain.BigInt) (struct let topname = "All Null" end)) @@ -1001,6 +1049,8 @@ struct (* (Must Null Set, May Null Set, Array Size) *) include Lattice.Prod3 (MustNulls) (MayNulls) (Idx) + include HelperFunctionsIndexMustMaySets + let name () = "arrays containing null bytes" type idx = Idx.t type value = Val.t @@ -1013,13 +1063,18 @@ struct | None -> None let get ?(checkBounds=true) (ask: VDQ.t) (must_nulls_set, may_nulls_set, size) (e, i) = - let rec all_indexes_must_null i max = - if Z.gt i max then - true - else if MustNulls.mem i must_nulls_set then - all_indexes_must_null (Z.succ i) max + let all_indexes_must_null i max = + let rec check_all_indexes i = + if Z.gt i max then + true + else if MustNulls.mem i must_nulls_set then + check_all_indexes (Z.succ i) + else + false in + if Z.lt (Z.of_int (MustNulls.cardinal must_nulls_set)) (Z.sub max i) then + false else - false in + check_all_indexes i in let min interval = match Idx.minimal interval with | Some min_num when Z.geq min_num Z.zero -> min_num | _ -> Z.zero in (* assume worst case minimal natural number *) @@ -1028,8 +1083,6 @@ struct let max_i = idx_maximal i in let min_size = min size in - (* warn if index is (potentially) out of bounds *) - if checkBounds then (array_oob_check (module Idx) ((must_nulls_set, may_nulls_set), size) (e, i)); match max_i, idx_maximal size with (* if there is no maximum value in index interval *) | None, _ -> @@ -1061,58 +1114,6 @@ struct (* if maximum number in interval is invalid, i.e. negative, return Top of value *) | _ -> Top - (* helper functions *) - let must_nulls_remove i must_nulls_set min_size = - let rec compute_set acc i = - if Z.geq i min_size then - acc - else - compute_set (MustNulls.add i acc) (Z.succ i) in - if MustNulls.is_bot must_nulls_set then - MustNulls.remove i (compute_set (MustNulls.empty ()) Z.zero) - else - MustNulls.remove i must_nulls_set - let must_nulls_filter cond must_nulls_set min_size = - let rec compute_set acc i = - if Z.geq i min_size then - acc - else - compute_set (MustNulls.add i acc) (Z.succ i) in - if MustNulls.is_bot must_nulls_set then - MustNulls.filter cond (compute_set (MustNulls.empty ()) Z.zero) - else - MustNulls.filter cond must_nulls_set - let must_nulls_min_elt must_nulls_set = - if MustNulls.is_bot must_nulls_set then - Z.zero - else - MustNulls.min_elt must_nulls_set - let may_nulls_remove i may_nulls_set max_size = - let rec compute_set acc i = - if Z.geq i max_size then - acc - else - compute_set (MayNulls.add i acc) (Z.succ i) in - if MayNulls.is_top may_nulls_set then - MayNulls.remove i (compute_set (MayNulls.empty ()) Z.zero) - else - MayNulls.remove i may_nulls_set - let may_nulls_filter cond may_nulls_set max_size = - let rec compute_set acc i = - if Z.geq i max_size then - acc - else - compute_set (MayNulls.add i acc) (Z.succ i) in - if MayNulls.is_top may_nulls_set then - MayNulls.filter cond (compute_set (MayNulls.empty ()) Z.zero) - else - MayNulls.filter cond may_nulls_set - let may_nulls_min_elt may_nulls_set = - if MayNulls.is_top may_nulls_set then - Z.zero - else - MayNulls.min_elt may_nulls_set - let set (ask: VDQ.t) (must_nulls_set, may_nulls_set, size) (e, i) v = let rec add_indexes i max may_nulls_set = if Z.gt i max then @@ -1131,32 +1132,34 @@ struct match idx_maximal size with (* if size has no upper limit *) | None -> - (* ..., i < minimal size and value = null, add i to must_nulls_set and may_nulls_set *) - if Z.lt i min_size && Val.is_null v then - (MustNulls.add i must_nulls_set, MayNulls.add i may_nulls_set, size) - (* ..., i < minimal size and value <> null, remove i from must_nulls_set and may_nulls_set *) - else if Z.lt i min_size then + (* ... and value <> null, remove i from must_nulls_set and also from may_nulls_set if not top *) + if Val.is_not_null v && not (MayNulls.is_top may_nulls_set) then (must_nulls_remove i must_nulls_set min_size, MayNulls.remove i may_nulls_set, size) - (* ..., i >= minimal size and value = null, add i only to may_nulls_set *) + else if Val.is_not_null v then + (must_nulls_remove i must_nulls_set min_size, may_nulls_set, size) + (* ..., i < minimal size and value = null, add i to must_nulls_set and may_nulls_set *) + else if Z.lt i min_size && Val.is_null v then + (MustNulls.add i must_nulls_set, MayNulls.add i may_nulls_set, size) + (* ..., i >= minimal size and value = null, add i only to may_nulls_set *) else if Val.is_null v then (must_nulls_set, MayNulls.add i may_nulls_set, size) - (* ..., i >= minimal size and value <> null, remove i only from must_nulls_set *) + (* ... and value unknown, remove i from must_nulls_set and add it to may_nulls_set *) else - (must_nulls_remove i must_nulls_set min_size, may_nulls_set, size) + (must_nulls_remove i must_nulls_set min_size, MayNulls.add i may_nulls_set, size) | Some max_size -> - (* if i < minimal size and value = null, add i to must_nulls_set and may_nulls_set *) - if Z.lt i min_size && Val.is_null v then - (MustNulls.add i must_nulls_set, MayNulls.add i may_nulls_set, size) - (* if i < minimal size and value <> null, remove i from must_nulls_set and may_nulls_set *) - else if Z.lt i min_size then + (* if value <> null, remove i from must_nulls_set and may_nulls_set *) + if Val.is_not_null v then (must_nulls_remove i must_nulls_set min_size, may_nulls_remove i may_nulls_set max_size, size) - (* if minimal size <= i < maximal size and value = null, add i only to may_nulls_set *) + (* if i < minimal size and value = null, add i to must_nulls_set and may_nulls_set *) + else if Z.lt i min_size && Val.is_null v then + (MustNulls.add i must_nulls_set, MayNulls.add i may_nulls_set, size) + (* if minimal size <= i < maximal size and value = null, add i only to may_nulls_set *) else if Z.lt i max_size && Val.is_null v then (must_nulls_set, MayNulls.add i may_nulls_set, size) - (* if minimal size <= i < maximal size and value <> null, remove i only from must_nulls_set *) + (* if i < maximal size and value unknown, remove i from must_nulls_set and add it to may_nulls_set *) else if Z.lt i max_size then - (must_nulls_remove i must_nulls_set min_size, may_nulls_set, size) - (* if i >= maximal size, return tuple unmodified *) + (must_nulls_remove i must_nulls_set min_size, MayNulls.add i may_nulls_set, size) + (* if i >= maximal size, return tuple unmodified *) else (must_nulls_set, may_nulls_set, size) in @@ -1164,7 +1167,7 @@ struct (* if value = null, return must_nulls_set unmodified as not clear which index is set to null *) if Val.is_null v then must_nulls_set - (* if value <> null, only keep indexes must_i < minimal index and must_i > maximal index *) + (* if value <> null or unknown, only keep indexes must_i < minimal index and must_i > maximal index *) else if Z.equal min_i Z.zero && Z.geq max_i min_size then MustNulls.top () else @@ -1172,9 +1175,9 @@ struct let set_interval_may min_i max_i = (* if value <> null, return may_nulls_set unmodified as not clear which index is set to value *) - if not (Val.is_null v) then + if Val.is_not_null v then may_nulls_set - (* if value = null *) + (* if value = null or unknown *) else match idx_maximal size with (* ... and size has no upper limit, add all indexes of interval to may_nulls_set *) @@ -1193,16 +1196,27 @@ struct match max_i with (* if no maximum number in index interval *) | None -> - (* ..., value = null*) - if Val.is_null v && idx_maximal size = None then - match idx_maximal size with - (* ... and there is no maximal size, modify may_nulls_set to top *) - | None -> (must_nulls_set, MayNulls.top (), size) - (* ..., add all i from minimal index to maximal size to may_nulls_set *) - | Some max_size -> (must_nulls_set, add_indexes min_i (Z.pred max_size) may_nulls_set, size) - (* ... and value <> null, only keep indexes < minimal index in must_nulls_set *) - else - (must_nulls_filter (Z.gt min_i) must_nulls_set min_size, may_nulls_set, size) + (* ..., value = null *) + (if Val.is_null v && idx_maximal size = None then + match idx_maximal size with + (* ... and there is no maximal size, modify may_nulls_set to top *) + | None -> (must_nulls_set, MayNulls.top (), size) + (* ... and there is a maximal size, add all i from minimal index to maximal size to may_nulls_set *) + | Some max_size -> (must_nulls_set, add_indexes min_i (Z.pred max_size) may_nulls_set, size) + (* ... and value <> null, only keep indexes < minimal index in must_nulls_set *) + else if Val.is_not_null v then + (must_nulls_filter (Z.gt min_i) must_nulls_set min_size, may_nulls_set, size) + (*..., value unknown *) + else + match Idx.minimal size, idx_maximal size with + (* ... and size unknown, modify both sets to top *) + | None, None -> (MustNulls.top (), MayNulls.top (), size) + (* ... and only minimal size known, remove all indexes < minimal size from must_nulls_set and modify may_nulls_set to top *) + | Some min_size, None -> (must_nulls_filter (Z.gt min_size) must_nulls_set min_size, MayNulls.top (), size) + (* ... and only maximal size known, modify must_nulls_set to top and add all i from minimal index to maximal size to may_nulls_set *) + | None, Some max_size -> (MustNulls.top (), add_indexes min_i (Z.pred max_size) may_nulls_set, size) + (* ... and size is known, remove all indexes < minimal size from must_nulls_set and add all i from minimal index to maximal size to may_nulls_set *) + | Some min_size, Some max_size -> (must_nulls_filter (Z.gt min_size) must_nulls_set min_size, add_indexes min_i (Z.pred max_size) may_nulls_set, size)) | Some max_i when Z.geq max_i Z.zero -> if Z.equal min_i max_i then set_exact min_i @@ -1216,7 +1230,7 @@ struct | Some min_i, Some max_i -> if Z.lt min_i Z.zero && Z.lt max_i Z.zero then (M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.before_start "Tries to create an array of negative size"; - Z.zero, Some Z.zero) + Z.zero, Some Z.zero) else if Z.lt min_i Z.zero then (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.before_start "May try to create an array of negative size"; Z.zero, Some max_i) @@ -1225,26 +1239,26 @@ struct | None, Some max_i -> if Z.lt max_i Z.zero then (M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.before_start "Tries to create an array of negative size"; - Z.zero, Some Z.zero) + Z.zero, Some Z.zero) else Z.zero, Some max_i | Some min_i, None -> if Z.lt min_i Z.zero then (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.before_start "May try to create an array of negative size"; - Z.zero, None) + Z.zero, None) else min_i, None | None, None -> Z.zero, None in - match max_i, Val.is_null v, Val.is_bot v with + match max_i, Val.is_null v, Val.is_not_null v with (* if value = null, return (bot = all indexes up to minimal size - 1, top = all indexes up to maximal size - 1, size) *) | Some max_i, true, _ -> (MustNulls.bot (), MayNulls.top (), Idx.of_interval ILong (min_i, max_i)) | None, true, _ -> (MustNulls.bot (), MayNulls.top (), Idx.starting ILong min_i) - (* if value = bot, return (top = no indexes, top = all indexes up to maximal size - 1, size) *) - | Some max_i, false, true -> (MustNulls.top (), MayNulls.top (), Idx.of_interval ILong (min_i, max_i)) - | None, false, true -> (MustNulls.top (), MayNulls.top (), Idx.starting ILong min_i) (* if value <> null, return (top = no indexes, bot = no indexes, size) *) - | Some max_i, false, false -> (MustNulls.top (), MayNulls.bot (), Idx.of_interval ILong (min_i, max_i)) - | None, false, false -> (MustNulls.top (), MayNulls.bot (), Idx.starting ILong min_i) + | Some max_i, false, true -> (MustNulls.top (), MayNulls.bot (), Idx.of_interval ILong (min_i, max_i)) + | None, false, true -> (MustNulls.top (), MayNulls.bot (), Idx.starting ILong min_i) + (* if value unknown, return (top = no indexes, top = all indexes up to maximal size - 1, size) *) + | Some max_i, false, false -> (MustNulls.top (), MayNulls.top (), Idx.of_interval ILong (min_i, max_i)) + | None, false, false -> (MustNulls.top (), MayNulls.top (), Idx.starting ILong min_i) let length (_, _, size) = Some size @@ -1257,14 +1271,14 @@ struct * assume top for may_nulls_set as checking effect of f for every possible value is unfeasbile *) if Val.is_null (f (Val.null ())) then (must_nulls_set, MayNulls.top (), size) - (* else also return top for must_nulls_set *) + (* else also return top for must_nulls_set *) else (MustNulls.top (), MayNulls.top (), size) let fold_left f acc _ = f acc (Val.top ()) let content_to_top (_, _, size) = (MustNulls.top (), MayNulls.top (), size) - + let smart_join _ _ = join let smart_widen _ _ = widen let smart_leq _ _ = leq @@ -1288,17 +1302,17 @@ struct (* if must_nulls_set and min_nulls_set empty, definitely no null byte in array => warn about certain buffer overflow and return tuple unchanged *) if MustNulls.is_empty must_nulls_set && MayNulls.is_empty may_nulls_set then (M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array access past end: buffer overflow"; - (must_nulls_set, may_nulls_set, size)) - (* if only must_nulls_set empty, no certainty about array containing null byte => warn about potential buffer overflow and return tuple unchanged *) + (must_nulls_set, may_nulls_set, size)) + (* if only must_nulls_set empty, no certainty about array containing null byte => warn about potential buffer overflow and return tuple unchanged *) else if MustNulls.is_empty must_nulls_set then (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "May access array past end: potential buffer overflow"; - (must_nulls_set, may_nulls_set, size)) + (must_nulls_set, may_nulls_set, size)) else let min_must_null = must_nulls_min_elt must_nulls_set in (* if smallest index in sets coincides, only this null byte is kept in both sets *) if Z.equal min_must_null (may_nulls_min_elt may_nulls_set) then (MustNulls.singleton min_must_null, MayNulls.singleton min_must_null, Idx.of_int ILong (Z.succ min_must_null)) - (* else return empty must_nulls_set and keep every index up to smallest index of must_nulls_set included in may_nulls_set *) + (* else return empty must_nulls_set and keep every index up to smallest index of must_nulls_set included in may_nulls_set *) else match idx_maximal size with | Some max_size -> (MustNulls.empty (), may_nulls_filter (Z.geq min_must_null) may_nulls_set max_size, Idx.of_int ILong (Z.succ min_must_null)) @@ -1346,68 +1360,68 @@ struct (MustNulls.top (), MayNulls.top (), Idx.top_of ILong) else ((match Idx.minimal size, idx_maximal size with - | Some min_size, Some max_size -> - if Z.gt (Z.of_int n) max_size then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array size is smaller than n bytes; can cause a buffer overflow" - else if Z.gt (Z.of_int n) min_size then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array size might be smaller than n bytes; can cause a buffer overflow" - | Some min_size, None -> - if Z.gt (Z.of_int n) min_size then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array size might be smaller than n bytes; can cause a buffer overflow" - | None, Some max_size -> - if Z.gt (Z.of_int n) max_size then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array size is smaller than n bytes; can cause a buffer overflow" - | None, None -> ()); - - (* if definitely no null byte in array, i.e. must_nulls_set = may_nulls_set = empty set *) - if MustNulls.is_empty must_nulls_set && MayNulls.is_empty may_nulls_set then - (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end - "Resulting string might not be null-terminated because src doesn't contain a null byte"; - match idx_maximal size with - (* ... there *may* be null bytes from maximal size to n - 1 if maximal size < n (i.e. past end) *) - | Some max_size when Z.geq max_size Z.zero -> (must_nulls_set, add_indexes max_size (Z.of_int n) may_nulls_set, Idx.of_int ILong (Z.of_int n)) - | _ -> (must_nulls_set, may_nulls_set, Idx.of_int ILong (Z.of_int n))) - (* if only must_nulls_set empty, remove indexes >= n from may_nulls_set and add all indexes from minimal may null index to n - 1; - * warn as in any case, resulting array not guaranteed to contain null byte *) - else if MustNulls.is_empty must_nulls_set then - let min_may_null = may_nulls_min_elt may_nulls_set in - warn_no_null Z.zero false min_may_null; - (must_nulls_set, update_may_indexes min_may_null may_nulls_set, Idx.of_int ILong (Z.of_int n)) - else - let min_must_null = must_nulls_min_elt must_nulls_set in - let min_may_null = may_nulls_min_elt may_nulls_set in - (* warn if resulting array may not contain null byte *) - warn_no_null min_must_null true min_may_null; - (* if min_must_null = min_may_null, remove indexes >= n and add all indexes from minimal must/may null to n - 1 in the sets *) - if Z.equal min_must_null min_may_null then - (update_must_indexes min_must_null must_nulls_set, update_may_indexes min_may_null may_nulls_set, Idx.of_int ILong (Z.of_int n)) - else - (MustNulls.top (), update_may_indexes min_may_null may_nulls_set, Idx.of_int ILong (Z.of_int n))) + | Some min_size, Some max_size -> + if Z.gt (Z.of_int n) max_size then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array size is smaller than n bytes; can cause a buffer overflow" + else if Z.gt (Z.of_int n) min_size then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array size might be smaller than n bytes; can cause a buffer overflow" + | Some min_size, None -> + if Z.gt (Z.of_int n) min_size then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array size might be smaller than n bytes; can cause a buffer overflow" + | None, Some max_size -> + if Z.gt (Z.of_int n) max_size then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array size is smaller than n bytes; can cause a buffer overflow" + | None, None -> ()); + + (* if definitely no null byte in array, i.e. must_nulls_set = may_nulls_set = empty set *) + if MustNulls.is_empty must_nulls_set && MayNulls.is_empty may_nulls_set then + (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end + "Resulting string might not be null-terminated because src doesn't contain a null byte"; + match idx_maximal size with + (* ... there *may* be null bytes from maximal size to n - 1 if maximal size < n (i.e. past end) *) + | Some max_size when Z.geq max_size Z.zero -> (must_nulls_set, add_indexes max_size (Z.of_int n) may_nulls_set, Idx.of_int ILong (Z.of_int n)) + | _ -> (must_nulls_set, may_nulls_set, Idx.of_int ILong (Z.of_int n))) + (* if only must_nulls_set empty, remove indexes >= n from may_nulls_set and add all indexes from minimal may null index to n - 1; + * warn as in any case, resulting array not guaranteed to contain null byte *) + else if MustNulls.is_empty must_nulls_set then + let min_may_null = may_nulls_min_elt may_nulls_set in + warn_no_null Z.zero false min_may_null; + (must_nulls_set, update_may_indexes min_may_null may_nulls_set, Idx.of_int ILong (Z.of_int n)) + else + let min_must_null = must_nulls_min_elt must_nulls_set in + let min_may_null = may_nulls_min_elt may_nulls_set in + (* warn if resulting array may not contain null byte *) + warn_no_null min_must_null true min_may_null; + (* if min_must_null = min_may_null, remove indexes >= n and add all indexes from minimal must/may null to n - 1 in the sets *) + if Z.equal min_must_null min_may_null then + (update_must_indexes min_must_null must_nulls_set, update_may_indexes min_may_null may_nulls_set, Idx.of_int ILong (Z.of_int n)) + else + (MustNulls.top (), update_may_indexes min_may_null may_nulls_set, Idx.of_int ILong (Z.of_int n))) let to_string_length (must_nulls_set, may_nulls_set, size) = (* if must_nulls_set and min_nulls_set empty, definitely no null byte in array => return interval [size, inf) and warn *) if MustNulls.is_empty must_nulls_set && MayNulls.is_empty may_nulls_set then (M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array doesn't contain a null byte: buffer overflow"; - match Idx.minimal size with - | Some min_size -> Idx.starting !Cil.kindOfSizeOf min_size - | None -> Idx.starting !Cil.kindOfSizeOf Z.zero) - (* if only must_nulls_set empty, no guarantee that null ever encountered in array => return interval [minimal may null, inf) and *) + match Idx.minimal size with + | Some min_size -> Idx.starting !Cil.kindOfSizeOf min_size + | None -> Idx.starting !Cil.kindOfSizeOf Z.zero) + (* if only must_nulls_set empty, no guarantee that null ever encountered in array => return interval [minimal may null, inf) and *) else if MustNulls.is_empty must_nulls_set then (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array might not contain a null byte: potential buffer overflow"; Idx.starting !Cil.kindOfSizeOf (may_nulls_min_elt may_nulls_set)) - (* else return interval [minimal may null, minimal must null] *) + (* else return interval [minimal may null, minimal must null] *) else Idx.of_interval !Cil.kindOfSizeOf (may_nulls_min_elt may_nulls_set, must_nulls_min_elt must_nulls_set) - + let string_copy (must_nulls_set1, may_nulls_set1, size1) (must_nulls_set2, may_nulls_set2, size2) n = (* filter out indexes before strlen(src) from dest sets and after strlen(src) from src sets and build union, keep size of dest *) let update_sets must_nulls_set2' may_nulls_set2' size2' len2 = match Idx.minimal size1, idx_maximal size1, Idx.minimal len2, idx_maximal len2 with | Some min_size1, Some max_size1, Some min_len2, Some max_len2 -> (if Z.lt max_size1 min_len2 then - M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src is greater than the allocated size for dest" - else if Z.lt min_size1 max_len2 then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src may be greater than the allocated size for dest"); + M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src is greater than the allocated size for dest" + else if Z.lt min_size1 max_len2 then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src may be greater than the allocated size for dest"); let must_nulls_set_result = let min_size2 = match Idx.minimal size2' with | Some min_size2 -> min_size2 @@ -1427,7 +1441,7 @@ struct (must_nulls_set_result, may_nulls_set_result, size1) | Some min_size1, None, Some min_len2, Some max_len2 -> (if Z.lt min_size1 max_len2 then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src may be greater than the allocated size for dest"); + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src may be greater than the allocated size for dest"); let must_nulls_set_result = let min_size2 = match Idx.minimal size2' with | Some min_size2 -> min_size2 @@ -1441,9 +1455,9 @@ struct (must_nulls_set_result, may_nulls_set_result, size1) | Some min_size1, Some max_size1, Some min_len2, None -> (if Z.lt max_size1 min_len2 then - M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src is greater than the allocated size for dest" - else if Z.lt min_size1 min_len2 then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src may be greater than the allocated size for dest"); + M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src is greater than the allocated size for dest" + else if Z.lt min_size1 min_len2 then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src may be greater than the allocated size for dest"); (* do not keep any index of dest as no maximal strlen of src *) let must_nulls_set_result = let min_size2 = match Idx.minimal size2' with @@ -1459,7 +1473,7 @@ struct (must_nulls_set_result, may_nulls_set_result, size1) | Some min_size1, None, Some min_len2, None -> (if Z.lt min_size1 min_len2 then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src may be greater than the allocated size for dest"); + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src may be greater than the allocated size for dest"); (* do not keep any index of dest as no maximal strlen of src *) let must_nulls_set_result = let min_size2 = match Idx.minimal size2' with @@ -1516,14 +1530,14 @@ struct let update_sets min_size1 max_size1 max_size1_exists minlen1 maxlen1 maxlen1_exists minlen2 maxlen2 maxlen2_exists must_nulls_set2' may_nulls_set2' = (* track any potential buffer overflow and issue warning if needed *) (if max_size1_exists && Z.lt max_size1 (Z.add minlen1 minlen2) then - M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end - "The length of the concatenation of the strings in src and dest is greater than the allocated size for dest" - else if (maxlen1_exists && maxlen2_exists && Z.lt min_size1 (Z.add maxlen1 maxlen2)) - || (maxlen1_exists && Z.lt min_size1 (Z.add maxlen1 minlen2)) - || (maxlen2_exists && Z.lt min_size1 (Z.add minlen1 maxlen2)) - || Z.lt min_size1 (Z.add minlen1 minlen2) then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end - "The length of the conctenation of the strings in src and dest may be greater than the allocated size for dest"); + M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end + "The length of the concatenation of the strings in src and dest is greater than the allocated size for dest" + else if (maxlen1_exists && maxlen2_exists && Z.lt min_size1 (Z.add maxlen1 maxlen2)) + || (maxlen1_exists && Z.lt min_size1 (Z.add maxlen1 minlen2)) + || (maxlen2_exists && Z.lt min_size1 (Z.add minlen1 maxlen2)) + || Z.lt min_size1 (Z.add minlen1 minlen2) then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end + "The length of the conctenation of the strings in src and dest may be greater than the allocated size for dest"); (* if any must_nulls_set empty, result must_nulls_set also empty; * for all i1, i2 in may_nulls_set1, may_nulls_set2: add i1 + i2 if it is <= strlen(dest) + strlen(src) to new may_nulls_set * and keep indexes > minimal strlen(dest) + strlen(src) of may_nulls_set *) @@ -1548,7 +1562,7 @@ struct else MayNulls.top () in (MustNulls.top (), may_nulls_set_result, size1) - (* if minimal must null = minimal may null in ar1 and ar2, add them together and keep indexes > strlen(dest) + strlen(src) of ar1 *) + (* if minimal must null = minimal may null in ar1 and ar2, add them together and keep indexes > strlen(dest) + strlen(src) of ar1 *) else if Z.equal (must_nulls_min_elt must_nulls_set1) (may_nulls_min_elt may_nulls_set1) && Z.equal (must_nulls_min_elt must_nulls_set2') (may_nulls_min_elt may_nulls_set2') then let min_i1 = must_nulls_min_elt must_nulls_set1 in let min_i2 = must_nulls_min_elt must_nulls_set2' in @@ -1565,7 +1579,7 @@ struct else MayNulls.top () in (must_nulls_set_result, may_nulls_set_result, size1) - (* else only add all may nulls together <= strlen(dest) + strlen(src) *) + (* else only add all may nulls together <= strlen(dest) + strlen(src) *) else let min_i2 = must_nulls_min_elt must_nulls_set2' in let may_nulls_set2'_until_min_i2 = @@ -1659,40 +1673,40 @@ struct false, false | _ -> false, false - let string_comparison (must_nulls_set1, may_nulls_set1, size1) (must_nulls_set2, may_nulls_set2, size2) n = + let string_comparison (must_nulls_set1, may_nulls_set1, size1) (must_nulls_set2, may_nulls_set2, size2) n = let compare n n_exists = (* if s1 = s2 = empty string, i.e. certain null byte at index 0, or n = 0, return 0 *) - if (MustNulls.mem Z.zero must_nulls_set1 && (MustNulls.mem Z.zero must_nulls_set2)) - || (n_exists && Z.equal Z.zero n) then - Idx.of_int IInt Z.zero + if (MustNulls.mem Z.zero must_nulls_set1 && (MustNulls.mem Z.zero must_nulls_set2)) + || (n_exists && Z.equal Z.zero n) then + Idx.of_int IInt Z.zero (* if only s1 = empty string, return negative integer *) - else if MustNulls.mem Z.zero must_nulls_set1 && not (MayNulls.mem Z.zero may_nulls_set2) then - Idx.ending IInt Z.minus_one + else if MustNulls.mem Z.zero must_nulls_set1 && not (MayNulls.mem Z.zero may_nulls_set2) then + Idx.ending IInt Z.minus_one (* if only s2 = empty string, return positive integer *) - else if MustNulls.mem Z.zero must_nulls_set2 then - Idx.starting IInt Z.one - else - (* if first null bytes are certain, have different indexes and are before index n if n present, return integer <> 0 *) - (try if Z.equal (must_nulls_min_elt must_nulls_set1) (may_nulls_min_elt may_nulls_set1) && (not n_exists || Z.lt (must_nulls_min_elt must_nulls_set1) n) - && Z.equal (must_nulls_min_elt must_nulls_set2) (may_nulls_min_elt may_nulls_set2) && (not n_exists || Z.lt (must_nulls_min_elt must_nulls_set2) n) - && not (Z.equal (must_nulls_min_elt must_nulls_set1) (must_nulls_min_elt must_nulls_set2)) then - Idx.of_excl_list IInt [Z.zero] - else - Idx.top_of IInt - with Not_found -> Idx.top_of IInt) in + else if MustNulls.mem Z.zero must_nulls_set2 then + Idx.starting IInt Z.one + else + (* if first null bytes are certain, have different indexes and are before index n if n present, return integer <> 0 *) + (try if Z.equal (must_nulls_min_elt must_nulls_set1) (may_nulls_min_elt may_nulls_set1) && (not n_exists || Z.lt (must_nulls_min_elt must_nulls_set1) n) + && Z.equal (must_nulls_min_elt must_nulls_set2) (may_nulls_min_elt may_nulls_set2) && (not n_exists || Z.lt (must_nulls_min_elt must_nulls_set2) n) + && not (Z.equal (must_nulls_min_elt must_nulls_set1) (must_nulls_min_elt must_nulls_set2)) then + Idx.of_excl_list IInt [Z.zero] + else + Idx.top_of IInt + with Not_found -> Idx.top_of IInt) in match n with (* strcmp *) | None -> (* track any potential buffer overflow and issue warning if needed *) (if MustNulls.is_empty must_nulls_set1 && MayNulls.is_empty may_nulls_set1 then - M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array of string 1 doesn't contain a null byte: buffer overflow" - else if MustNulls.is_empty must_nulls_set1 then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array of string 1 might not contain a null byte: potential buffer overflow"); + M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array of string 1 doesn't contain a null byte: buffer overflow" + else if MustNulls.is_empty must_nulls_set1 then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array of string 1 might not contain a null byte: potential buffer overflow"); (if MustNulls.is_empty must_nulls_set2 && MayNulls.is_empty may_nulls_set2 then - M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array of string 2 doesn't contain a null byte: buffer overflow" - else if MustNulls.is_empty must_nulls_set2 then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array of string 2 might not contain a null byte: potential buffer overflow"); + M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array of string 2 doesn't contain a null byte: buffer overflow" + else if MustNulls.is_empty must_nulls_set2 then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array of string 2 might not contain a null byte: potential buffer overflow"); (* compute abstract value for result of strcmp *) compare Z.zero false (* strncmp *) @@ -1703,27 +1717,27 @@ struct let min_size2 = match Idx.minimal size2 with | Some min_size2 -> min_size2 | None -> Z.zero in - (* issue a warning if n is (potentially) smaller than array sizes *) + (* issue a warning if n is (potentially) smaller than array sizes *) (match idx_maximal size1 with - | Some max_size1 -> - if Z.gt (Z.of_int n) max_size1 then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The size of the array of string 1 is smaller than n bytes" - else if Z.gt (Z.of_int n) min_size1 then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The size of the array of string 1 might be smaller than n bytes" - | None -> - if Z.gt (Z.of_int n) min_size1 then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The size of the array of string 1 might be smaller than n bytes"); + | Some max_size1 -> + if Z.gt (Z.of_int n) max_size1 then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The size of the array of string 1 is smaller than n bytes" + else if Z.gt (Z.of_int n) min_size1 then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The size of the array of string 1 might be smaller than n bytes" + | None -> + if Z.gt (Z.of_int n) min_size1 then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The size of the array of string 1 might be smaller than n bytes"); (match idx_maximal size2 with - | Some max_size2 -> - if Z.gt (Z.of_int n) max_size2 then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The size of the array of string 2 is smaller than n bytes" - else if Z.gt (Z.of_int n) min_size2 then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The size of the array of string 2 might be smaller than n bytes" - | None -> - if Z.gt (Z.of_int n) min_size2 then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The size of the array of string 2 might be smaller than n bytes"); - (* compute abstract value for result of strncmp *) - compare (Z.of_int n) true + | Some max_size2 -> + if Z.gt (Z.of_int n) max_size2 then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The size of the array of string 2 is smaller than n bytes" + else if Z.gt (Z.of_int n) min_size2 then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The size of the array of string 2 might be smaller than n bytes" + | None -> + if Z.gt (Z.of_int n) min_size2 then + M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The size of the array of string 2 might be smaller than n bytes"); + (* compute abstract value for result of strncmp *) + compare (Z.of_int n) true | _ -> Idx.top_of IInt let update_length new_size (must_nulls_set, may_nulls_set, size) = (must_nulls_set, may_nulls_set, new_size) @@ -1863,7 +1877,7 @@ struct module N = NullByte (Val) (Idx) include Lattice.Prod (F) (N) - + let name () = "AttributeConfiguredArrayDomain" type idx = Idx.t type value = Val.t diff --git a/src/cdomains/arrayDomain.mli b/src/cdomains/arrayDomain.mli index 894fa9192e..e8deae06e0 100644 --- a/src/cdomains/arrayDomain.mli +++ b/src/cdomains/arrayDomain.mli @@ -12,7 +12,7 @@ val get_domain: varAttr:Cil.attributes -> typAttr:Cil.attributes -> domain val can_recover_from_top: domain -> bool (** Some domains such as Trivial cannot recover from their value ever being top. {!ValueDomain} handles intialization differently for these *) -module type SMinusDomainAndRet = +module type S0 = sig include Lattice.S type idx @@ -60,7 +60,7 @@ end (** Abstract domains representing arrays. *) module type S = sig - include SMinusDomainAndRet + include S0 val domain_of_t: t -> domain (* Returns the domain used for the array*) @@ -72,7 +72,7 @@ end (** Abstract domains representing strings a.k.a. null-terminated char arrays. *) module type Str = sig - include SMinusDomainAndRet + include S0 type ret = Null | NotNull | Top @@ -126,9 +126,10 @@ end module type LatticeWithNull = sig include LatticeWithSmartOps - + val null: unit -> t val is_null: t -> bool + val is_not_null: t -> bool val is_int_ikind: t -> Cil.ikind option val zero_of_ikind: Cil.ikind -> t diff --git a/src/cdomains/valueDomain.ml b/src/cdomains/valueDomain.ml index 6fa3b21731..76f304c37e 100644 --- a/src/cdomains/valueDomain.ml +++ b/src/cdomains/valueDomain.ml @@ -40,6 +40,7 @@ sig val null: unit -> t val is_null: t -> bool + val is_not_null: t -> bool val is_int_ikind: t -> Cil.ikind option val zero_of_ikind: Cil.ikind -> t @@ -278,6 +279,27 @@ struct | None -> false end | _ -> false + let is_not_null = function + | Int n -> + begin match ID.minimal n, ID.maximal n with + | Some min, Some max -> + if Z.gt min Z.zero || Z.lt max Z.zero then + true + else + false + | Some min, None -> + if Z.gt min Z.zero then + true + else + false + | None, Some max -> + if Z.lt max Z.zero then + true + else + false + | _ -> false + end + | _ -> true let is_int_ikind = function | Int n -> Some (ID.ikind n) diff --git a/tests/regression/73-strings/04-char_arrays.c b/tests/regression/73-strings/04-char_arrays.c index 940960569f..72d5a4637e 100644 --- a/tests/regression/73-strings/04-char_arrays.c +++ b/tests/regression/73-strings/04-char_arrays.c @@ -150,7 +150,10 @@ void example7() { len = strlen(s2); // WARN __goblint_check(len >= 12); // UNKNOWN: loop transformed to interval - s2[4] = s2[5] = s2[6] = s2[7] = 'a'; + s2[4] = 'a'; + s2[5] = 'a'; + s2[6] = 'a'; + s2[7] = 'a'; len = strlen(s2); // WARN: no must nulls and may nulls __goblint_check(len >= 12); } From 40f0de701493334204e8a3619a3e0d9b6262cb6c Mon Sep 17 00:00:00 2001 From: Nathan Schmidt Date: Mon, 10 Jul 2023 19:14:45 +0200 Subject: [PATCH 0298/1312] Fix macOS tests --- src/cdomains/arrayDomain.ml | 2 +- tests/regression/73-strings/01-string_literals.c | 10 +++++----- tests/regression/73-strings/04-char_arrays.c | 12 ++++++++---- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 35f87cee81..f1bab39208 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1001,7 +1001,7 @@ struct module MaySet = SetDomain.ToppedSet (IntDomain.BigInt) (struct let topname = "All indexes" end) let compute_set len = - List.init (Z.to_int len) (fun i -> i) + List.init (Z.to_int len) (Fun.id) |> List.map Z.of_int |> MustSet.of_list diff --git a/tests/regression/73-strings/01-string_literals.c b/tests/regression/73-strings/01-string_literals.c index bc27c917be..159ca57f1c 100644 --- a/tests/regression/73-strings/01-string_literals.c +++ b/tests/regression/73-strings/01-string_literals.c @@ -11,7 +11,7 @@ char* hello_world() { void id(char* s) { char* ptr = NULL; // future usage of cmp should warn: workaround for macOS test #ifdef __APPLE__ - #define ID int i = strcmp(ptr, "trigger warning") + #define ID int i = *ptr #else #define ID strcpy(s, s) #endif @@ -71,28 +71,28 @@ int main() { cmp = NULL; // future usage of cmp should warn: workaround for macOS test #ifdef __APPLE__ - #define STRCPY i = strcmp(cmp, "trigger warning") + #define STRCPY i = *cmp #else #define STRCPY strcpy(s1, "hi"); #endif STRCPY; // WARN #ifdef __APPLE__ - #define STRNCPY i = strcmp(cmp, "trigger warning") + #define STRNCPY i = *cmp #else # define STRNCPY strncpy(s1, "hi", 1) #endif STRNCPY; // WARN #ifdef __APPLE__ - #define STRCAT i = strcmp(cmp, "trigger warning") + #define STRCAT i = *cmp #else #define STRCAT strcat(s1, "hi") #endif STRCAT; // WARN #ifdef __APPLE__ - #define STRNCAT i = strcmp(cmp, "trigger warning") + #define STRNCAT i = *cmp #else #define STRNCAT strncat(s1, "hi", 1) #endif diff --git a/tests/regression/73-strings/04-char_arrays.c b/tests/regression/73-strings/04-char_arrays.c index 72d5a4637e..076169cf05 100644 --- a/tests/regression/73-strings/04-char_arrays.c +++ b/tests/regression/73-strings/04-char_arrays.c @@ -164,10 +164,14 @@ void example8() { char s2[] = "test"; // must and may null at 4 char cmp[50]; - strcpy(cmp, strstr(s1, empty)); // NOWARN: strstr(s1, empty) != NULL - size_t len = strlen(cmp); - __goblint_check(len == 11); - + #ifdef __APPLE__ + // do nothing => no warning + #else + strcpy(cmp, strstr(s1, empty)); // NOWARN: strstr(s1, empty) != NULL + size_t len = strlen(cmp); + __goblint_check(len == 11); + #endif + char* cmp_ptr = strstr(s2, s1); __goblint_check(cmp_ptr == NULL); } From 4af911a0c99939ea90dcdc357b0cffb61e2a1444 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Mon, 10 Jul 2023 19:55:19 +0200 Subject: [PATCH 0299/1312] Revert "added warning for multithreaded case" This reverts commit 472a76c77d7437e446fed001fac2184fb96bd4bb. --- src/analyses/loopTermination.ml | 26 ++++++++----------- .../28-do-while-continue-terminating.c | 8 +++--- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/analyses/loopTermination.ml b/src/analyses/loopTermination.ml index 4242dd2b36..ecb48a5284 100644 --- a/src/analyses/loopTermination.ml +++ b/src/analyses/loopTermination.ml @@ -61,6 +61,17 @@ struct let startstate _ = () let exitstate = startstate + let finalize () = + if not (no_upjumping_gotos ()) then ( + List.iter + (fun x -> + let msgs = + [(Pretty.dprintf "The program might not terminate! (Upjumping Goto)\n", Some (M.Location.CilLocation x));] in + M.msg_group Warning ~category:NonTerminating "Possibly non terminating loops" msgs) + (!upjumping_gotos) + ); + () + let assign ctx (lval : lval) (rval : exp) = if !AnalysisState.postsolving then (* Detect assignment to loop counter variable *) @@ -115,21 +126,6 @@ struct G.for_all (fun _ term_info -> term_info) (ctx.global ()) && no_upjumping_gotos () && must_be_single_threaded_since_start ctx - | WarnGlobal v -> - (* warning for detected possible non-termination *) - (*upjumping gotos *) - if not (no_upjumping_gotos ()) then ( - List.iter - (fun x -> - let msgs = - [(Pretty.dprintf "The program might not terminate! (Upjumping Goto)\n", Some (M.Location.CilLocation x));] in - M.msg_group Warning ~category:NonTerminating "Possibly non terminating loops" msgs) - (!upjumping_gotos) - ); - (* multithreaded *) - if not (must_be_single_threaded_since_start ctx) then ( - M.warn ~category:NonTerminating "The program might not terminate! (Multithreaded)\n" - ); | _ -> Queries.Result.top q end diff --git a/tests/regression/75-termination/28-do-while-continue-terminating.c b/tests/regression/75-termination/28-do-while-continue-terminating.c index 495c6f30b1..b55aaf28c2 100644 --- a/tests/regression/75-termination/28-do-while-continue-terminating.c +++ b/tests/regression/75-termination/28-do-while-continue-terminating.c @@ -45,8 +45,8 @@ statement. Hence, it is not analyzed for the upjumping gotos, which does not lead to the problem as with the "do while". ------ SHORTENED CIL output for Test 28 (DO WHILE): ----- -int main(void) +------------------- SHORTENED CIL output for Test 28 (DO WHILE): +------------------- int main(void) {{{{ #line 8 while (1) { @@ -71,8 +71,8 @@ int main(void) }} ------ SHORTENED CIL output for Test 28 (WHILE): ----- -Test 28: replacing DO WHILE with WHILE: int main(void) +------------------- SHORTENED CIL output for Test 28 (WHILE): +------------------- Test 28: replacing DO WHILE with WHILE: int main(void) {{{{ #line 8 while (1) { From 20722581892d8de17684f0d34c94fc2665038639 Mon Sep 17 00:00:00 2001 From: Nathan Schmidt <73504207+nathanschmidt@users.noreply.github.com> Date: Mon, 10 Jul 2023 20:27:10 +0200 Subject: [PATCH 0300/1312] Fix test 04-char_arrays.c for macOS --- tests/regression/73-strings/04-char_arrays.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/regression/73-strings/04-char_arrays.c b/tests/regression/73-strings/04-char_arrays.c index 076169cf05..0af19ba968 100644 --- a/tests/regression/73-strings/04-char_arrays.c +++ b/tests/regression/73-strings/04-char_arrays.c @@ -165,12 +165,12 @@ void example8() { char cmp[50]; #ifdef __APPLE__ - // do nothing => no warning + size_t len = 11; #else strcpy(cmp, strstr(s1, empty)); // NOWARN: strstr(s1, empty) != NULL size_t len = strlen(cmp); - __goblint_check(len == 11); #endif + __goblint_check(len == 11); char* cmp_ptr = strstr(s2, s1); __goblint_check(cmp_ptr == NULL); From 32a654bbdbe411b91360149541d3c3532d42ecde Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Mon, 10 Jul 2023 21:04:50 +0200 Subject: [PATCH 0301/1312] addedd the possibly non-terminating warning for multi threaded programs --- src/analyses/loopTermination.ml | 32 +++++++++++++------ .../28-do-while-continue-terminating.c | 8 ++--- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/analyses/loopTermination.ml b/src/analyses/loopTermination.ml index ecb48a5284..bfcb364e87 100644 --- a/src/analyses/loopTermination.ml +++ b/src/analyses/loopTermination.ml @@ -6,6 +6,10 @@ open TerminationPreprocessing exception PreProcessing of string +(** Stores the result of the query if the program is single threaded or not + since finalize does not has ctx as an argument*) +let single_thread : bool ref = ref false + (** Contains all loop counter variables (varinfo) and maps them to their corresponding loop statement. *) let loop_counters : stmt VarToStmt.t ref = ref VarToStmt.empty @@ -62,15 +66,21 @@ struct let exitstate = startstate let finalize () = + (* warning for detected possible non-termination *) + (*upjumping gotos *) if not (no_upjumping_gotos ()) then ( List.iter (fun x -> let msgs = - [(Pretty.dprintf "The program might not terminate! (Upjumping Goto)\n", Some (M.Location.CilLocation x));] in - M.msg_group Warning ~category:NonTerminating "Possibly non terminating loops" msgs) + [(Pretty.dprintf "The program might not terminate! (Upjumping Goto)\n", Some (M.Location.CilLocation x));] in + M.msg_group Warning ~category:NonTerminating "Possibly non terminating loops" msgs) (!upjumping_gotos) - ); - () + ); + (* multithreaded *) + if not (!single_thread) then ( + M.warn ~category:NonTerminating "The program might not terminate! (Multithreaded)\n" + ) + let assign ctx (lval : lval) (rval : exp) = if !AnalysisState.postsolving then @@ -112,20 +122,24 @@ struct (* not (ctx.ask Queries.IsEverMultiThreaded) *) - ctx.ask (Queries.MustBeSingleThreaded {since_start = true}) + let single_threaded = ctx.ask (Queries.MustBeSingleThreaded {since_start = true}) in + single_thread := single_threaded; + single_threaded (** Provides information to Goblint *) let query ctx (type a) (q: a Queries.t): a Queries.result = match q with | Queries.MustTermLoop loop_statement -> - (match G.find_opt (`Lifted loop_statement) (ctx.global ()) with + must_be_single_threaded_since_start ctx + && (match G.find_opt (`Lifted loop_statement) (ctx.global ()) with Some b -> b | None -> false) - && must_be_single_threaded_since_start ctx | Queries.MustTermAllLoops -> - G.for_all (fun _ term_info -> term_info) (ctx.global ()) + must_be_single_threaded_since_start ctx (* must be the first to be evaluated! + This has the side effect that the single_Thread variable is set + In case of another order and due to lazy evaluation the correct value of single_Thread can otherwise not be guaranteed! *) && no_upjumping_gotos () - && must_be_single_threaded_since_start ctx + && G.for_all (fun _ term_info -> term_info) (ctx.global ()) | _ -> Queries.Result.top q end diff --git a/tests/regression/75-termination/28-do-while-continue-terminating.c b/tests/regression/75-termination/28-do-while-continue-terminating.c index b55aaf28c2..9e8cb7496b 100644 --- a/tests/regression/75-termination/28-do-while-continue-terminating.c +++ b/tests/regression/75-termination/28-do-while-continue-terminating.c @@ -45,8 +45,8 @@ statement. Hence, it is not analyzed for the upjumping gotos, which does not lead to the problem as with the "do while". -------------------- SHORTENED CIL output for Test 28 (DO WHILE): -------------------- int main(void) +------- SHORTENED CIL output for Test 28 (DO WHILE): ------- +int main(void) {{{{ #line 8 while (1) { @@ -71,8 +71,8 @@ lead to the problem as with the "do while". }} -------------------- SHORTENED CIL output for Test 28 (WHILE): -------------------- Test 28: replacing DO WHILE with WHILE: int main(void) +------- SHORTENED CIL output for Test 28 (WHILE): ------- +Test 28: replacing DO WHILE with WHILE: int main(void) {{{{ #line 8 while (1) { From 9d21da49f6c477b13fae050a6f5913fffd1a8a2f Mon Sep 17 00:00:00 2001 From: Nathan Schmidt Date: Tue, 11 Jul 2023 13:58:02 +0200 Subject: [PATCH 0302/1312] Updated is_not_null with case for potential null_ptr --- src/cdomains/valueDomain.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cdomains/valueDomain.ml b/src/cdomains/valueDomain.ml index 76f304c37e..7480ca12a6 100644 --- a/src/cdomains/valueDomain.ml +++ b/src/cdomains/valueDomain.ml @@ -299,6 +299,7 @@ struct false | _ -> false end + | Address a when AD.may_be_null a -> false | _ -> true let is_int_ikind = function From 53c3c1ce32c78f9844d6d36b5ec73b1954bbf108 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Wed, 12 Jul 2023 16:01:32 +0200 Subject: [PATCH 0303/1312] Enable use of everMultiThreaded analysis --- src/analyses/everMultiThreaded.ml | 2 +- src/analyses/loopTermination.ml | 40 +++++++++++++++---------------- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/src/analyses/everMultiThreaded.ml b/src/analyses/everMultiThreaded.ml index 5567ef1223..f44aaffc93 100644 --- a/src/analyses/everMultiThreaded.ml +++ b/src/analyses/everMultiThreaded.ml @@ -1,4 +1,4 @@ -(** Work in progress *) +(** Analysis to register whether any additional thread has ever been spawned ([evermultithreaded]). *) open Analyses diff --git a/src/analyses/loopTermination.ml b/src/analyses/loopTermination.ml index bfcb364e87..f62eedcce4 100644 --- a/src/analyses/loopTermination.ml +++ b/src/analyses/loopTermination.ml @@ -6,7 +6,7 @@ open TerminationPreprocessing exception PreProcessing of string -(** Stores the result of the query if the program is single threaded or not +(** Stores the result of the query if the program is single threaded or not since finalize does not has ctx as an argument*) let single_thread : bool ref = ref false @@ -65,21 +65,21 @@ struct let startstate _ = () let exitstate = startstate - let finalize () = + let finalize () = (* warning for detected possible non-termination *) (*upjumping gotos *) if not (no_upjumping_gotos ()) then ( - List.iter - (fun x -> - let msgs = - [(Pretty.dprintf "The program might not terminate! (Upjumping Goto)\n", Some (M.Location.CilLocation x));] in - M.msg_group Warning ~category:NonTerminating "Possibly non terminating loops" msgs) - (!upjumping_gotos) - ); + List.iter + (fun x -> + let msgs = + [(Pretty.dprintf "The program might not terminate! (Upjumping Goto)\n", Some (M.Location.CilLocation x));] in + M.msg_group Warning ~category:NonTerminating "Possibly non terminating loops" msgs) + (!upjumping_gotos) + ); (* multithreaded *) if not (!single_thread) then ( - M.warn ~category:NonTerminating "The program might not terminate! (Multithreaded)\n" - ) + M.warn ~category:NonTerminating "The program might not terminate! (Multithreaded)\n" + ) let assign ctx (lval : lval) (rval : exp) = @@ -119,10 +119,7 @@ struct (** Checks whether a new thread was spawned some time. We want to discard * any knowledge about termination then (see query function) *) let must_be_single_threaded_since_start ctx = - (* - not (ctx.ask Queries.IsEverMultiThreaded) - *) - let single_threaded = ctx.ask (Queries.MustBeSingleThreaded {since_start = true}) in + let single_threaded = not (ctx.ask Queries.IsEverMultiThreaded) in single_thread := single_threaded; single_threaded @@ -130,14 +127,15 @@ struct let query ctx (type a) (q: a Queries.t): a Queries.result = match q with | Queries.MustTermLoop loop_statement -> - must_be_single_threaded_since_start ctx + must_be_single_threaded_since_start ctx && (match G.find_opt (`Lifted loop_statement) (ctx.global ()) with - Some b -> b - | None -> false) + Some b -> b + | None -> false) | Queries.MustTermAllLoops -> - must_be_single_threaded_since_start ctx (* must be the first to be evaluated! - This has the side effect that the single_Thread variable is set - In case of another order and due to lazy evaluation the correct value of single_Thread can otherwise not be guaranteed! *) + (* Must be the first to be evaluated! This has the side effect that + * single_thread is set. In case of another order and due to lazy + * evaluation the correct value of single_thread can not be guaranteed! *) + must_be_single_threaded_since_start ctx && no_upjumping_gotos () && G.for_all (fun _ term_info -> term_info) (ctx.global ()) | _ -> Queries.Result.top q From 76486c9a9c7bc08fd213cda8beb737c5861bfa8e Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Wed, 12 Jul 2023 16:04:20 +0200 Subject: [PATCH 0304/1312] Make struct for V anonymous --- src/analyses/loopTermination.ml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/analyses/loopTermination.ml b/src/analyses/loopTermination.ml index f62eedcce4..367c3a328c 100644 --- a/src/analyses/loopTermination.ml +++ b/src/analyses/loopTermination.ml @@ -37,12 +37,6 @@ let check_bounded ctx varinfo = | `Lifted v -> not (is_top_of (ikind v) v) | `Bot -> raise (PreProcessing "Loop variable is Bot") -module UnitV = -struct - include Printable.Unit - let is_write_only _ = true -end - (** We want to record termination information of loops and use the loop * statements for that. We use this lifting because we need to have a * lattice. *) @@ -59,7 +53,11 @@ struct module D = Lattice.Unit module C = D - module V = UnitV + module V = + struct + include Printable.Unit + let is_write_only _ = true + end module G = MapDomain.MapBot (Statements) (BoolDomain.MustBool) let startstate _ = () From ba9d35a9973a38f748bbbcba3cabd4284c6ac57f Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Thu, 13 Jul 2023 12:19:12 +0200 Subject: [PATCH 0305/1312] indentation --- src/analyses/loopTermination.ml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/analyses/loopTermination.ml b/src/analyses/loopTermination.ml index bfcb364e87..1dece04bbc 100644 --- a/src/analyses/loopTermination.ml +++ b/src/analyses/loopTermination.ml @@ -75,11 +75,11 @@ struct [(Pretty.dprintf "The program might not terminate! (Upjumping Goto)\n", Some (M.Location.CilLocation x));] in M.msg_group Warning ~category:NonTerminating "Possibly non terminating loops" msgs) (!upjumping_gotos) - ); + ); (* multithreaded *) if not (!single_thread) then ( - M.warn ~category:NonTerminating "The program might not terminate! (Multithreaded)\n" - ) + M.warn ~category:NonTerminating "The program might not terminate! (Multithreaded)\n" + ) let assign ctx (lval : lval) (rval : exp) = @@ -135,9 +135,9 @@ struct Some b -> b | None -> false) | Queries.MustTermAllLoops -> - must_be_single_threaded_since_start ctx (* must be the first to be evaluated! - This has the side effect that the single_Thread variable is set - In case of another order and due to lazy evaluation the correct value of single_Thread can otherwise not be guaranteed! *) + must_be_single_threaded_since_start ctx (* must be the first to be evaluated! *) + (*Reason: must_be_single_threaded_since_start has the side effect that the single_Thread variable is set + In case of another order and due to lazy evaluation the correct value of single_Thread can otherwise not be guaranteed! *) && no_upjumping_gotos () && G.for_all (fun _ term_info -> term_info) (ctx.global ()) | _ -> Queries.Result.top q From a98f503ed7c47e2d208b7031268e314f08680ade Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Thu, 13 Jul 2023 12:27:02 +0200 Subject: [PATCH 0306/1312] Test goal and comments changed --- .../75-termination/17-goto-terminating.c | 3 ++- .../24-upjumping-goto-loopless-terminating.c | 3 ++- .../28-do-while-continue-terminating.c | 3 +-- .../32-multithread-terminating.c | 3 ++- ...42-downjumping-goto-loopless-terminating.c | 19 +++++++++++++++++++ 5 files changed, 26 insertions(+), 5 deletions(-) create mode 100644 tests/regression/75-termination/42-downjumping-goto-loopless-terminating.c diff --git a/tests/regression/75-termination/17-goto-terminating.c b/tests/regression/75-termination/17-goto-terminating.c index 3ad01cbd79..8270f8c960 100644 --- a/tests/regression/75-termination/17-goto-terminating.c +++ b/tests/regression/75-termination/17-goto-terminating.c @@ -1,4 +1,5 @@ -// TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// The program terminates but the analysis is currently only meant to detect up-jumping gotos as potentially NonTerminating, therefore we expect an NonTerm #include int main() { diff --git a/tests/regression/75-termination/24-upjumping-goto-loopless-terminating.c b/tests/regression/75-termination/24-upjumping-goto-loopless-terminating.c index 1a4ef63ff7..88d8c8f418 100644 --- a/tests/regression/75-termination/24-upjumping-goto-loopless-terminating.c +++ b/tests/regression/75-termination/24-upjumping-goto-loopless-terminating.c @@ -1,4 +1,5 @@ -// TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// The program terminates but the analysis is currently only meant to detect up-jumping gotos as potentially NonTerminating, therefore we expect an NonTerm #include int main() { // Currently not able to detect up-jumping loop free gotos diff --git a/tests/regression/75-termination/28-do-while-continue-terminating.c b/tests/regression/75-termination/28-do-while-continue-terminating.c index 9e8cb7496b..402e744e2f 100644 --- a/tests/regression/75-termination/28-do-while-continue-terminating.c +++ b/tests/regression/75-termination/28-do-while-continue-terminating.c @@ -10,8 +10,7 @@ int main() { if (i % 2 == 0) { printf("Skipping %i is even\n", i); - continue; // This is handled as an goto to line 8 and there an up-jumping - // goto + continue; // This is handled as an goto to line 8 and therefore an up-jumping goto } } while (i <= 5); diff --git a/tests/regression/75-termination/32-multithread-terminating.c b/tests/regression/75-termination/32-multithread-terminating.c index beab8564f5..737b867cde 100644 --- a/tests/regression/75-termination/32-multithread-terminating.c +++ b/tests/regression/75-termination/32-multithread-terminating.c @@ -1,4 +1,5 @@ -// TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// The program terminates but as the termination analysis is meant to not handle multithreaded programs we expect NonTerm here #include #include #include diff --git a/tests/regression/75-termination/42-downjumping-goto-loopless-terminating.c b/tests/regression/75-termination/42-downjumping-goto-loopless-terminating.c new file mode 100644 index 0000000000..54bcfdc508 --- /dev/null +++ b/tests/regression/75-termination/42-downjumping-goto-loopless-terminating.c @@ -0,0 +1,19 @@ +// TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() { // Currently not able to detect up-jumping loop free gotos + goto mark2; + +mark1: + printf("This is mark1\n"); + goto mark3; + +mark2: + printf("This is mark2\n"); + goto mark3; + +mark3: + printf("This is mark3\n"); + + return 0; +} From 98c062a2c28db2418adcd67fa5c540c75d2cfebf Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Mon, 10 Jul 2023 16:12:13 +0200 Subject: [PATCH 0307/1312] Test indentation --- .../01-simple-loop-terminating.c | 6 +- .../02-simple-loop-nonterminating.c | 6 +- .../03-nested-loop-terminating.c | 9 ++- .../04-nested-loop-nonterminating.c | 9 ++- .../75-termination/05-for-loop-terminating.c | 6 +- .../06-for-loop-nonterminating.c | 6 +- .../07-nested-for-loop-terminating.c | 9 ++- .../08-nested-for-loop-nonterminating.c | 9 ++- .../09-complex-for-loop-terminating.c | 51 +++++++++++------ .../10-complex-loop-terminating.c | 57 +++++++++++++------ .../75-termination/11-loopless-termination.c | 3 +- .../12-do-while-instant-terminating.c | 6 +- .../75-termination/13-do-while-terminating.c | 6 +- .../14-do-while-nonterminating.c | 6 +- .../15-complex-loop-combination-terminating.c | 53 +++++++++++------ ...16-nested-loop-nontrivial-nonterminating.c | 9 ++- .../75-termination/17-goto-terminating.c | 6 +- .../75-termination/18-goto-nonterminating.c | 3 +- .../75-termination/19-rand-terminating.c | 16 ++++-- .../75-termination/20-rand-nonterminating.c | 16 ++++-- .../21-no-exit-on-rand-unproofable.c | 12 ++-- .../22-exit-on-rand-unproofable.c | 6 +- .../23-exit-on-rand-terminating.c | 6 +- .../24-upjumping-goto-loopless-terminating.c | 3 +- .../25-leave-loop-goto-terminating.c | 9 ++- .../26-enter-loop-goto-terminating.c | 9 ++- .../27-upjumping-goto-nonterminating.c | 3 +- .../28-do-while-continue-terminating.c | 9 ++- .../29-do-while-continue-nonterminating.c | 9 ++- .../30-goto-out-of-inner-loop-terminating.c | 12 ++-- ...31-goto-out-of-inner-loop-nonterminating.c | 12 ++-- .../32-multithread-terminating.c | 6 +- .../33-multithread-nonterminating.c | 9 ++- .../34-nested-for-loop-nonterminating.c | 9 ++- ...out-of-inner-loop-with-print-terminating.c | 12 ++-- .../75-termination/36-recursion-terminating.c | 9 ++- .../37-recursion-nonterminating.c | 9 ++- .../38-recursion-nested-terminating.c | 15 +++-- .../39-recursion-nested-nonterminating.c | 9 ++- .../75-termination/40-complex-conditions.c | 21 ++++--- .../regression/75-termination/41-more-tests.c | 15 +++-- 41 files changed, 334 insertions(+), 162 deletions(-) diff --git a/tests/regression/75-termination/01-simple-loop-terminating.c b/tests/regression/75-termination/01-simple-loop-terminating.c index 66b6585f67..aaa2a7a895 100644 --- a/tests/regression/75-termination/01-simple-loop-terminating.c +++ b/tests/regression/75-termination/01-simple-loop-terminating.c @@ -1,10 +1,12 @@ // TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int i = 1; - while (i <= 10) { + while (i <= 10) + { printf("%d\n", i); i++; } diff --git a/tests/regression/75-termination/02-simple-loop-nonterminating.c b/tests/regression/75-termination/02-simple-loop-nonterminating.c index 6fe8816da4..51fb340f3b 100644 --- a/tests/regression/75-termination/02-simple-loop-nonterminating.c +++ b/tests/regression/75-termination/02-simple-loop-nonterminating.c @@ -1,8 +1,10 @@ // NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { - while (1) { +int main() +{ + while (1) + { continue; } diff --git a/tests/regression/75-termination/03-nested-loop-terminating.c b/tests/regression/75-termination/03-nested-loop-terminating.c index 4e3fafabcf..70327c1016 100644 --- a/tests/regression/75-termination/03-nested-loop-terminating.c +++ b/tests/regression/75-termination/03-nested-loop-terminating.c @@ -1,17 +1,20 @@ // TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int rows = 3; int columns = 4; int i = 1; // Outer while loop for rows - while (i <= rows) { + while (i <= rows) + { int j = 1; // Inner while loop for columns - while (j <= columns) { + while (j <= columns) + { printf("(%d, %d) ", i, j); j++; } diff --git a/tests/regression/75-termination/04-nested-loop-nonterminating.c b/tests/regression/75-termination/04-nested-loop-nonterminating.c index 00c2554ed2..fffc932f36 100644 --- a/tests/regression/75-termination/04-nested-loop-nonterminating.c +++ b/tests/regression/75-termination/04-nested-loop-nonterminating.c @@ -1,13 +1,16 @@ // NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int outerCount = 1; - while (outerCount <= 3) { + while (outerCount <= 3) + { int innerCount = 1; - while (1) { + while (1) + { printf("(%d, %d) ", outerCount, innerCount); innerCount++; } diff --git a/tests/regression/75-termination/05-for-loop-terminating.c b/tests/regression/75-termination/05-for-loop-terminating.c index fe07200e5b..bf58408487 100644 --- a/tests/regression/75-termination/05-for-loop-terminating.c +++ b/tests/regression/75-termination/05-for-loop-terminating.c @@ -1,10 +1,12 @@ // TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int i; - for (i = 1; i <= 10; i++) { + for (i = 1; i <= 10; i++) + { printf("%d\n", i); } diff --git a/tests/regression/75-termination/06-for-loop-nonterminating.c b/tests/regression/75-termination/06-for-loop-nonterminating.c index 374cd3e59f..be876c9741 100644 --- a/tests/regression/75-termination/06-for-loop-nonterminating.c +++ b/tests/regression/75-termination/06-for-loop-nonterminating.c @@ -1,8 +1,10 @@ // NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { - for (;;) { +int main() +{ + for (;;) + { printf("This loop does not terminate.\n"); } diff --git a/tests/regression/75-termination/07-nested-for-loop-terminating.c b/tests/regression/75-termination/07-nested-for-loop-terminating.c index a94f3f360c..1c43eeaada 100644 --- a/tests/regression/75-termination/07-nested-for-loop-terminating.c +++ b/tests/regression/75-termination/07-nested-for-loop-terminating.c @@ -1,13 +1,16 @@ // TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int rows = 3; int columns = 4; // Nested loop to iterate over rows and columns - for (int i = 1; i <= rows; i++) { - for (int j = 1; j <= columns; j++) { + for (int i = 1; i <= rows; i++) + { + for (int j = 1; j <= columns; j++) + { printf("(%d, %d) ", i, j); } printf("\n"); diff --git a/tests/regression/75-termination/08-nested-for-loop-nonterminating.c b/tests/regression/75-termination/08-nested-for-loop-nonterminating.c index e78e819cc0..e360d45d0a 100644 --- a/tests/regression/75-termination/08-nested-for-loop-nonterminating.c +++ b/tests/regression/75-termination/08-nested-for-loop-nonterminating.c @@ -1,11 +1,14 @@ // NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int outerCount, innerCount; - for (outerCount = 1; outerCount <= 3; outerCount++) { - for (innerCount = 1;; innerCount++) { + for (outerCount = 1; outerCount <= 3; outerCount++) + { + for (innerCount = 1;; innerCount++) + { printf("(%d, %d) ", outerCount, innerCount); } diff --git a/tests/regression/75-termination/09-complex-for-loop-terminating.c b/tests/regression/75-termination/09-complex-for-loop-terminating.c index 018fba6822..9767b4bc1c 100644 --- a/tests/regression/75-termination/09-complex-for-loop-terminating.c +++ b/tests/regression/75-termination/09-complex-for-loop-terminating.c @@ -1,57 +1,75 @@ // TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --set sem.int.signed_overflow assume_none #include -int main() { +int main() +{ int i, j, k; // Outer loop - for (i = 1; i <= 5; i++) { + for (i = 1; i <= 5; i++) + { // Inner loop 1 - for (j = 1; j <= i; j++) { + for (j = 1; j <= i; j++) + { printf("%d ", j); } printf("\n"); // Inner loop 2 - for (k = i; k >= 1; k--) { + for (k = i; k >= 1; k--) + { printf("%d ", k); } printf("\n"); } // Additional loop - for (i = 5; i >= 1; i--) { - for (j = i; j >= 1; j--) { + for (i = 5; i >= 1; i--) + { + for (j = i; j >= 1; j--) + { printf("%d ", j); } printf("\n"); } // Loop with conditions - for (i = 1; i <= 10; i++) { - if (i % 2 == 0) { + for (i = 1; i <= 10; i++) + { + if (i % 2 == 0) + { printf("%d is even\n", i); - } else { + } + else + { printf("%d is odd\n", i); } } // Loop with nested conditions - for (i = 1; i <= 10; i++) { + for (i = 1; i <= 10; i++) + { printf("Number: %d - ", i); - if (i < 5) { + if (i < 5) + { printf("Less than 5\n"); - } else if (i > 5) { + } + else if (i > 5) + { printf("Greater than 5\n"); - } else { + } + else + { printf("Equal to 5\n"); } } // Loop with a break statement - for (i = 1; i <= 10; i++) { + for (i = 1; i <= 10; i++) + { printf("%d ", i); - if (i == 5) { + if (i == 5) + { break; } } @@ -59,7 +77,8 @@ int main() { // Loop with multiple variables int a, b, c; - for (a = 1, b = 2, c = 3; a <= 10; a++, b += 2, c += 3) { + for (a = 1, b = 2, c = 3; a <= 10; a++, b += 2, c += 3) + { printf("%d %d %d\n", a, b, c); } diff --git a/tests/regression/75-termination/10-complex-loop-terminating.c b/tests/regression/75-termination/10-complex-loop-terminating.c index 88bf6a4565..19091b1033 100644 --- a/tests/regression/75-termination/10-complex-loop-terminating.c +++ b/tests/regression/75-termination/10-complex-loop-terminating.c @@ -1,15 +1,18 @@ // TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --set sem.int.signed_overflow assume_none #include -int main() { +int main() +{ int i = 1; int j = 1; int k = 5; // Outer while loop - while (i <= 5) { + while (i <= 5) + { // Inner while loop 1 - while (j <= i) { + while (j <= i) + { printf("%d ", j); j++; } @@ -17,7 +20,8 @@ int main() { j = 1; // Inner while loop 2 - while (k >= 1) { + while (k >= 1) + { printf("%d ", k); k--; } @@ -29,9 +33,11 @@ int main() { // Additional while loop i = 5; - while (i >= 1) { + while (i >= 1) + { j = i; - while (j >= 1) { + while (j >= 1) + { printf("%d ", j); j--; } @@ -41,10 +47,14 @@ int main() { // Loop with conditions i = 1; - while (i <= 10) { - if (i % 2 == 0) { + while (i <= 10) + { + if (i % 2 == 0) + { printf("%d is even\n", i); - } else { + } + else + { printf("%d is odd\n", i); } i++; @@ -52,13 +62,19 @@ int main() { // Loop with nested conditions i = 1; - while (i <= 10) { + while (i <= 10) + { printf("Number: %d - ", i); - if (i < 5) { + if (i < 5) + { printf("Less than 5\n"); - } else if (i > 5) { + } + else if (i > 5) + { printf("Greater than 5\n"); - } else { + } + else + { printf("Equal to 5\n"); } i++; @@ -66,9 +82,11 @@ int main() { // Loop with a break statement i = 1; - while (i <= 10) { + while (i <= 10) + { printf("%d ", i); - if (i == 5) { + if (i == 5) + { break; } i++; @@ -77,8 +95,10 @@ int main() { // Loop with a continue statement i = 1; - while (i <= 10) { - if (i % 2 == 0) { + while (i <= 10) + { + if (i % 2 == 0) + { i++; continue; } @@ -91,7 +111,8 @@ int main() { int a = 1; int b = 2; int c = 3; - while (a <= 10) { + while (a <= 10) + { printf("%d %d %d\n", a, b, c); a++; b += 2; diff --git a/tests/regression/75-termination/11-loopless-termination.c b/tests/regression/75-termination/11-loopless-termination.c index a1846905fc..51c0605757 100644 --- a/tests/regression/75-termination/11-loopless-termination.c +++ b/tests/regression/75-termination/11-loopless-termination.c @@ -1,7 +1,8 @@ // TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ printf("Terminating code without a loop\n"); return 0; } diff --git a/tests/regression/75-termination/12-do-while-instant-terminating.c b/tests/regression/75-termination/12-do-while-instant-terminating.c index 087b88f1f5..3767430a51 100644 --- a/tests/regression/75-termination/12-do-while-instant-terminating.c +++ b/tests/regression/75-termination/12-do-while-instant-terminating.c @@ -1,10 +1,12 @@ // TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int i = 0; - do { + do + { printf("Inside the do-while loop\n"); } while (i > 0); diff --git a/tests/regression/75-termination/13-do-while-terminating.c b/tests/regression/75-termination/13-do-while-terminating.c index 34343d6ba6..8faeec1e64 100644 --- a/tests/regression/75-termination/13-do-while-terminating.c +++ b/tests/regression/75-termination/13-do-while-terminating.c @@ -1,10 +1,12 @@ // TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int i = 1; - do { + do + { printf("Inside the do-while loop\n"); i++; } while (i <= 5); diff --git a/tests/regression/75-termination/14-do-while-nonterminating.c b/tests/regression/75-termination/14-do-while-nonterminating.c index 6473fdc20d..30c8349bb5 100644 --- a/tests/regression/75-termination/14-do-while-nonterminating.c +++ b/tests/regression/75-termination/14-do-while-nonterminating.c @@ -1,10 +1,12 @@ // NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int i = 1; - do { + do + { printf("Inside the do-while loop\n"); i++; } while (i >= 2); diff --git a/tests/regression/75-termination/15-complex-loop-combination-terminating.c b/tests/regression/75-termination/15-complex-loop-combination-terminating.c index 23282d24b1..d987397dd7 100644 --- a/tests/regression/75-termination/15-complex-loop-combination-terminating.c +++ b/tests/regression/75-termination/15-complex-loop-combination-terminating.c @@ -1,25 +1,29 @@ // TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --set sem.int.signed_overflow assume_none #include -int main() { +int main() +{ // Non-nested loops int i; // for loop - for (i = 1; i <= 10; i++) { + for (i = 1; i <= 10; i++) + { printf("For loop iteration: %d\n", i); } // while loop int j = 1; - while (j <= 10) { + while (j <= 10) + { printf("While loop iteration: %d\n", j); j++; } // do-while loop int k = 1; - do { + do + { printf("Do-While loop iteration: %d\n", k); k++; } while (k <= 10); @@ -28,9 +32,11 @@ int main() { int a, b; // Nested for and while loop - for (a = 1; a <= 5; a++) { + for (a = 1; a <= 5; a++) + { int c = 1; - while (c <= a) { + while (c <= a) + { printf("Nested For-While loop: %d\n", c); c++; } @@ -38,9 +44,11 @@ int main() { // Nested while and do-while loop int x = 1; - while (x <= 5) { + while (x <= 5) + { int y = 1; - do { + do + { printf("Nested While-Do-While loop: %d\n", y); y++; } while (y <= x); @@ -49,8 +57,10 @@ int main() { // Nested do-while and for loop int p = 1; - do { - for (int q = 1; q <= p; q++) { + do + { + for (int q = 1; q <= p; q++) + { printf("Nested Do-While-For loop: %d\n", q); } p++; @@ -61,13 +71,16 @@ int main() { // Nested while loop with a break statement int n = 1; - while (n <= 5) { + while (n <= 5) + { printf("Outer While loop iteration: %d\n", n); m = 1; - while (1) { + while (1) + { printf("Inner While loop iteration: %d\n", m); m++; - if (m == 4) { + if (m == 4) + { break; } } @@ -75,13 +88,19 @@ int main() { } // Loop with nested conditions - for (int v = 1; v <= 10; v++) { + for (int v = 1; v <= 10; v++) + { printf("Loop with Nested Conditions: %d - ", v); - if (v < 5) { + if (v < 5) + { printf("Less than 5\n"); - } else if (v > 5) { + } + else if (v > 5) + { printf("Greater than 5\n"); - } else { + } + else + { printf("Equal to 5\n"); } } diff --git a/tests/regression/75-termination/16-nested-loop-nontrivial-nonterminating.c b/tests/regression/75-termination/16-nested-loop-nontrivial-nonterminating.c index f89e28d91a..87b4b82ed9 100644 --- a/tests/regression/75-termination/16-nested-loop-nontrivial-nonterminating.c +++ b/tests/regression/75-termination/16-nested-loop-nontrivial-nonterminating.c @@ -1,13 +1,16 @@ // NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int outerCount = 1; - while (outerCount <= 3) { + while (outerCount <= 3) + { int innerCount = 1; - while (outerCount < 3 || innerCount > 0) { + while (outerCount < 3 || innerCount > 0) + { printf("(%d, %d) ", outerCount, innerCount); innerCount++; } diff --git a/tests/regression/75-termination/17-goto-terminating.c b/tests/regression/75-termination/17-goto-terminating.c index 8270f8c960..45e92f32e8 100644 --- a/tests/regression/75-termination/17-goto-terminating.c +++ b/tests/regression/75-termination/17-goto-terminating.c @@ -2,14 +2,16 @@ // The program terminates but the analysis is currently only meant to detect up-jumping gotos as potentially NonTerminating, therefore we expect an NonTerm #include -int main() { +int main() +{ int num = 1; loop: printf("Current number: %d\n", num); num++; - if (num <= 10) { + if (num <= 10) + { goto loop; // We are not able to detect up-jumping gotos as terminating, we // just warn about them might being nonterminating. } diff --git a/tests/regression/75-termination/18-goto-nonterminating.c b/tests/regression/75-termination/18-goto-nonterminating.c index e26f02ec11..25f79e5b57 100644 --- a/tests/regression/75-termination/18-goto-nonterminating.c +++ b/tests/regression/75-termination/18-goto-nonterminating.c @@ -1,7 +1,8 @@ // NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int num = 1; loop: diff --git a/tests/regression/75-termination/19-rand-terminating.c b/tests/regression/75-termination/19-rand-terminating.c index fc5d6ee7b7..06deac6c34 100644 --- a/tests/regression/75-termination/19-rand-terminating.c +++ b/tests/regression/75-termination/19-rand-terminating.c @@ -3,19 +3,25 @@ #include #include -int main() { +int main() +{ // Seed the random number generator srand(time(NULL)); - if (rand()) { + if (rand()) + { // Loop inside the if part - for (int i = 1; i <= 5; i++) { + for (int i = 1; i <= 5; i++) + { printf("Loop inside if part: %d\n", i); } - } else { + } + else + { // Loop inside the else part int j = 1; - while (j <= 5) { + while (j <= 5) + { printf("Loop inside else part: %d\n", j); j++; } diff --git a/tests/regression/75-termination/20-rand-nonterminating.c b/tests/regression/75-termination/20-rand-nonterminating.c index e74c15c948..83630ed6c4 100644 --- a/tests/regression/75-termination/20-rand-nonterminating.c +++ b/tests/regression/75-termination/20-rand-nonterminating.c @@ -3,19 +3,25 @@ #include #include -int main() { +int main() +{ // Seed the random number generator srand(time(NULL)); - if (rand()) { + if (rand()) + { // Loop inside the if part - for (int i = 1; i >= 0; i++) { + for (int i = 1; i >= 0; i++) + { printf("Loop inside if part: %d\n", i); } - } else { + } + else + { // Loop inside the else part int j = 1; - while (j > 0) { + while (j > 0) + { printf("Loop inside else part: %d\n", j); } } diff --git a/tests/regression/75-termination/21-no-exit-on-rand-unproofable.c b/tests/regression/75-termination/21-no-exit-on-rand-unproofable.c index 10774e3420..3e7a65dfd4 100644 --- a/tests/regression/75-termination/21-no-exit-on-rand-unproofable.c +++ b/tests/regression/75-termination/21-no-exit-on-rand-unproofable.c @@ -1,14 +1,18 @@ // NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int forever, i = 0; // This loop is not provable, therefore it should throw a warning - while (i < 4 || forever == 1) { + while (i < 4 || forever == 1) + { i++; - if (i == 4) { - if (rand()) { + if (i == 4) + { + if (rand()) + { forever = 1; } } diff --git a/tests/regression/75-termination/22-exit-on-rand-unproofable.c b/tests/regression/75-termination/22-exit-on-rand-unproofable.c index 3f76f05aa9..b8d7992bd9 100644 --- a/tests/regression/75-termination/22-exit-on-rand-unproofable.c +++ b/tests/regression/75-termination/22-exit-on-rand-unproofable.c @@ -1,11 +1,13 @@ // NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int forever = 1; // This loop is not provable, therefore it should throw a warning - while (forever == 1) { + while (forever == 1) + { if (rand()) // May exit, may not { forever = 0; diff --git a/tests/regression/75-termination/23-exit-on-rand-terminating.c b/tests/regression/75-termination/23-exit-on-rand-terminating.c index 080b3c8871..24d4980406 100644 --- a/tests/regression/75-termination/23-exit-on-rand-terminating.c +++ b/tests/regression/75-termination/23-exit-on-rand-terminating.c @@ -2,14 +2,16 @@ #include #include -int main() { +int main() +{ int short_run, i = 0; while (i < 90 && short_run != 1) // Currently not able to detect this as terminating { i++; - if (rand()) { + if (rand()) + { short_run = 1; } } diff --git a/tests/regression/75-termination/24-upjumping-goto-loopless-terminating.c b/tests/regression/75-termination/24-upjumping-goto-loopless-terminating.c index 88d8c8f418..3dbff9d7ea 100644 --- a/tests/regression/75-termination/24-upjumping-goto-loopless-terminating.c +++ b/tests/regression/75-termination/24-upjumping-goto-loopless-terminating.c @@ -2,7 +2,8 @@ // The program terminates but the analysis is currently only meant to detect up-jumping gotos as potentially NonTerminating, therefore we expect an NonTerm #include -int main() { // Currently not able to detect up-jumping loop free gotos +int main() +{ // Currently not able to detect up-jumping loop free gotos goto mark2; mark1: diff --git a/tests/regression/75-termination/25-leave-loop-goto-terminating.c b/tests/regression/75-termination/25-leave-loop-goto-terminating.c index 35edf86938..2cda3d3a03 100644 --- a/tests/regression/75-termination/25-leave-loop-goto-terminating.c +++ b/tests/regression/75-termination/25-leave-loop-goto-terminating.c @@ -1,10 +1,12 @@ // TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int counter = 0; - while (1) { + while (1) + { counter++; // Dummy code @@ -13,7 +15,8 @@ int main() { printf("Result: %d\n", result); // Condition to terminate the loop - if (result >= 10) { // Apron is not able to detect this + if (result >= 10) + { // Apron is not able to detect this goto end; } } diff --git a/tests/regression/75-termination/26-enter-loop-goto-terminating.c b/tests/regression/75-termination/26-enter-loop-goto-terminating.c index 97b46f66ca..0de9a95d6c 100644 --- a/tests/regression/75-termination/26-enter-loop-goto-terminating.c +++ b/tests/regression/75-termination/26-enter-loop-goto-terminating.c @@ -1,12 +1,14 @@ // TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int counter = 0; goto jump_point; - while (1) { + while (1) + { counter++; // Dummy code @@ -16,7 +18,8 @@ int main() { printf("Result: %d\n", result); // Condition to terminate the loop - if (result >= 10) { // Apron is not able to detect this + if (result >= 10) + { // Apron is not able to detect this goto end; } } diff --git a/tests/regression/75-termination/27-upjumping-goto-nonterminating.c b/tests/regression/75-termination/27-upjumping-goto-nonterminating.c index a6621dd986..e27d7161d5 100644 --- a/tests/regression/75-termination/27-upjumping-goto-nonterminating.c +++ b/tests/regression/75-termination/27-upjumping-goto-nonterminating.c @@ -1,7 +1,8 @@ // NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ goto mark2; mark1: diff --git a/tests/regression/75-termination/28-do-while-continue-terminating.c b/tests/regression/75-termination/28-do-while-continue-terminating.c index 402e744e2f..61bd578dcf 100644 --- a/tests/regression/75-termination/28-do-while-continue-terminating.c +++ b/tests/regression/75-termination/28-do-while-continue-terminating.c @@ -1,13 +1,16 @@ // TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int i = 1; - do { + do + { i++; printf("Inside the do-while loop\n"); - if (i % 2 == 0) { + if (i % 2 == 0) + { printf("Skipping %i is even\n", i); continue; // This is handled as an goto to line 8 and therefore an up-jumping goto diff --git a/tests/regression/75-termination/29-do-while-continue-nonterminating.c b/tests/regression/75-termination/29-do-while-continue-nonterminating.c index be3e7e12de..41f1dbd5bc 100644 --- a/tests/regression/75-termination/29-do-while-continue-nonterminating.c +++ b/tests/regression/75-termination/29-do-while-continue-nonterminating.c @@ -1,14 +1,17 @@ // NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int i = 1; - do { + do + { printf("Inside the do-while loop\n"); i++; - if (i % 2) { + if (i % 2) + { printf("Continue as %i is odd\n", i); continue; } diff --git a/tests/regression/75-termination/30-goto-out-of-inner-loop-terminating.c b/tests/regression/75-termination/30-goto-out-of-inner-loop-terminating.c index e2eff29c8b..5cdadf4396 100644 --- a/tests/regression/75-termination/30-goto-out-of-inner-loop-terminating.c +++ b/tests/regression/75-termination/30-goto-out-of-inner-loop-terminating.c @@ -1,15 +1,19 @@ // TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int rows = 5; int columns = 5; // Outer loop for rows - for (int i = 1; i <= rows; i++) { + for (int i = 1; i <= rows; i++) + { // Inner loop for columns - for (int j = 1; j <= columns; j++) { - if (j == 3) { + for (int j = 1; j <= columns; j++) + { + if (j == 3) + { goto outer_loop; // Jump to the label "outer_loop" } printf("(%d, %d) ", i, j); diff --git a/tests/regression/75-termination/31-goto-out-of-inner-loop-nonterminating.c b/tests/regression/75-termination/31-goto-out-of-inner-loop-nonterminating.c index 756a93414b..cb54b5dd2f 100644 --- a/tests/regression/75-termination/31-goto-out-of-inner-loop-nonterminating.c +++ b/tests/regression/75-termination/31-goto-out-of-inner-loop-nonterminating.c @@ -1,15 +1,19 @@ // NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int rows = 5; int columns = 5; // Outer loop for rows - for (int i = 1; 1; i++) { + for (int i = 1; 1; i++) + { // Inner loop for columns - for (int j = 1; j <= columns; j++) { - if (j == 3) { + for (int j = 1; j <= columns; j++) + { + if (j == 3) + { printf("Goto as continue for outer loop\n"); goto outer_loop; // Jump to the label "outer_loop" } diff --git a/tests/regression/75-termination/32-multithread-terminating.c b/tests/regression/75-termination/32-multithread-terminating.c index 737b867cde..fbac273776 100644 --- a/tests/regression/75-termination/32-multithread-terminating.c +++ b/tests/regression/75-termination/32-multithread-terminating.c @@ -5,14 +5,16 @@ #include // Thread function -void *printPID(void *arg) { +void *printPID(void *arg) +{ pid_t pid = getpid(); pthread_t tid = pthread_self(); printf("Thread ID: %lu, Process ID: %d\n", (unsigned long)tid, pid); return NULL; } -int main() { +int main() +{ // Create three threads pthread_t thread1, thread2, thread3; pthread_create(&thread1, NULL, printPID, NULL); diff --git a/tests/regression/75-termination/33-multithread-nonterminating.c b/tests/regression/75-termination/33-multithread-nonterminating.c index 278c107821..dad62aa0f4 100644 --- a/tests/regression/75-termination/33-multithread-nonterminating.c +++ b/tests/regression/75-termination/33-multithread-nonterminating.c @@ -6,10 +6,12 @@ #include // Thread function -void *printPID(void *arg) { +void *printPID(void *arg) +{ pid_t pid = getpid(); pthread_t tid = pthread_self(); - while (1) { + while (1) + { printf("Thread ID: %lu, Process ID: %d\n", (unsigned long)tid, pid); struct timespec sleepTime; sleepTime.tv_sec = 1; // Seconds @@ -21,7 +23,8 @@ void *printPID(void *arg) { return NULL; } -int main() { +int main() +{ // Create three threads pthread_t thread1, thread2, thread3; pthread_create(&thread1, NULL, printPID, NULL); diff --git a/tests/regression/75-termination/34-nested-for-loop-nonterminating.c b/tests/regression/75-termination/34-nested-for-loop-nonterminating.c index 3384ed0f60..709960640f 100644 --- a/tests/regression/75-termination/34-nested-for-loop-nonterminating.c +++ b/tests/regression/75-termination/34-nested-for-loop-nonterminating.c @@ -1,11 +1,14 @@ // NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int outerCount, innerCount; - for (outerCount = 1; outerCount <= 3; outerCount++) { - for (innerCount = 1; innerCount > 0; innerCount++) { + for (outerCount = 1; outerCount <= 3; outerCount++) + { + for (innerCount = 1; innerCount > 0; innerCount++) + { printf("(%d, %d) ", outerCount, innerCount); } diff --git a/tests/regression/75-termination/35-goto-out-of-inner-loop-with-print-terminating.c b/tests/regression/75-termination/35-goto-out-of-inner-loop-with-print-terminating.c index 646f39111a..f564354e51 100644 --- a/tests/regression/75-termination/35-goto-out-of-inner-loop-with-print-terminating.c +++ b/tests/regression/75-termination/35-goto-out-of-inner-loop-with-print-terminating.c @@ -1,15 +1,19 @@ // TERM PARAM: --set "ana.activated[+]" termination --set "ana.activated[+]" apron --enable ana.int.interval --set ana.apron.domain polyhedra --set sem.int.signed_overflow assume_none #include -int main() { +int main() +{ int rows = 5; int columns = 5; // Outer loop for rows - for (int i = 1; i <= rows; i++) { + for (int i = 1; i <= rows; i++) + { // Inner loop for columns - for (int j = 1; j <= columns; j++) { - if (j == 3) { + for (int j = 1; j <= columns; j++) + { + if (j == 3) + { goto outer_loop; // Jump to the label "outer_loop" } printf("(%d, %d) ", i, j); diff --git a/tests/regression/75-termination/36-recursion-terminating.c b/tests/regression/75-termination/36-recursion-terminating.c index 533778332f..7336417c91 100644 --- a/tests/regression/75-termination/36-recursion-terminating.c +++ b/tests/regression/75-termination/36-recursion-terminating.c @@ -1,9 +1,11 @@ // TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -void recursiveFunction(int n) { +void recursiveFunction(int n) +{ // Base case: When n reaches 0, stop recursion - if (n == 0) { + if (n == 0) + { printf("Terminating recursion\n"); return; } @@ -14,7 +16,8 @@ void recursiveFunction(int n) { recursiveFunction(n - 1); } -int main() { +int main() +{ // Call the recursive function with an initial value recursiveFunction(5); diff --git a/tests/regression/75-termination/37-recursion-nonterminating.c b/tests/regression/75-termination/37-recursion-nonterminating.c index 089a4d3bcc..38aaf3de85 100644 --- a/tests/regression/75-termination/37-recursion-nonterminating.c +++ b/tests/regression/75-termination/37-recursion-nonterminating.c @@ -1,9 +1,11 @@ // NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --enable ana.context.widen #include -void recursiveFunction(int n) { +void recursiveFunction(int n) +{ // Base case: When n reaches 0, stop recursion - if (n == 30) { + if (n == 30) + { printf("Terminating recursion\n"); return; } @@ -14,7 +16,8 @@ void recursiveFunction(int n) { recursiveFunction(n - 1); } -int main() { +int main() +{ // Call the recursive function with an initial value recursiveFunction(5); diff --git a/tests/regression/75-termination/38-recursion-nested-terminating.c b/tests/regression/75-termination/38-recursion-nested-terminating.c index eace365a44..bef05eb1a0 100644 --- a/tests/regression/75-termination/38-recursion-nested-terminating.c +++ b/tests/regression/75-termination/38-recursion-nested-terminating.c @@ -1,8 +1,10 @@ // TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -void innerRecursiveFunction(int n) { - if (n == 0) { +void innerRecursiveFunction(int n) +{ + if (n == 0) + { printf("Terminating inner recursion\n"); return; } @@ -13,8 +15,10 @@ void innerRecursiveFunction(int n) { innerRecursiveFunction(n - 1); } -void outerRecursiveFunction(int n) { - if (n == 0) { +void outerRecursiveFunction(int n) +{ + if (n == 0) + { printf("Terminating outer recursion\n"); return; } @@ -28,7 +32,8 @@ void outerRecursiveFunction(int n) { innerRecursiveFunction(n); } -int main() { +int main() +{ // Call the outerRecursiveFunction with an initial value outerRecursiveFunction(3); diff --git a/tests/regression/75-termination/39-recursion-nested-nonterminating.c b/tests/regression/75-termination/39-recursion-nested-nonterminating.c index 8b57f83857..8311d9f573 100644 --- a/tests/regression/75-termination/39-recursion-nested-nonterminating.c +++ b/tests/regression/75-termination/39-recursion-nested-nonterminating.c @@ -1,14 +1,16 @@ // NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -void innerRecursiveFunction() { +void innerRecursiveFunction() +{ printf("Nested recursive call\n"); // Recursive call to the innerRecursiveFunction innerRecursiveFunction(); } -void outerRecursiveFunction() { +void outerRecursiveFunction() +{ printf("Outer recursive call\n"); // Recursive call to the outerRecursiveFunction @@ -18,7 +20,8 @@ void outerRecursiveFunction() { innerRecursiveFunction(); } -int main() { +int main() +{ // Call the outerRecursiveFunction outerRecursiveFunction(); diff --git a/tests/regression/75-termination/40-complex-conditions.c b/tests/regression/75-termination/40-complex-conditions.c index d5fe6b808a..a74c863fb4 100644 --- a/tests/regression/75-termination/40-complex-conditions.c +++ b/tests/regression/75-termination/40-complex-conditions.c @@ -1,12 +1,15 @@ // TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ int i; // Loop with a continue statement - for (i = 1; i <= 10; i++) { - if (i % 2 == 0) { + for (i = 1; i <= 10; i++) + { + if (i % 2 == 0) + { continue; } printf("%d ", i); @@ -14,8 +17,10 @@ int main() { printf("\n"); // Loop with complex conditions - for (i = 1; i <= 10; i++) { - if (i > 5 && i % 2 == 0) { + for (i = 1; i <= 10; i++) + { + if (i > 5 && i % 2 == 0) + { printf("%d ", i); } } @@ -23,8 +28,10 @@ int main() { // Loop with complex conditions i = 1; - while (i <= 10) { - if (i > 5 && i % 2 == 0) { + while (i <= 10) + { + if (i > 5 && i % 2 == 0) + { printf("%d ", i); } i++; diff --git a/tests/regression/75-termination/41-more-tests.c b/tests/regression/75-termination/41-more-tests.c index 272be43293..1fb9f83f5d 100644 --- a/tests/regression/75-termination/41-more-tests.c +++ b/tests/regression/75-termination/41-more-tests.c @@ -1,11 +1,14 @@ // TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -int main() { +int main() +{ // Loop with a continue statement - for (int r = 1; r <= 10; r++) { - if (r % 3 == 0) { + for (int r = 1; r <= 10; r++) + { + if (r % 3 == 0) + { continue; } printf("Loop with Continue: %d\n", r); @@ -13,14 +16,16 @@ int main() { // Loop with multiple conditions int s = 1; - while (s <= 10 && s % 2 == 0) { + while (s <= 10 && s % 2 == 0) + { printf("Loop with Multiple Conditions: %d\n", s); s++; } // Loop with multiple variables int t, u; - for (t = 1, u = 10; t <= 5 && u >= 5; t++, u--) { + for (t = 1, u = 10; t <= 5 && u >= 5; t++, u--) + { printf("Loop with Multiple Variables: %d %d\n", t, u); } } \ No newline at end of file From 4dd330ce4c530b68f1b2f4722c89c3e44c289a1f Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Mon, 10 Jul 2023 16:55:45 +0200 Subject: [PATCH 0308/1312] Execute termination tests in CI even if skipped. --- .github/workflows/locked.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/locked.yml b/.github/workflows/locked.yml index 751ade6880..48809d34c1 100644 --- a/.github/workflows/locked.yml +++ b/.github/workflows/locked.yml @@ -64,6 +64,9 @@ jobs: - name: Test apron regression (Mukherjee et. al SAS '17 paper') # skipped by default but CI has apron, so explicitly test group (which ignores skipping -- it's now a feature!) run: ruby scripts/update_suite.rb group apron-mukherjee -s + - name: Test apron termination regression # skipped by default but CI has apron, so explicitly test group (which ignores skipping -- it's now a feature!) + run: ruby scripts/update_suite.rb group termination -s + - name: Test regression cram run: opam exec -- dune runtest tests/regression From 5f984170bd0de91e79d972be599896a637037a84 Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Thu, 13 Jul 2023 20:51:13 +0200 Subject: [PATCH 0309/1312] Test explanation && new test case termination 43 --- ...-multi-expression-conditions-terminating.c | 44 +++++++++++++++++++ ...itions.c => 41-for-continue-terminating.c} | 25 +++-------- .../regression/75-termination/41-more-tests.c | 31 ------------- .../43-return-from-endless-loop-terminating.c | 14 ++++++ 4 files changed, 64 insertions(+), 50 deletions(-) create mode 100644 tests/regression/75-termination/40-multi-expression-conditions-terminating.c rename tests/regression/75-termination/{40-complex-conditions.c => 41-for-continue-terminating.c} (52%) delete mode 100644 tests/regression/75-termination/41-more-tests.c create mode 100644 tests/regression/75-termination/43-return-from-endless-loop-terminating.c diff --git a/tests/regression/75-termination/40-multi-expression-conditions-terminating.c b/tests/regression/75-termination/40-multi-expression-conditions-terminating.c new file mode 100644 index 0000000000..8e7b4e273d --- /dev/null +++ b/tests/regression/75-termination/40-multi-expression-conditions-terminating.c @@ -0,0 +1,44 @@ +// TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int i; + + // Loop with complex conditions + for (i = 1; i <= 10; i++) + { + if (i > 5 && i % 2 == 0) // CIL defines new jump labels to default location (-1) + { + printf("%d ", i); + } + } + printf("\n"); + + // Loop with complex conditions + i = 1; + while (i <= 10) + { + if (i > 5 && i % 2 == 0) // CIL defines new jump labels to default location (-1) + { + printf("%d ", i); + } + i++; + } + printf("\n"); + + // Loop with multiple conditions + int s = 1; + while (s <= 10 && s % 2 == 0) // CIL defines new jump labels to default location (-1) + { + printf("Loop with Multiple Conditions: %d\n", s); + s++; + } + + // Loop with multiple variables + int t, u; + for (t = 1, u = 10; t <= 5 && u >= 5; t++, u--) // CIL defines new jump labels to default location (-1) + { + printf("Loop with Multiple Variables: %d %d\n", t, u); + } +} \ No newline at end of file diff --git a/tests/regression/75-termination/40-complex-conditions.c b/tests/regression/75-termination/41-for-continue-terminating.c similarity index 52% rename from tests/regression/75-termination/40-complex-conditions.c rename to tests/regression/75-termination/41-for-continue-terminating.c index a74c863fb4..1d109c4ee7 100644 --- a/tests/regression/75-termination/40-complex-conditions.c +++ b/tests/regression/75-termination/41-for-continue-terminating.c @@ -3,38 +3,25 @@ int main() { - int i; - // Loop with a continue statement for (i = 1; i <= 10; i++) { if (i % 2 == 0) { - continue; + continue; // Converted to an goto to "for" in line 7 } printf("%d ", i); } printf("\n"); - // Loop with complex conditions - for (i = 1; i <= 10; i++) - { - if (i > 5 && i % 2 == 0) - { - printf("%d ", i); - } - } - printf("\n"); - // Loop with complex conditions - i = 1; - while (i <= 10) + // Loop with a continue statement + for (int r = 1; r <= 10; r++) { - if (i > 5 && i % 2 == 0) + if (r % 3 == 0) { - printf("%d ", i); + continue; // Converted to an goto to "for" in line 19 } - i++; + printf("Loop with Continue: %d\n", r); } - printf("\n"); } \ No newline at end of file diff --git a/tests/regression/75-termination/41-more-tests.c b/tests/regression/75-termination/41-more-tests.c deleted file mode 100644 index 1fb9f83f5d..0000000000 --- a/tests/regression/75-termination/41-more-tests.c +++ /dev/null @@ -1,31 +0,0 @@ -// TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra -#include - -int main() -{ - - // Loop with a continue statement - for (int r = 1; r <= 10; r++) - { - if (r % 3 == 0) - { - continue; - } - printf("Loop with Continue: %d\n", r); - } - - // Loop with multiple conditions - int s = 1; - while (s <= 10 && s % 2 == 0) - { - printf("Loop with Multiple Conditions: %d\n", s); - s++; - } - - // Loop with multiple variables - int t, u; - for (t = 1, u = 10; t <= 5 && u >= 5; t++, u--) - { - printf("Loop with Multiple Variables: %d %d\n", t, u); - } -} \ No newline at end of file diff --git a/tests/regression/75-termination/43-return-from-endless-loop-terminating.c b/tests/regression/75-termination/43-return-from-endless-loop-terminating.c new file mode 100644 index 0000000000..06bda24bd7 --- /dev/null +++ b/tests/regression/75-termination/43-return-from-endless-loop-terminating.c @@ -0,0 +1,14 @@ +// TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() { + int i = 1; + + while (i != 0) { + printf("%d\n", i); + i++; + if (i>10) { + return 0; + } + } +} From 76f540ffea9d19b7c9dc711047774900892c850d Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Thu, 13 Jul 2023 20:58:17 +0200 Subject: [PATCH 0310/1312] Test explanation && new test case termination 43 --- tests/regression/75-termination/41-for-continue-terminating.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression/75-termination/41-for-continue-terminating.c b/tests/regression/75-termination/41-for-continue-terminating.c index 1d109c4ee7..1d3b96fcf8 100644 --- a/tests/regression/75-termination/41-for-continue-terminating.c +++ b/tests/regression/75-termination/41-for-continue-terminating.c @@ -4,7 +4,7 @@ int main() { // Loop with a continue statement - for (i = 1; i <= 10; i++) + for (int i = 1; i <= 10; i++) { if (i % 2 == 0) { From 40ea15c5a2d3691bfa047701e09e45bcec161799 Mon Sep 17 00:00:00 2001 From: Isidor Zweckstetter Date: Thu, 13 Jul 2023 22:27:45 +0200 Subject: [PATCH 0311/1312] Added special function --Still work in progress --- src/analyses/libraryDesc.ml | 1 + src/util/terminationPreprocessing.ml | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/analyses/libraryDesc.ml b/src/analyses/libraryDesc.ml index 8b90553e95..0ac3e87f96 100644 --- a/src/analyses/libraryDesc.ml +++ b/src/analyses/libraryDesc.ml @@ -72,6 +72,7 @@ type special = | Identity of Cil.exp (** Identity function. Some compiler optimization annotation functions map to this. *) | Setjmp of { env: Cil.exp; } | Longjmp of { env: Cil.exp; value: Cil.exp; } + | Bounded of { var: Cil.exp} | Unknown (** Anything not belonging to other types. *) (* TODO: rename to Other? *) diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index 4043c6d256..1aa78ccae7 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -3,6 +3,19 @@ include Printf module VarToStmt = Map.Make(CilType.Varinfo) (* maps varinfos (= loop counter variable) to the statement of the corresponding loop*) +let specialFunction name = + print_endline @@ "specialfunction done"; + { svar = makeGlobalVar name (TFun(voidType, Some [("exp", intType, [])], false,[])); + smaxid = 0; + slocals = []; + sformals = []; + sbody = mkBlock []; + smaxstmtid = None; + sallstmts = []; + } + +let f_bounded = Lval (var (specialFunction "__goblint_bounded").svar) + let extract_file_name s = (*There still may be a need to filter more chars*) let ls = String.split_on_char '/' s in (*Assuming '/' as path seperator*) let ls = List.rev ls in @@ -35,11 +48,12 @@ class loopCounterVisitor lc lg le (fd : fundec) = object(self) let inc_stmt = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in let check_stmt = mkStmtOneInstr @@ Set ((var !le), (Lval (var v)), loc, eloc) in let inc_stmt2 = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in + let exit_stmt = mkStmtOneInstr @@ Call (None, f_bounded, [Lval (var v)], loc, locUnknown) in (match b.bstmts with | s :: ss -> (*duplicate increment statement here to fix inconsistencies in nested loops*) - b.bstmts <- inc_stmt :: check_stmt :: s :: inc_stmt2 :: ss; + b.bstmts <- inc_stmt :: check_stmt :: exit_stmt :: s :: inc_stmt2 :: ss; | ss -> - b.bstmts <- inc_stmt :: check_stmt :: ss; + b.bstmts <- inc_stmt :: check_stmt :: exit_stmt :: ss; ); lc := VarToStmt.add (v: varinfo) (s: stmt) !lc; let nb = mkBlock [init_stmt; mkStmt s.skind] in From 30692fc5d626c99c5b120502009e7e88c546d14e Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Thu, 13 Jul 2023 22:47:22 +0200 Subject: [PATCH 0312/1312] Test for code line detection --- scripts/update_suite.rb | 45 +++++++++++-------- src/analyses/loopTermination.ml | 6 +-- src/framework/constraints.ml | 2 +- .../02-simple-loop-nonterminating.c | 2 +- .../04-nested-loop-nonterminating.c | 2 +- .../06-for-loop-nonterminating.c | 2 +- .../08-nested-for-loop-nonterminating.c | 2 +- .../14-do-while-nonterminating.c | 2 +- ...16-nested-loop-nontrivial-nonterminating.c | 2 +- .../75-termination/17-goto-terminating.c | 3 +- .../75-termination/18-goto-nonterminating.c | 2 +- .../75-termination/20-rand-nonterminating.c | 4 +- .../21-no-exit-on-rand-unproofable.c | 2 +- .../22-exit-on-rand-unproofable.c | 2 +- .../23-exit-on-rand-terminating.c | 5 +-- .../24-upjumping-goto-loopless-terminating.c | 2 +- .../27-upjumping-goto-nonterminating.c | 4 +- .../29-do-while-continue-nonterminating.c | 2 +- ...31-goto-out-of-inner-loop-nonterminating.c | 4 +- .../34-nested-for-loop-nonterminating.c | 2 +- .../37-recursion-nonterminating.c | 2 +- .../39-recursion-nested-nonterminating.c | 4 +- 22 files changed, 56 insertions(+), 47 deletions(-) diff --git a/scripts/update_suite.rb b/scripts/update_suite.rb index 8841ecea88..60a7ec06be 100755 --- a/scripts/update_suite.rb +++ b/scripts/update_suite.rb @@ -149,24 +149,27 @@ def collect_warnings next unless l =~ /(.*)\(.*?\:(\d+)(?:\:\d+)?(?:-(?:\d+)(?:\:\d+)?)?\)/ obj,i = $1,$2.to_i - ranking = ["other", "warn", "term", "nonterm", "race", "norace", "deadlock", "nodeadlock", "success", "fail", "unknown"] + ranking = ["other", "warn", "goto", "fundec", "loop", "term", "nonterm", "race", "norace", "deadlock", "nodeadlock", "success", "fail", "unknown"] thiswarn = case obj - when /\(conf\. \d+\)/ then "race" - when /Deadlock/ then "deadlock" - when /lock (before|after):/ then "deadlock" - when /Assertion .* will fail/ then "fail" - when /Assertion .* will succeed/ then "success" - when /Assertion .* is unknown/ then "unknown" - when /invariant confirmed/ then "success" - when /invariant unconfirmed/ then "unknown" - when /invariant refuted/ then "fail" - when /^\[Warning\]/ then "warn" - when /^\[Error\]/ then "warn" - when /^\[Info\]/ then "warn" - when /^\[Success\]/ then "success" - when /\[Debug\]/ then next # debug "warnings" shouldn't count as other warnings (against NOWARN) - when /^ on line \d+ $/ then next # dead line warnings shouldn't count (used for unreachability with NOWARN) - when /^ on lines \d+..\d+ $/ then next # dead line warnings shouldn't count (used for unreachability with NOWARN) + when /\(conf\. \d+\)/ then "race" + when /Deadlock/ then "deadlock" + when /lock (before|after):/ then "deadlock" + when /Assertion .* will fail/ then "fail" + when /Assertion .* will succeed/ then "success" + when /Assertion .* is unknown/ then "unknown" + when /invariant confirmed/ then "success" + when /invariant unconfirmed/ then "unknown" + when /invariant refuted/ then "fail" + when /^\[Warning\]/ then "warn" + when /^\[Error\]/ then "warn" + when /^\[Info\]/ then "warn" + when /^\[Success\]/ then "success" + when /(Upjumping Goto)/ then "goto" + when /(Fundec \w+ is contained in a call graph cycle)/ then "fundec" + when /(Loop analysis)/ then "loop" + when /\[Debug\]/ then next # debug "warnings" shouldn't count as other warnings (against NOWARN) + when /^ on line \d+ $/ then next # dead line warnings shouldn't count (used for unreachability with NOWARN) + when /^ on lines \d+..\d+ $/ then next # dead line warnings shouldn't count (used for unreachability with NOWARN) else "other" end oldwarn = warnings[i] @@ -206,7 +209,7 @@ def compare_warnings end } case type - when "deadlock", "race", "fail", "unknown", "warn" + when "goto", "fundec", "loop", "deadlock", "race", "fail", "unknown", "warn" check.call warnings[idx] == type when "nonterm" check.call warnings[idx] == type @@ -309,6 +312,12 @@ def parse_tests (lines) tests[i] = "success" elsif obj =~ /FAIL/ then tests[i] = "fail" + elsif obj =~ /NONTERMLOOP/ then + tests[i] = "loop" + elsif obj =~ /NONTERMGOTO/ then + tests[i] = "goto" + elsif obj =~ /NONTERMFUNDEC/ then + tests[i] = "fundec" elsif obj =~ /UNKNOWN/ then tests[i] = "unknown" elsif obj =~ /(assert|__goblint_check).*\(/ then diff --git a/src/analyses/loopTermination.ml b/src/analyses/loopTermination.ml index 367c3a328c..44caf5c90a 100644 --- a/src/analyses/loopTermination.ml +++ b/src/analyses/loopTermination.ml @@ -70,13 +70,13 @@ struct List.iter (fun x -> let msgs = - [(Pretty.dprintf "The program might not terminate! (Upjumping Goto)\n", Some (M.Location.CilLocation x));] in + [(Pretty.dprintf "The program might not terminate! (Upjumping Goto)", Some (M.Location.CilLocation x));] in M.msg_group Warning ~category:NonTerminating "Possibly non terminating loops" msgs) (!upjumping_gotos) ); (* multithreaded *) if not (!single_thread) then ( - M.warn ~category:NonTerminating "The program might not terminate! (Multithreaded)\n" + M.warn ~category:NonTerminating "The program might not terminate! (Multithreaded)" ) @@ -93,7 +93,7 @@ struct (* In case the loop is not bounded, a warning is created*) if not (is_bounded) then ( let msgs = - [(Pretty.dprintf "The program might not terminate! (Loop analysis)\n", Some (M.Location.CilLocation (Cilfacade.get_stmtLoc loop_statement)));] in + [(Pretty.dprintf "The program might not terminate! (Loop analysis)", Some (M.Location.CilLocation (Cilfacade.get_stmtLoc loop_statement)));] in M.msg_group Warning ~category:NonTerminating "Possibly non terminating loops" msgs); () | _ -> () diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 6114f30adb..3d10248885 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1797,7 +1797,7 @@ struct (*Cycle found*) let msgs = [ - (Pretty.dprintf "The program might not terminate! (Fundec %a is contained in a call graph cycle)\n" CilType.Fundec.pretty fundec_e, Some (M.Location.CilLocation fundec_e.svar.vdecl)); + (Pretty.dprintf "The program might not terminate! (Fundec %a is contained in a call graph cycle)" CilType.Fundec.pretty fundec_e, Some (M.Location.CilLocation fundec_e.svar.vdecl)); ] in M.msg_group Warning ~category:NonTerminating "Recursion cycle" msgs) (* output a warning for non-termination*) else if not (LH.mem global_visited_calls call) then begin diff --git a/tests/regression/75-termination/02-simple-loop-nonterminating.c b/tests/regression/75-termination/02-simple-loop-nonterminating.c index 51fb340f3b..c6e1c6c8d6 100644 --- a/tests/regression/75-termination/02-simple-loop-nonterminating.c +++ b/tests/regression/75-termination/02-simple-loop-nonterminating.c @@ -3,7 +3,7 @@ int main() { - while (1) + while (1) // NONTERMLOOP termination analysis shall mark beginning of while as non-terminating loop { continue; } diff --git a/tests/regression/75-termination/04-nested-loop-nonterminating.c b/tests/regression/75-termination/04-nested-loop-nonterminating.c index fffc932f36..21a6d47051 100644 --- a/tests/regression/75-termination/04-nested-loop-nonterminating.c +++ b/tests/regression/75-termination/04-nested-loop-nonterminating.c @@ -9,7 +9,7 @@ int main() { int innerCount = 1; - while (1) + while (1) // NONTERMLOOP termination analysis shall mark beginning of while as non-terminating loop { printf("(%d, %d) ", outerCount, innerCount); innerCount++; diff --git a/tests/regression/75-termination/06-for-loop-nonterminating.c b/tests/regression/75-termination/06-for-loop-nonterminating.c index be876c9741..0f96209e35 100644 --- a/tests/regression/75-termination/06-for-loop-nonterminating.c +++ b/tests/regression/75-termination/06-for-loop-nonterminating.c @@ -3,7 +3,7 @@ int main() { - for (;;) + for (;;) // NONTERMLOOP termination analysis shall mark beginning of for as non-terminating loop { printf("This loop does not terminate.\n"); } diff --git a/tests/regression/75-termination/08-nested-for-loop-nonterminating.c b/tests/regression/75-termination/08-nested-for-loop-nonterminating.c index e360d45d0a..ec76f31534 100644 --- a/tests/regression/75-termination/08-nested-for-loop-nonterminating.c +++ b/tests/regression/75-termination/08-nested-for-loop-nonterminating.c @@ -7,7 +7,7 @@ int main() for (outerCount = 1; outerCount <= 3; outerCount++) { - for (innerCount = 1;; innerCount++) + for (innerCount = 1;; innerCount++) // NONTERMLOOP termination analysis shall mark beginning of for as non-terminating loop { printf("(%d, %d) ", outerCount, innerCount); } diff --git a/tests/regression/75-termination/14-do-while-nonterminating.c b/tests/regression/75-termination/14-do-while-nonterminating.c index 30c8349bb5..5522b61d88 100644 --- a/tests/regression/75-termination/14-do-while-nonterminating.c +++ b/tests/regression/75-termination/14-do-while-nonterminating.c @@ -9,7 +9,7 @@ int main() { printf("Inside the do-while loop\n"); i++; - } while (i >= 2); + } while (i >= 2); // NONTERMLOOP termination analysis shall mark while as non-terminating loop printf("Exited the loop\n"); return 0; diff --git a/tests/regression/75-termination/16-nested-loop-nontrivial-nonterminating.c b/tests/regression/75-termination/16-nested-loop-nontrivial-nonterminating.c index 87b4b82ed9..bded788a90 100644 --- a/tests/regression/75-termination/16-nested-loop-nontrivial-nonterminating.c +++ b/tests/regression/75-termination/16-nested-loop-nontrivial-nonterminating.c @@ -9,7 +9,7 @@ int main() { int innerCount = 1; - while (outerCount < 3 || innerCount > 0) + while (outerCount < 3 || innerCount > 0) // NONTERMLOOP termination analysis shall mark beginning of while as non-terminating loop { printf("(%d, %d) ", outerCount, innerCount); innerCount++; diff --git a/tests/regression/75-termination/17-goto-terminating.c b/tests/regression/75-termination/17-goto-terminating.c index 45e92f32e8..941db0c601 100644 --- a/tests/regression/75-termination/17-goto-terminating.c +++ b/tests/regression/75-termination/17-goto-terminating.c @@ -12,7 +12,8 @@ int main() if (num <= 10) { - goto loop; // We are not able to detect up-jumping gotos as terminating, we + goto loop; // NONTERMGOTO termination analysis shall mark goto statement up-jumping goto + // We are not able to detect up-jumping gotos as terminating, we // just warn about them might being nonterminating. } diff --git a/tests/regression/75-termination/18-goto-nonterminating.c b/tests/regression/75-termination/18-goto-nonterminating.c index 25f79e5b57..f88088ad12 100644 --- a/tests/regression/75-termination/18-goto-nonterminating.c +++ b/tests/regression/75-termination/18-goto-nonterminating.c @@ -9,7 +9,7 @@ int main() printf("Current number: %d\n", num); num++; - goto loop; + goto loop; // NONTERMGOTO termination analysis shall mark goto statement up-jumping goto return 0; } diff --git a/tests/regression/75-termination/20-rand-nonterminating.c b/tests/regression/75-termination/20-rand-nonterminating.c index 83630ed6c4..394bce7709 100644 --- a/tests/regression/75-termination/20-rand-nonterminating.c +++ b/tests/regression/75-termination/20-rand-nonterminating.c @@ -11,7 +11,7 @@ int main() if (rand()) { // Loop inside the if part - for (int i = 1; i >= 0; i++) + for (int i = 1; i >= 0; i++) // NONTERMLOOP termination analysis shall mark beginning of while as non-terminating loop { printf("Loop inside if part: %d\n", i); } @@ -20,7 +20,7 @@ int main() { // Loop inside the else part int j = 1; - while (j > 0) + while (j > 0) // NONTERMLOOP termination analysis shall mark beginning of while as non-terminating loop { printf("Loop inside else part: %d\n", j); } diff --git a/tests/regression/75-termination/21-no-exit-on-rand-unproofable.c b/tests/regression/75-termination/21-no-exit-on-rand-unproofable.c index 3e7a65dfd4..902ef2a4e2 100644 --- a/tests/regression/75-termination/21-no-exit-on-rand-unproofable.c +++ b/tests/regression/75-termination/21-no-exit-on-rand-unproofable.c @@ -6,7 +6,7 @@ int main() int forever, i = 0; // This loop is not provable, therefore it should throw a warning - while (i < 4 || forever == 1) + while (i < 4 || forever == 1) // NONTERMLOOP termination analysis shall mark beginning of while as non-terminating loop { i++; if (i == 4) diff --git a/tests/regression/75-termination/22-exit-on-rand-unproofable.c b/tests/regression/75-termination/22-exit-on-rand-unproofable.c index b8d7992bd9..f14f7d4e3f 100644 --- a/tests/regression/75-termination/22-exit-on-rand-unproofable.c +++ b/tests/regression/75-termination/22-exit-on-rand-unproofable.c @@ -6,7 +6,7 @@ int main() int forever = 1; // This loop is not provable, therefore it should throw a warning - while (forever == 1) + while (forever == 1) // NONTERMLOOP termination analysis shall mark beginning of while as non-terminating loop { if (rand()) // May exit, may not { diff --git a/tests/regression/75-termination/23-exit-on-rand-terminating.c b/tests/regression/75-termination/23-exit-on-rand-terminating.c index 24d4980406..013aff2dd5 100644 --- a/tests/regression/75-termination/23-exit-on-rand-terminating.c +++ b/tests/regression/75-termination/23-exit-on-rand-terminating.c @@ -5,9 +5,8 @@ int main() { int short_run, i = 0; - - while (i < 90 && - short_run != 1) // Currently not able to detect this as terminating + // Currently not able to detect this as terminating due to multiple conditions + while (i < 90 && short_run != 1) { i++; if (rand()) diff --git a/tests/regression/75-termination/24-upjumping-goto-loopless-terminating.c b/tests/regression/75-termination/24-upjumping-goto-loopless-terminating.c index 3dbff9d7ea..3f4e115445 100644 --- a/tests/regression/75-termination/24-upjumping-goto-loopless-terminating.c +++ b/tests/regression/75-termination/24-upjumping-goto-loopless-terminating.c @@ -12,7 +12,7 @@ int main() mark2: printf("This is mark2\n"); - goto mark1; + goto mark1; // NONTERMGOTO termination analysis shall mark goto statement up-jumping goto mark3: printf("This is mark3\n"); diff --git a/tests/regression/75-termination/27-upjumping-goto-nonterminating.c b/tests/regression/75-termination/27-upjumping-goto-nonterminating.c index e27d7161d5..5ce295872c 100644 --- a/tests/regression/75-termination/27-upjumping-goto-nonterminating.c +++ b/tests/regression/75-termination/27-upjumping-goto-nonterminating.c @@ -11,11 +11,11 @@ int main() mark2: printf("This is mark2\n"); - goto mark1; + goto mark1; // NONTERMGOTO termination analysis shall mark goto statement up-jumping goto mark3: printf("This is mark3\n"); - goto mark1; + goto mark1; // NONTERMGOTO termination analysis shall mark goto statement up-jumping goto return 0; } diff --git a/tests/regression/75-termination/29-do-while-continue-nonterminating.c b/tests/regression/75-termination/29-do-while-continue-nonterminating.c index 41f1dbd5bc..34766ab2e7 100644 --- a/tests/regression/75-termination/29-do-while-continue-nonterminating.c +++ b/tests/regression/75-termination/29-do-while-continue-nonterminating.c @@ -15,7 +15,7 @@ int main() printf("Continue as %i is odd\n", i); continue; } - } while (i >= 2); + } while (i >= 2); // NONTERMLOOP termination analysis shall mark beginning of while as non-terminating loop printf("Exited the loop\n"); return 0; diff --git a/tests/regression/75-termination/31-goto-out-of-inner-loop-nonterminating.c b/tests/regression/75-termination/31-goto-out-of-inner-loop-nonterminating.c index cb54b5dd2f..d7ff329396 100644 --- a/tests/regression/75-termination/31-goto-out-of-inner-loop-nonterminating.c +++ b/tests/regression/75-termination/31-goto-out-of-inner-loop-nonterminating.c @@ -7,7 +7,7 @@ int main() int columns = 5; // Outer loop for rows - for (int i = 1; 1; i++) + for (int i = 1; 1; i++) // NONTERMLOOP termination analysis shall mark beginning of for as non-terminating loop { // Inner loop for columns for (int j = 1; j <= columns; j++) @@ -15,7 +15,7 @@ int main() if (j == 3) { printf("Goto as continue for outer loop\n"); - goto outer_loop; // Jump to the label "outer_loop" + goto outer_loop; } printf("(%d, %d) ", i, j); } diff --git a/tests/regression/75-termination/34-nested-for-loop-nonterminating.c b/tests/regression/75-termination/34-nested-for-loop-nonterminating.c index 709960640f..24605ad478 100644 --- a/tests/regression/75-termination/34-nested-for-loop-nonterminating.c +++ b/tests/regression/75-termination/34-nested-for-loop-nonterminating.c @@ -7,7 +7,7 @@ int main() for (outerCount = 1; outerCount <= 3; outerCount++) { - for (innerCount = 1; innerCount > 0; innerCount++) + for (innerCount = 1; innerCount > 0; innerCount++) // NONTERMLOOP termination analysis shall mark beginning of for as non-terminating loop { printf("(%d, %d) ", outerCount, innerCount); } diff --git a/tests/regression/75-termination/37-recursion-nonterminating.c b/tests/regression/75-termination/37-recursion-nonterminating.c index 38aaf3de85..21316de9bd 100644 --- a/tests/regression/75-termination/37-recursion-nonterminating.c +++ b/tests/regression/75-termination/37-recursion-nonterminating.c @@ -1,7 +1,7 @@ // NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --enable ana.context.widen #include -void recursiveFunction(int n) +void recursiveFunction(int n) // NONTERMFUNDEC termination analysis shall mark fundec of non-terminating function { // Base case: When n reaches 0, stop recursion if (n == 30) diff --git a/tests/regression/75-termination/39-recursion-nested-nonterminating.c b/tests/regression/75-termination/39-recursion-nested-nonterminating.c index 8311d9f573..b3aa8bf21b 100644 --- a/tests/regression/75-termination/39-recursion-nested-nonterminating.c +++ b/tests/regression/75-termination/39-recursion-nested-nonterminating.c @@ -1,7 +1,7 @@ // NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -void innerRecursiveFunction() +void innerRecursiveFunction() // TODO NONTERMFUNDEC termination analysis shall mark fundec of non-terminating function { printf("Nested recursive call\n"); @@ -9,7 +9,7 @@ void innerRecursiveFunction() innerRecursiveFunction(); } -void outerRecursiveFunction() +void outerRecursiveFunction() // NONTERMFUNDEC termination analysis shall mark fundec of non-terminating function { printf("Outer recursive call\n"); From dc0a284ccc4c6337db5458882345a6aa126cf3bc Mon Sep 17 00:00:00 2001 From: Isidor Zweckstetter Date: Thu, 13 Jul 2023 23:42:13 +0200 Subject: [PATCH 0313/1312] changed __goblint_bounded constructor from Assert to Bounded --- src/analyses/libraryDesc.ml | 2 +- src/analyses/libraryFunctions.ml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/analyses/libraryDesc.ml b/src/analyses/libraryDesc.ml index 0ac3e87f96..3896e74574 100644 --- a/src/analyses/libraryDesc.ml +++ b/src/analyses/libraryDesc.ml @@ -72,7 +72,7 @@ type special = | Identity of Cil.exp (** Identity function. Some compiler optimization annotation functions map to this. *) | Setjmp of { env: Cil.exp; } | Longjmp of { env: Cil.exp; value: Cil.exp; } - | Bounded of { var: Cil.exp} + | Bounded of { exp: Cil.exp} | Unknown (** Anything not belonging to other types. *) (* TODO: rename to Other? *) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 5d65cf9cb3..045268ca4d 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -303,7 +303,7 @@ let goblint_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__goblint_assert", special [__ "exp" []] @@ fun exp -> Assert { exp; check = true; refine = get_bool "sem.assert.refine" }); ("__goblint_split_begin", unknown [drop "exp" []]); ("__goblint_split_end", unknown [drop "exp" []]); - ("__goblint_bounded", special [__ "exp"[]] @@ fun exp -> Assert { exp; check = true; refine = false }); + ("__goblint_bounded", special [__ "exp"[]] @@ fun exp -> Bounded { exp }); ] (** zstd functions. From 1f499e99f91a5345105fd873f08fc00758336e5d Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Fri, 14 Jul 2023 14:31:05 +0200 Subject: [PATCH 0314/1312] Use special function instead of variable indicator We now use __goblint_bounded to mark the place where the value of the loop counter variable shall be checked for being bounded. Before, we used a variable called loop exit indicator for that. --- src/analyses/loopTermination.ml | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/analyses/loopTermination.ml b/src/analyses/loopTermination.ml index 367c3a328c..307e76211d 100644 --- a/src/analyses/loopTermination.ml +++ b/src/analyses/loopTermination.ml @@ -4,6 +4,7 @@ open Analyses open GoblintCil open TerminationPreprocessing +(* TODO: Remove *) exception PreProcessing of string (** Stores the result of the query if the program is single threaded or not @@ -40,7 +41,7 @@ let check_bounded ctx varinfo = (** We want to record termination information of loops and use the loop * statements for that. We use this lifting because we need to have a * lattice. *) -module Statements = Lattice.Flat (CilType.Stmt) (Printable.DefaultNames) (* TODO: Use Basetype.CilStmt instead? *) +module Statements = Lattice.Flat (CilType.Stmt) (Printable.DefaultNames) (** The termination analysis considering loops and gotos *) module Spec : Analyses.MCPSpec = @@ -79,7 +80,7 @@ struct M.warn ~category:NonTerminating "The program might not terminate! (Multithreaded)\n" ) - + (* let assign ctx (lval : lval) (rval : exp) = if !AnalysisState.postsolving then (* Detect assignment to loop counter variable *) @@ -98,21 +99,23 @@ struct () | _ -> () else () + *) - (* let special ctx (lval : lval option) (f : varinfo) (arglist : exp list) = - (* TODO: Implement check for our special loop exit indicator function *) if !AnalysisState.postsolving then match f.vname, arglist with "__goblint_bounded", [Lval (Var x, NoOffset)] -> - let () = print_endline "schpecial" in let is_bounded = check_bounded ctx x in let loop_statement = VarToStmt.find x !loop_counters in ctx.sideg () (G.add (`Lifted loop_statement) is_bounded (ctx.global ())); + (* In case the loop is not bounded, a warning is created*) + if not (is_bounded) then ( + let msgs = + [(Pretty.dprintf "The program might not terminate! (Loop analysis)\n", Some (M.Location.CilLocation (Cilfacade.get_stmtLoc loop_statement)));] in + M.msg_group Warning ~category:NonTerminating "Possibly non terminating loops" msgs); () | _ -> () else () - *) (** Checks whether a new thread was spawned some time. We want to discard * any knowledge about termination then (see query function) *) From b9a5b3fdda1cc2a216937206cd60d89d6a6849a0 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Fri, 14 Jul 2023 14:44:28 +0200 Subject: [PATCH 0315/1312] Clean up, make things look nicer --- src/analyses/loopTermination.ml | 42 ++++++++++----------------------- 1 file changed, 12 insertions(+), 30 deletions(-) diff --git a/src/analyses/loopTermination.ml b/src/analyses/loopTermination.ml index 307e76211d..beb1cbfd06 100644 --- a/src/analyses/loopTermination.ml +++ b/src/analyses/loopTermination.ml @@ -23,9 +23,6 @@ let loop_exit : varinfo ref = ref (makeVarinfo false "-error" Cil.intType) let is_loop_counter_var (x : varinfo) = VarToStmt.mem x !loop_counters -let is_loop_exit_indicator (x : varinfo) = - x = !loop_exit - let no_upjumping_gotos () = upjumping_gotos.contents = [] @@ -65,42 +62,24 @@ struct let exitstate = startstate let finalize () = - (* warning for detected possible non-termination *) - (*upjumping gotos *) + (* Warning for detected possible non-termination *) + (* Upjumping gotos *) if not (no_upjumping_gotos ()) then ( List.iter (fun x -> let msgs = - [(Pretty.dprintf "The program might not terminate! (Upjumping Goto)\n", Some (M.Location.CilLocation x));] in + [(Pretty.dprintf + "The program might not terminate! (Upjumping Goto)\n", + Some (M.Location.CilLocation x) + );] in M.msg_group Warning ~category:NonTerminating "Possibly non terminating loops" msgs) (!upjumping_gotos) ); - (* multithreaded *) + (* Multithreaded *) if not (!single_thread) then ( M.warn ~category:NonTerminating "The program might not terminate! (Multithreaded)\n" ) - (* - let assign ctx (lval : lval) (rval : exp) = - if !AnalysisState.postsolving then - (* Detect assignment to loop counter variable *) - match lval, rval with - (Var y, NoOffset), Lval (Var x, NoOffset) when is_loop_exit_indicator y -> - (* Loop exit: Check whether loop counter variable is bounded *) - (* TODO: Move to special *) - let is_bounded = check_bounded ctx x in - let loop_statement = VarToStmt.find x !loop_counters in - ctx.sideg () (G.add (`Lifted loop_statement) is_bounded (ctx.global ())); - (* In case the loop is not bounded, a warning is created*) - if not (is_bounded) then ( - let msgs = - [(Pretty.dprintf "The program might not terminate! (Loop analysis)\n", Some (M.Location.CilLocation (Cilfacade.get_stmtLoc loop_statement)));] in - M.msg_group Warning ~category:NonTerminating "Possibly non terminating loops" msgs); - () - | _ -> () - else () - *) - let special ctx (lval : lval option) (f : varinfo) (arglist : exp list) = if !AnalysisState.postsolving then match f.vname, arglist with @@ -108,10 +87,13 @@ struct let is_bounded = check_bounded ctx x in let loop_statement = VarToStmt.find x !loop_counters in ctx.sideg () (G.add (`Lifted loop_statement) is_bounded (ctx.global ())); - (* In case the loop is not bounded, a warning is created*) + (* In case the loop is not bounded, a warning is created. *) if not (is_bounded) then ( let msgs = - [(Pretty.dprintf "The program might not terminate! (Loop analysis)\n", Some (M.Location.CilLocation (Cilfacade.get_stmtLoc loop_statement)));] in + [(Pretty.dprintf + "The program might not terminate! (Loop analysis)\n", + Some (M.Location.CilLocation (Cilfacade.get_stmtLoc loop_statement)) + );] in M.msg_group Warning ~category:NonTerminating "Possibly non terminating loops" msgs); () | _ -> () From 6146c760cbf6279e7055448820ab4f5c787ecc4a Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Fri, 14 Jul 2023 14:54:19 +0200 Subject: [PATCH 0316/1312] Wrap always_single_threaded in let-in clause --- src/analyses/loopTermination.ml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/analyses/loopTermination.ml b/src/analyses/loopTermination.ml index beb1cbfd06..0211cc9cb5 100644 --- a/src/analyses/loopTermination.ml +++ b/src/analyses/loopTermination.ml @@ -115,10 +115,12 @@ struct Some b -> b | None -> false) | Queries.MustTermAllLoops -> + let always_single_threaded = must_be_single_threaded_since_start ctx in (* Must be the first to be evaluated! This has the side effect that * single_thread is set. In case of another order and due to lazy - * evaluation the correct value of single_thread can not be guaranteed! *) - must_be_single_threaded_since_start ctx + * evaluation, the correct value of single_thread can not be guaranteed! + * Therefore, we use a let-in clause here. *) + always_single_threaded && no_upjumping_gotos () && G.for_all (fun _ term_info -> term_info) (ctx.global ()) | _ -> Queries.Result.top q From 847ea368bc746551371286a36bb4c3263294f962 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Fri, 14 Jul 2023 15:55:43 +0200 Subject: [PATCH 0317/1312] Remove PreProcessing exception --- src/analyses/loopTermination.ml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/analyses/loopTermination.ml b/src/analyses/loopTermination.ml index 0211cc9cb5..942d38c0f3 100644 --- a/src/analyses/loopTermination.ml +++ b/src/analyses/loopTermination.ml @@ -4,9 +4,6 @@ open Analyses open GoblintCil open TerminationPreprocessing -(* TODO: Remove *) -exception PreProcessing of string - (** Stores the result of the query if the program is single threaded or not since finalize does not has ctx as an argument*) let single_thread : bool ref = ref false @@ -33,7 +30,7 @@ let check_bounded ctx varinfo = match ctx.ask (EvalInt exp) with | `Top -> false | `Lifted v -> not (is_top_of (ikind v) v) - | `Bot -> raise (PreProcessing "Loop variable is Bot") + | `Bot -> failwith "Loop counter variable is Bot." (** We want to record termination information of loops and use the loop * statements for that. We use this lifting because we need to have a From 0a9c5d432888750cf0d860a6568fcb887cedc12f Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Sat, 15 Jul 2023 14:54:57 +0200 Subject: [PATCH 0318/1312] Fix indentation --- src/analyses/base.ml | 578 +++++++++++++++++++++---------------------- 1 file changed, 289 insertions(+), 289 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 3a231ea396..fa45a7c38d 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2064,307 +2064,307 @@ struct s1_a, s1_typ, VD.top_value (unrollType s1_typ) in let st = match desc.special args, f.vname with - | Memset { dest; ch; count; }, _ -> - (* TODO: check count *) - let eval_ch = eval_rv (Analyses.ask_of_ctx ctx) gs st ch in - let dest_a, dest_typ = addr_type_of_exp dest in - let value = - match eval_ch with - | Int i when ID.to_int i = Some Z.zero -> - VD.zero_init_value dest_typ - | _ -> - VD.top_value dest_typ - in - set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value - | Bzero { dest; count; }, _ -> - (* TODO: share something with memset special case? *) - (* TODO: check count *) - let dest_a, dest_typ = addr_type_of_exp dest in - let value = VD.zero_init_value dest_typ in - set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value - | Memcpy { dest = dst; src }, _ -> - memory_copying dst src - (* strcpy(dest, src); *) - | Strcpy { dest = dst; src; n = None }, _ -> - let dest_a, dest_typ = addr_type_of_exp dst in - (* when dest surely isn't a string literal, try copying src to dest *) - if AD.string_writing_defined dest_a then - memory_copying dst src - else - (* else return top (after a warning was issued) *) - set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ (VD.top_value (unrollType dest_typ)) - (* strncpy(dest, src, n); *) - | Strcpy { dest = dst; src; n }, _ -> - begin match eval_n n with - | Some num -> - let dest_a, dest_typ, value = string_manipulation dst src None false None in - set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value - | None -> failwith "already handled in case above" - end - | Strcat { dest = dst; src; n }, _ -> - let dest_a, dest_typ, value = string_manipulation dst src None false None in - set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value - | Strlen s, _ -> - begin match lv with - | Some lv_val -> - let dest_a = eval_lv (Analyses.ask_of_ctx ctx) gs st lv_val in - let dest_typ = Cilfacade.typeOfLval lv_val in - let lval = mkMem ~addr:(Cil.stripCasts s) ~off:NoOffset in - let address = eval_lv (Analyses.ask_of_ctx ctx) gs st lval in - let (value:value) = Int(AD.to_string_length address) in - set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value - | None -> st - end - | Strstr { haystack; needle }, _ -> - begin match lv with - | Some _ -> - (* when haystack, needle and dest type coincide, check if needle is a substring of haystack: - if that is the case, assign the substring of haystack starting at the first occurrence of needle to dest, - else use top *) - let dest_a, dest_typ, value = string_manipulation haystack needle lv true (Some (fun h_a n_a -> Address(AD.substring_extraction h_a n_a))) in - set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value - | None -> st - end - | Strcmp { s1; s2; n }, _ -> - begin match lv with - | Some _ -> - (* when s1 and s2 type coincide, compare both both strings completely or their first n characters, otherwise use top *) - let dest_a, dest_typ, value = string_manipulation s1 s2 lv false (Some (fun s1_a s2_a -> Int(AD.string_comparison s1_a s2_a (eval_n n)))) in - set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value - | None -> st - end - | Abort, _ -> raise Deadcode - | ThreadExit { ret_val = exp }, _ -> - begin match ThreadId.get_current (Analyses.ask_of_ctx ctx) with - | `Lifted tid -> - ( - let rv = eval_rv (Analyses.ask_of_ctx ctx) ctx.global ctx.local exp in - ctx.sideg (V.thread tid) (G.create_thread rv); - (* TODO: emit thread return event so other analyses are aware? *) - (* TODO: publish still needed? *) - publish_all ctx `Return; (* like normal return *) - match ThreadId.get_current (Analyses.ask_of_ctx ctx) with - | `Lifted tid when ThreadReturn.is_current (Analyses.ask_of_ctx ctx) -> - ignore @@ Priv.thread_return (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) (priv_sideg ctx.sideg) tid st - | _ -> ()) - | _ -> () - end; - raise Deadcode - | MutexAttrSetType {attr = attr; typ = mtyp}, _ -> - begin - let get_type lval = - let address = eval_lv (Analyses.ask_of_ctx ctx) gs st lval in - AD.type_of address + | Memset { dest; ch; count; }, _ -> + (* TODO: check count *) + let eval_ch = eval_rv (Analyses.ask_of_ctx ctx) gs st ch in + let dest_a, dest_typ = addr_type_of_exp dest in + let value = + match eval_ch with + | Int i when ID.to_int i = Some Z.zero -> + VD.zero_init_value dest_typ + | _ -> + VD.top_value dest_typ in - let dst_lval = mkMem ~addr:(Cil.stripCasts attr) ~off:NoOffset in - let dest_typ = get_type dst_lval in - let dest_a = eval_lv (Analyses.ask_of_ctx ctx) gs st dst_lval in - match eval_rv (Analyses.ask_of_ctx ctx) gs st mtyp with - | Int x -> - begin - match ID.to_int x with - | Some z -> - if M.tracing then M.tracel "attr" "setting\n"; - set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ (MutexAttr (ValueDomain.MutexAttr.of_int z)) - | None -> set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ (MutexAttr (ValueDomain.MutexAttr.top ())) - end - | _ -> set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ (MutexAttr (ValueDomain.MutexAttr.top ())) - end - | Identity e, _ -> - begin match lv with - | Some x -> assign ctx x e - | None -> ctx.local - end - (**Floating point classification and trigonometric functions defined in c99*) - | Math { fun_args; }, _ -> - let apply_unary fk float_fun x = - let eval_x = eval_rv (Analyses.ask_of_ctx ctx) gs st x in - begin match eval_x with - | Float float_x -> float_fun (FD.cast_to fk float_x) - | _ -> failwith ("non-floating-point argument in call to function "^f.vname) + set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value + | Bzero { dest; count; }, _ -> + (* TODO: share something with memset special case? *) + (* TODO: check count *) + let dest_a, dest_typ = addr_type_of_exp dest in + let value = VD.zero_init_value dest_typ in + set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value + | Memcpy { dest = dst; src }, _ -> + memory_copying dst src + (* strcpy(dest, src); *) + | Strcpy { dest = dst; src; n = None }, _ -> + let dest_a, dest_typ = addr_type_of_exp dst in + (* when dest surely isn't a string literal, try copying src to dest *) + if AD.string_writing_defined dest_a then + memory_copying dst src + else + (* else return top (after a warning was issued) *) + set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ (VD.top_value (unrollType dest_typ)) + (* strncpy(dest, src, n); *) + | Strcpy { dest = dst; src; n }, _ -> + begin match eval_n n with + | Some num -> + let dest_a, dest_typ, value = string_manipulation dst src None false None in + set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value + | None -> failwith "already handled in case above" end - in - let apply_binary fk float_fun x y = - let eval_x = eval_rv (Analyses.ask_of_ctx ctx) gs st x in - let eval_y = eval_rv (Analyses.ask_of_ctx ctx) gs st y in - begin match eval_x, eval_y with - | Float float_x, Float float_y -> float_fun (FD.cast_to fk float_x) (FD.cast_to fk float_y) - | _ -> failwith ("non-floating-point argument in call to function "^f.vname) + | Strcat { dest = dst; src; n }, _ -> + let dest_a, dest_typ, value = string_manipulation dst src None false None in + set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value + | Strlen s, _ -> + begin match lv with + | Some lv_val -> + let dest_a = eval_lv (Analyses.ask_of_ctx ctx) gs st lv_val in + let dest_typ = Cilfacade.typeOfLval lv_val in + let lval = mkMem ~addr:(Cil.stripCasts s) ~off:NoOffset in + let address = eval_lv (Analyses.ask_of_ctx ctx) gs st lval in + let (value:value) = Int(AD.to_string_length address) in + set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value + | None -> st end - in - let result:value = - begin match fun_args with - | Nan (fk, str) when Cil.isPointerType (Cilfacade.typeOf str) -> Float (FD.nan_of fk) - | Nan _ -> failwith ("non-pointer argument in call to function "^f.vname) - | Inf fk -> Float (FD.inf_of fk) - | Isfinite x -> Int (ID.cast_to IInt (apply_unary FDouble FD.isfinite x)) - | Isinf x -> Int (ID.cast_to IInt (apply_unary FDouble FD.isinf x)) - | Isnan x -> Int (ID.cast_to IInt (apply_unary FDouble FD.isnan x)) - | Isnormal x -> Int (ID.cast_to IInt (apply_unary FDouble FD.isnormal x)) - | Signbit x -> Int (ID.cast_to IInt (apply_unary FDouble FD.signbit x)) - | Ceil (fk,x) -> Float (apply_unary fk FD.ceil x) - | Floor (fk,x) -> Float (apply_unary fk FD.floor x) - | Fabs (fk, x) -> Float (apply_unary fk FD.fabs x) - | Acos (fk, x) -> Float (apply_unary fk FD.acos x) - | Asin (fk, x) -> Float (apply_unary fk FD.asin x) - | Atan (fk, x) -> Float (apply_unary fk FD.atan x) - | Atan2 (fk, y, x) -> Float (apply_binary fk (fun y' x' -> FD.atan (FD.div y' x')) y x) - | Cos (fk, x) -> Float (apply_unary fk FD.cos x) - | Sin (fk, x) -> Float (apply_unary fk FD.sin x) - | Tan (fk, x) -> Float (apply_unary fk FD.tan x) - | Isgreater (x,y) -> Int(ID.cast_to IInt (apply_binary FDouble FD.gt x y)) - | Isgreaterequal (x,y) -> Int(ID.cast_to IInt (apply_binary FDouble FD.ge x y)) - | Isless (x,y) -> Int(ID.cast_to IInt (apply_binary FDouble FD.lt x y)) - | Islessequal (x,y) -> Int(ID.cast_to IInt (apply_binary FDouble FD.le x y)) - | Islessgreater (x,y) -> Int(ID.logor (ID.cast_to IInt (apply_binary FDouble FD.lt x y)) (ID.cast_to IInt (apply_binary FDouble FD.gt x y))) - | Isunordered (x,y) -> Int(ID.cast_to IInt (apply_binary FDouble FD.unordered x y)) - | Fmax (fd, x ,y) -> Float (apply_binary fd FD.fmax x y) - | Fmin (fd, x ,y) -> Float (apply_binary fd FD.fmin x y) + | Strstr { haystack; needle }, _ -> + begin match lv with + | Some _ -> + (* when haystack, needle and dest type coincide, check if needle is a substring of haystack: + if that is the case, assign the substring of haystack starting at the first occurrence of needle to dest, + else use top *) + let dest_a, dest_typ, value = string_manipulation haystack needle lv true (Some (fun h_a n_a -> Address(AD.substring_extraction h_a n_a))) in + set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value + | None -> st end - in - begin match lv with - | Some lv_val -> set ~ctx (Analyses.ask_of_ctx ctx) gs st (eval_lv (Analyses.ask_of_ctx ctx) ctx.global st lv_val) (Cilfacade.typeOfLval lv_val) result - | None -> st - end - (* handling thread creations *) - | ThreadCreate _, _ -> - invalidate_ret_lv ctx.local (* actual results joined via threadspawn *) - (* handling thread joins... sort of *) - | ThreadJoin { thread = id; ret_var }, _ -> - let st' = - match (eval_rv (Analyses.ask_of_ctx ctx) gs st ret_var) with - | Int n when GobOption.exists (BI.equal BI.zero) (ID.to_int n) -> st - | Address ret_a -> - begin match eval_rv (Analyses.ask_of_ctx ctx) gs st id with - | Thread a -> - let v = List.fold VD.join (VD.bot ()) (List.map (fun x -> G.thread (ctx.global (V.thread x))) (ValueDomain.Threads.elements a)) in - (* TODO: is this type right? *) - set ~ctx (Analyses.ask_of_ctx ctx) gs st ret_a (Cilfacade.typeOf ret_var) v - | _ -> invalidate ~ctx (Analyses.ask_of_ctx ctx) gs st [ret_var] - end - | _ -> invalidate ~ctx (Analyses.ask_of_ctx ctx) gs st [ret_var] - in - let st' = invalidate_ret_lv st' in - Priv.thread_join (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) id st' - | Unknown, "__goblint_assume_join" -> - let id = List.hd args in - Priv.thread_join ~force:true (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) id st - | Malloc size, _ -> begin - match lv with - | Some lv -> - let heap_var = - if (get_bool "sem.malloc.fail") - then AD.join (AD.of_var (heap_var ctx)) AD.null_ptr - else AD.of_var (heap_var ctx) - in - (* ignore @@ printf "malloc will allocate %a bytes\n" ID.pretty (eval_int ctx.ask gs st size); *) - set_many ~ctx (Analyses.ask_of_ctx ctx) gs st [(heap_var, TVoid [], Blob (VD.bot (), eval_int (Analyses.ask_of_ctx ctx) gs st size, true)); - (eval_lv (Analyses.ask_of_ctx ctx) gs st lv, (Cilfacade.typeOfLval lv), Address heap_var)] - | _ -> st - end - | Calloc { count = n; size }, _ -> - begin match lv with - | Some lv -> (* array length is set to one, as num*size is done when turning into `Calloc *) - let heap_var = heap_var ctx in - let add_null addr = - if get_bool "sem.malloc.fail" - then AD.join addr AD.null_ptr (* calloc can fail and return NULL *) - else addr in - let ik = Cilfacade.ptrdiff_ikind () in - let blobsize = ID.mul (ID.cast_to ik @@ eval_int (Analyses.ask_of_ctx ctx) gs st size) (ID.cast_to ik @@ eval_int (Analyses.ask_of_ctx ctx) gs st n) in - (* the memory that was allocated by calloc is set to bottom, but we keep track that it originated from calloc, so when bottom is read from memory allocated by calloc it is turned to zero *) - set_many ~ctx (Analyses.ask_of_ctx ctx) gs st [(add_null (AD.of_var heap_var), TVoid [], Array (CArrays.make (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) BI.one) (Blob (VD.bot (), blobsize, false)))); - (eval_lv (Analyses.ask_of_ctx ctx) gs st lv, (Cilfacade.typeOfLval lv), Address (add_null (AD.of_mval (heap_var, `Index (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) BI.zero, `NoOffset)))))] - | _ -> st - end - | Realloc { ptr = p; size }, _ -> - begin match lv with - | Some lv -> - let ask = Analyses.ask_of_ctx ctx in - let p_rv = eval_rv ask gs st p in - let p_addr = - match p_rv with - | Address a -> a - (* TODO: don't we already have logic for this? *) - | Int i when ID.to_int i = Some BI.zero -> AD.null_ptr - | Int i -> AD.top_ptr - | _ -> AD.top_ptr (* TODO: why does this ever happen? *) - in - let p_addr' = AD.remove NullPtr p_addr in (* realloc with NULL is same as malloc, remove to avoid unknown value from NullPtr access *) - let p_addr_get = get ask gs st p_addr' None in (* implicitly includes join of malloc value (VD.bot) *) - let size_int = eval_int ask gs st size in - let heap_val:value = Blob (p_addr_get, size_int, true) in (* copy old contents with new size *) - let heap_addr = AD.of_var (heap_var ctx) in - let heap_addr' = - if get_bool "sem.malloc.fail" then - AD.join heap_addr AD.null_ptr - else - heap_addr + | Strcmp { s1; s2; n }, _ -> + begin match lv with + | Some _ -> + (* when s1 and s2 type coincide, compare both both strings completely or their first n characters, otherwise use top *) + let dest_a, dest_typ, value = string_manipulation s1 s2 lv false (Some (fun s1_a s2_a -> Int(AD.string_comparison s1_a s2_a (eval_n n)))) in + set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value + | None -> st + end + | Abort, _ -> raise Deadcode + | ThreadExit { ret_val = exp }, _ -> + begin match ThreadId.get_current (Analyses.ask_of_ctx ctx) with + | `Lifted tid -> + ( + let rv = eval_rv (Analyses.ask_of_ctx ctx) ctx.global ctx.local exp in + ctx.sideg (V.thread tid) (G.create_thread rv); + (* TODO: emit thread return event so other analyses are aware? *) + (* TODO: publish still needed? *) + publish_all ctx `Return; (* like normal return *) + match ThreadId.get_current (Analyses.ask_of_ctx ctx) with + | `Lifted tid when ThreadReturn.is_current (Analyses.ask_of_ctx ctx) -> + ignore @@ Priv.thread_return (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) (priv_sideg ctx.sideg) tid st + | _ -> ()) + | _ -> () + end; + raise Deadcode + | MutexAttrSetType {attr = attr; typ = mtyp}, _ -> + begin + let get_type lval = + let address = eval_lv (Analyses.ask_of_ctx ctx) gs st lval in + AD.type_of address in - let lv_addr = eval_lv ask gs st lv in - set_many ~ctx ask gs st [ - (heap_addr, TVoid [], heap_val); - (lv_addr, Cilfacade.typeOfLval lv, Address heap_addr'); - ] (* TODO: free (i.e. invalidate) old blob if successful? *) - | None -> - st - end - | Assert { exp; refine; _ }, _ -> assert_fn ctx exp refine - | Setjmp { env }, _ -> - let ask = Analyses.ask_of_ctx ctx in - let st' = match eval_rv ask gs st env with - | Address jmp_buf -> - let value = VD.JmpBuf (ValueDomain.JmpBufs.Bufs.singleton (Target (ctx.prev_node, ctx.control_context ())), false) in - let r = set ~ctx ask gs st jmp_buf (Cilfacade.typeOf env) value in - if M.tracing then M.tracel "setjmp" "setting setjmp %a on %a -> %a\n" d_exp env D.pretty st D.pretty r; - r - | _ -> failwith "problem?!" - in - begin match lv with - | Some lv -> - set ~ctx ask gs st' (eval_lv ask ctx.global st lv) (Cilfacade.typeOfLval lv) (Int (ID.of_int IInt BI.zero)) - | None -> st' - end - | Longjmp {env; value}, _ -> - let ask = Analyses.ask_of_ctx ctx in - let ensure_not_zero (rv:value) = match rv with - | Int i -> - begin match ID.to_bool i with - | Some true -> rv - | Some false -> - M.error "Must: Longjmp with a value of 0 is silently changed to 1"; - Int (ID.of_int (ID.ikind i) Z.one) - | None -> - M.warn "May: Longjmp with a value of 0 is silently changed to 1"; - let ik = ID.ikind i in - Int (ID.join (ID.meet i (ID.of_excl_list ik [Z.zero])) (ID.of_int ik Z.one)) + let dst_lval = mkMem ~addr:(Cil.stripCasts attr) ~off:NoOffset in + let dest_typ = get_type dst_lval in + let dest_a = eval_lv (Analyses.ask_of_ctx ctx) gs st dst_lval in + match eval_rv (Analyses.ask_of_ctx ctx) gs st mtyp with + | Int x -> + begin + match ID.to_int x with + | Some z -> + if M.tracing then M.tracel "attr" "setting\n"; + set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ (MutexAttr (ValueDomain.MutexAttr.of_int z)) + | None -> set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ (MutexAttr (ValueDomain.MutexAttr.top ())) + end + | _ -> set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ (MutexAttr (ValueDomain.MutexAttr.top ())) + end + | Identity e, _ -> + begin match lv with + | Some x -> assign ctx x e + | None -> ctx.local + end + (**Floating point classification and trigonometric functions defined in c99*) + | Math { fun_args; }, _ -> + let apply_unary fk float_fun x = + let eval_x = eval_rv (Analyses.ask_of_ctx ctx) gs st x in + begin match eval_x with + | Float float_x -> float_fun (FD.cast_to fk float_x) + | _ -> failwith ("non-floating-point argument in call to function "^f.vname) end - | _ -> - M.warn ~category:Program "Arguments to longjmp are strange!"; - rv - in - let rv = ensure_not_zero @@ eval_rv ask ctx.global ctx.local value in - let t = Cilfacade.typeOf value in - set ~ctx ~t_override:t ask ctx.global ctx.local (AD.of_var !longjmp_return) t rv (* Not raising Deadcode here, deadcode is raised at a higher level! *) - | Rand, _ -> - begin match lv with - | Some x -> - let result:value = (Int (ID.starting IInt Z.zero)) in - set ~ctx (Analyses.ask_of_ctx ctx) gs st (eval_lv (Analyses.ask_of_ctx ctx) ctx.global st x) (Cilfacade.typeOfLval x) result - | None -> st - end - | _, _ -> - let st = - special_unknown_invalidate ctx (Analyses.ask_of_ctx ctx) gs st f args + in + let apply_binary fk float_fun x y = + let eval_x = eval_rv (Analyses.ask_of_ctx ctx) gs st x in + let eval_y = eval_rv (Analyses.ask_of_ctx ctx) gs st y in + begin match eval_x, eval_y with + | Float float_x, Float float_y -> float_fun (FD.cast_to fk float_x) (FD.cast_to fk float_y) + | _ -> failwith ("non-floating-point argument in call to function "^f.vname) + end + in + let result:value = + begin match fun_args with + | Nan (fk, str) when Cil.isPointerType (Cilfacade.typeOf str) -> Float (FD.nan_of fk) + | Nan _ -> failwith ("non-pointer argument in call to function "^f.vname) + | Inf fk -> Float (FD.inf_of fk) + | Isfinite x -> Int (ID.cast_to IInt (apply_unary FDouble FD.isfinite x)) + | Isinf x -> Int (ID.cast_to IInt (apply_unary FDouble FD.isinf x)) + | Isnan x -> Int (ID.cast_to IInt (apply_unary FDouble FD.isnan x)) + | Isnormal x -> Int (ID.cast_to IInt (apply_unary FDouble FD.isnormal x)) + | Signbit x -> Int (ID.cast_to IInt (apply_unary FDouble FD.signbit x)) + | Ceil (fk,x) -> Float (apply_unary fk FD.ceil x) + | Floor (fk,x) -> Float (apply_unary fk FD.floor x) + | Fabs (fk, x) -> Float (apply_unary fk FD.fabs x) + | Acos (fk, x) -> Float (apply_unary fk FD.acos x) + | Asin (fk, x) -> Float (apply_unary fk FD.asin x) + | Atan (fk, x) -> Float (apply_unary fk FD.atan x) + | Atan2 (fk, y, x) -> Float (apply_binary fk (fun y' x' -> FD.atan (FD.div y' x')) y x) + | Cos (fk, x) -> Float (apply_unary fk FD.cos x) + | Sin (fk, x) -> Float (apply_unary fk FD.sin x) + | Tan (fk, x) -> Float (apply_unary fk FD.tan x) + | Isgreater (x,y) -> Int(ID.cast_to IInt (apply_binary FDouble FD.gt x y)) + | Isgreaterequal (x,y) -> Int(ID.cast_to IInt (apply_binary FDouble FD.ge x y)) + | Isless (x,y) -> Int(ID.cast_to IInt (apply_binary FDouble FD.lt x y)) + | Islessequal (x,y) -> Int(ID.cast_to IInt (apply_binary FDouble FD.le x y)) + | Islessgreater (x,y) -> Int(ID.logor (ID.cast_to IInt (apply_binary FDouble FD.lt x y)) (ID.cast_to IInt (apply_binary FDouble FD.gt x y))) + | Isunordered (x,y) -> Int(ID.cast_to IInt (apply_binary FDouble FD.unordered x y)) + | Fmax (fd, x ,y) -> Float (apply_binary fd FD.fmax x y) + | Fmin (fd, x ,y) -> Float (apply_binary fd FD.fmin x y) + end + in + begin match lv with + | Some lv_val -> set ~ctx (Analyses.ask_of_ctx ctx) gs st (eval_lv (Analyses.ask_of_ctx ctx) ctx.global st lv_val) (Cilfacade.typeOfLval lv_val) result + | None -> st + end + (* handling thread creations *) + | ThreadCreate _, _ -> + invalidate_ret_lv ctx.local (* actual results joined via threadspawn *) + (* handling thread joins... sort of *) + | ThreadJoin { thread = id; ret_var }, _ -> + let st' = + match (eval_rv (Analyses.ask_of_ctx ctx) gs st ret_var) with + | Int n when GobOption.exists (BI.equal BI.zero) (ID.to_int n) -> st + | Address ret_a -> + begin match eval_rv (Analyses.ask_of_ctx ctx) gs st id with + | Thread a -> + let v = List.fold VD.join (VD.bot ()) (List.map (fun x -> G.thread (ctx.global (V.thread x))) (ValueDomain.Threads.elements a)) in + (* TODO: is this type right? *) + set ~ctx (Analyses.ask_of_ctx ctx) gs st ret_a (Cilfacade.typeOf ret_var) v + | _ -> invalidate ~ctx (Analyses.ask_of_ctx ctx) gs st [ret_var] + end + | _ -> invalidate ~ctx (Analyses.ask_of_ctx ctx) gs st [ret_var] + in + let st' = invalidate_ret_lv st' in + Priv.thread_join (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) id st' + | Unknown, "__goblint_assume_join" -> + let id = List.hd args in + Priv.thread_join ~force:true (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) id st + | Malloc size, _ -> begin + match lv with + | Some lv -> + let heap_var = + if (get_bool "sem.malloc.fail") + then AD.join (AD.of_var (heap_var ctx)) AD.null_ptr + else AD.of_var (heap_var ctx) + in + (* ignore @@ printf "malloc will allocate %a bytes\n" ID.pretty (eval_int ctx.ask gs st size); *) + set_many ~ctx (Analyses.ask_of_ctx ctx) gs st [(heap_var, TVoid [], Blob (VD.bot (), eval_int (Analyses.ask_of_ctx ctx) gs st size, true)); + (eval_lv (Analyses.ask_of_ctx ctx) gs st lv, (Cilfacade.typeOfLval lv), Address heap_var)] + | _ -> st + end + | Calloc { count = n; size }, _ -> + begin match lv with + | Some lv -> (* array length is set to one, as num*size is done when turning into `Calloc *) + let heap_var = heap_var ctx in + let add_null addr = + if get_bool "sem.malloc.fail" + then AD.join addr AD.null_ptr (* calloc can fail and return NULL *) + else addr in + let ik = Cilfacade.ptrdiff_ikind () in + let blobsize = ID.mul (ID.cast_to ik @@ eval_int (Analyses.ask_of_ctx ctx) gs st size) (ID.cast_to ik @@ eval_int (Analyses.ask_of_ctx ctx) gs st n) in + (* the memory that was allocated by calloc is set to bottom, but we keep track that it originated from calloc, so when bottom is read from memory allocated by calloc it is turned to zero *) + set_many ~ctx (Analyses.ask_of_ctx ctx) gs st [(add_null (AD.of_var heap_var), TVoid [], Array (CArrays.make (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) BI.one) (Blob (VD.bot (), blobsize, false)))); + (eval_lv (Analyses.ask_of_ctx ctx) gs st lv, (Cilfacade.typeOfLval lv), Address (add_null (AD.of_mval (heap_var, `Index (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) BI.zero, `NoOffset)))))] + | _ -> st + end + | Realloc { ptr = p; size }, _ -> + begin match lv with + | Some lv -> + let ask = Analyses.ask_of_ctx ctx in + let p_rv = eval_rv ask gs st p in + let p_addr = + match p_rv with + | Address a -> a + (* TODO: don't we already have logic for this? *) + | Int i when ID.to_int i = Some BI.zero -> AD.null_ptr + | Int i -> AD.top_ptr + | _ -> AD.top_ptr (* TODO: why does this ever happen? *) + in + let p_addr' = AD.remove NullPtr p_addr in (* realloc with NULL is same as malloc, remove to avoid unknown value from NullPtr access *) + let p_addr_get = get ask gs st p_addr' None in (* implicitly includes join of malloc value (VD.bot) *) + let size_int = eval_int ask gs st size in + let heap_val:value = Blob (p_addr_get, size_int, true) in (* copy old contents with new size *) + let heap_addr = AD.of_var (heap_var ctx) in + let heap_addr' = + if get_bool "sem.malloc.fail" then + AD.join heap_addr AD.null_ptr + else + heap_addr + in + let lv_addr = eval_lv ask gs st lv in + set_many ~ctx ask gs st [ + (heap_addr, TVoid [], heap_val); + (lv_addr, Cilfacade.typeOfLval lv, Address heap_addr'); + ] (* TODO: free (i.e. invalidate) old blob if successful? *) + | None -> + st + end + | Assert { exp; refine; _ }, _ -> assert_fn ctx exp refine + | Setjmp { env }, _ -> + let ask = Analyses.ask_of_ctx ctx in + let st' = match eval_rv ask gs st env with + | Address jmp_buf -> + let value = VD.JmpBuf (ValueDomain.JmpBufs.Bufs.singleton (Target (ctx.prev_node, ctx.control_context ())), false) in + let r = set ~ctx ask gs st jmp_buf (Cilfacade.typeOf env) value in + if M.tracing then M.tracel "setjmp" "setting setjmp %a on %a -> %a\n" d_exp env D.pretty st D.pretty r; + r + | _ -> failwith "problem?!" + in + begin match lv with + | Some lv -> + set ~ctx ask gs st' (eval_lv ask ctx.global st lv) (Cilfacade.typeOfLval lv) (Int (ID.of_int IInt BI.zero)) + | None -> st' + end + | Longjmp {env; value}, _ -> + let ask = Analyses.ask_of_ctx ctx in + let ensure_not_zero (rv:value) = match rv with + | Int i -> + begin match ID.to_bool i with + | Some true -> rv + | Some false -> + M.error "Must: Longjmp with a value of 0 is silently changed to 1"; + Int (ID.of_int (ID.ikind i) Z.one) + | None -> + M.warn "May: Longjmp with a value of 0 is silently changed to 1"; + let ik = ID.ikind i in + Int (ID.join (ID.meet i (ID.of_excl_list ik [Z.zero])) (ID.of_int ik Z.one)) + end + | _ -> + M.warn ~category:Program "Arguments to longjmp are strange!"; + rv + in + let rv = ensure_not_zero @@ eval_rv ask ctx.global ctx.local value in + let t = Cilfacade.typeOf value in + set ~ctx ~t_override:t ask ctx.global ctx.local (AD.of_var !longjmp_return) t rv (* Not raising Deadcode here, deadcode is raised at a higher level! *) + | Rand, _ -> + begin match lv with + | Some x -> + let result:value = (Int (ID.starting IInt Z.zero)) in + set ~ctx (Analyses.ask_of_ctx ctx) gs st (eval_lv (Analyses.ask_of_ctx ctx) ctx.global st x) (Cilfacade.typeOfLval x) result + | None -> st + end + | _, _ -> + let st = + special_unknown_invalidate ctx (Analyses.ask_of_ctx ctx) gs st f args (* * TODO: invalidate vars reachable via args * publish globals * if single-threaded: *call f*, privatize globals * else: spawn f *) - in - (* invalidate lhs in case of assign *) - invalidate_ret_lv st + in + (* invalidate lhs in case of assign *) + invalidate_ret_lv st in if get_bool "sem.noreturn.dead_code" && Cil.hasAttribute "noreturn" f.vattr then raise Deadcode else st From 10260bde16acbb131cd2053eefe0099cf4ca03b2 Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Sun, 16 Jul 2023 11:59:32 +0200 Subject: [PATCH 0319/1312] Added more recursive tests --- ...recursion-multiple-functions-terminating.c | 40 +++++++++++++++++++ ...ursion-multiple-functions-nonterminating.c | 40 +++++++++++++++++++ ...-recursion-different-context-terminating.c | 32 +++++++++++++++ ...cursion-different-context-nonterminating.c | 32 +++++++++++++++ 4 files changed, 144 insertions(+) create mode 100644 tests/regression/75-termination/44-recursion-multiple-functions-terminating.c create mode 100644 tests/regression/75-termination/45-recursion-multiple-functions-nonterminating.c create mode 100644 tests/regression/75-termination/46-recursion-different-context-terminating.c create mode 100644 tests/regression/75-termination/47-recursion-different-context-nonterminating.c diff --git a/tests/regression/75-termination/44-recursion-multiple-functions-terminating.c b/tests/regression/75-termination/44-recursion-multiple-functions-terminating.c new file mode 100644 index 0000000000..c112c72a73 --- /dev/null +++ b/tests/regression/75-termination/44-recursion-multiple-functions-terminating.c @@ -0,0 +1,40 @@ +// TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +void functionB(int n); +void functionC(int n); +void functionD(int n); + +void functionA(int n) { + if (n > 0) { + printf("Function A: %d\n", n); + functionB(n - 1); + } +} + +void functionB(int n) { + if (n > 0) { + printf("Function B: %d\n", n); + functionC(n - 1); + } +} + +void functionC(int n) { + if (n > 0) { + printf("Function C: %d\n", n); + functionD(n - 1); + } +} + +void functionD(int n) { + if (n > 0) { + printf("Function D: %d\n", n); + functionA(n - 1); + } +} + +int main() { + int n = 15; + functionA(n); + return 0; +} diff --git a/tests/regression/75-termination/45-recursion-multiple-functions-nonterminating.c b/tests/regression/75-termination/45-recursion-multiple-functions-nonterminating.c new file mode 100644 index 0000000000..47c13c0dca --- /dev/null +++ b/tests/regression/75-termination/45-recursion-multiple-functions-nonterminating.c @@ -0,0 +1,40 @@ +// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +void functionB(int n); +void functionC(int n); +void functionD(int n); + +void functionA(int n) { + if (n > 0) { + printf("Function A: %d\n", n); + functionB(n - 1); + } +} + +void functionB(int n) { + if (n > 0) { + printf("Function B: %d\n", n); + functionC(n - 1); + } +} + +void functionC(int n) { + if (n > 0) { + printf("Function C: %d\n", n); + functionD(n + 1); + } +} + +void functionD(int n) { + if (n > 0) { + printf("Function D: %d\n", n); + functionA(n + 1); + } +} + +int main() { + int n = 15; + functionA(n); + return 0; +} diff --git a/tests/regression/75-termination/46-recursion-different-context-terminating.c b/tests/regression/75-termination/46-recursion-different-context-terminating.c new file mode 100644 index 0000000000..4c5dd13035 --- /dev/null +++ b/tests/regression/75-termination/46-recursion-different-context-terminating.c @@ -0,0 +1,32 @@ +// TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +void functionC(int n); + +void functionA(int n) { + if (n > 0) { + printf("Function A: %d\n", n); + functionC(n - 1); + } +} + +void functionB(int n) { + if (n > 0) { + printf("Function B: %d\n", n); + functionC(n - 1); + } +} + +void functionC(int n) { + if (n > 0) { + printf("Function C: %d\n", n); + functionC(n - 1); + } +} + +int main() { + int n = 5; + functionA(n + 1); + functionB(n + 7); + return 0; +} diff --git a/tests/regression/75-termination/47-recursion-different-context-nonterminating.c b/tests/regression/75-termination/47-recursion-different-context-nonterminating.c new file mode 100644 index 0000000000..3216275748 --- /dev/null +++ b/tests/regression/75-termination/47-recursion-different-context-nonterminating.c @@ -0,0 +1,32 @@ +// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +void functionC(int n); + +void functionA(int n) { + if (n > 0) { + printf("Function A: %d\n", n); + functionC(n - 1); + } +} + +void functionB(int n) { + if (n > 0) { + printf("Function B: %d\n", n); + functionC(n - 1); + } +} + +void functionC(int n) { + if (n > 0) { + printf("Function C: %d\n", n); + functionC(n); + } +} + +int main() { + int n = 5; + functionA(n + 1); + functionB(n + 7); + return 0; +} From 4e365110bde212326e7d06867eef1801be85e732 Mon Sep 17 00:00:00 2001 From: Isidor Zweckstetter Date: Sun, 16 Jul 2023 18:36:02 +0200 Subject: [PATCH 0320/1312] removed exit-indikator variable --- src/analyses/libraryDesc.ml | 2 +- src/analyses/loopTermination.ml | 5 +---- src/util/terminationPreprocessing.ml | 15 +++------------ 3 files changed, 5 insertions(+), 17 deletions(-) diff --git a/src/analyses/libraryDesc.ml b/src/analyses/libraryDesc.ml index c3c8d0f85b..0557dc28a2 100644 --- a/src/analyses/libraryDesc.ml +++ b/src/analyses/libraryDesc.ml @@ -72,7 +72,7 @@ type special = | Identity of Cil.exp (** Identity function. Some compiler optimization annotation functions map to this. *) | Setjmp of { env: Cil.exp; } | Longjmp of { env: Cil.exp; value: Cil.exp; } - | Bounded of { exp: Cil.exp} + | Bounded of { exp: Cil.exp} (** Used to check for bounds for termination analysis. *) | Rand | Unknown (** Anything not belonging to other types. *) (* TODO: rename to Other? *) diff --git a/src/analyses/loopTermination.ml b/src/analyses/loopTermination.ml index 72743444b0..b07fb143b4 100644 --- a/src/analyses/loopTermination.ml +++ b/src/analyses/loopTermination.ml @@ -14,9 +14,6 @@ let loop_counters : stmt VarToStmt.t ref = ref VarToStmt.empty (** Contains the locations of the upjumping gotos *) let upjumping_gotos : location list ref = ref [] -(** Indicates the place in the code, right after a loop is exited. *) -let loop_exit : varinfo ref = ref (makeVarinfo false "-error" Cil.intType) - let is_loop_counter_var (x : varinfo) = VarToStmt.mem x !loop_counters @@ -126,6 +123,6 @@ end let () = (* Register the preprocessing *) - Cilfacade.register_preprocess_cil (Spec.name ()) (new loopCounterVisitor loop_counters upjumping_gotos loop_exit); + Cilfacade.register_preprocess_cil (Spec.name ()) (new loopCounterVisitor loop_counters upjumping_gotos); (* Register this analysis within the master control program *) MCP.register_analysis (module Spec : MCPSpec) diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index 1aa78ccae7..1432a2da98 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -1,5 +1,4 @@ open GoblintCil -include Printf module VarToStmt = Map.Make(CilType.Varinfo) (* maps varinfos (= loop counter variable) to the statement of the corresponding loop*) @@ -29,15 +28,8 @@ let extract_file_name s = (*There still may be a need to filt let show_location_id l = string_of_int l.line ^ "_" ^ string_of_int l.column ^ "-file" ^ "_" ^ extract_file_name l.file -class loopCounterVisitor lc lg le (fd : fundec) = object(self) +class loopCounterVisitor lc lg (fd : fundec) = object(self) inherit nopCilVisitor - method! vfunc (f:fundec) = - if !le.vname <> "term_exit-" then begin - let exit_name = "term_exit-" in - let typ = Cil.intType in - le := Cil.makeGlobalVar exit_name typ; - end; - DoChildren; (* function definition *) method! vstmt s = let action s = match s.skind with | Loop (b, loc, eloc, _, _) -> @@ -46,14 +38,13 @@ class loopCounterVisitor lc lg le (fd : fundec) = object(self) let v = (Cil.makeLocalVar fd name typ) in (*Not tested for incremental mode*) let init_stmt = mkStmtOneInstr @@ Set (var v, zero, loc, eloc) in let inc_stmt = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in - let check_stmt = mkStmtOneInstr @@ Set ((var !le), (Lval (var v)), loc, eloc) in let inc_stmt2 = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in let exit_stmt = mkStmtOneInstr @@ Call (None, f_bounded, [Lval (var v)], loc, locUnknown) in (match b.bstmts with | s :: ss -> (*duplicate increment statement here to fix inconsistencies in nested loops*) - b.bstmts <- inc_stmt :: check_stmt :: exit_stmt :: s :: inc_stmt2 :: ss; + b.bstmts <- inc_stmt :: exit_stmt :: s :: inc_stmt2 :: ss; | ss -> - b.bstmts <- inc_stmt :: check_stmt :: exit_stmt :: ss; + b.bstmts <- inc_stmt :: exit_stmt :: ss; ); lc := VarToStmt.add (v: varinfo) (s: stmt) !lc; let nb = mkBlock [init_stmt; mkStmt s.skind] in From d47e88e795b453c8453d75526f6b6f6cb59de0a8 Mon Sep 17 00:00:00 2001 From: Isidor Zweckstetter Date: Mon, 17 Jul 2023 14:09:02 +0200 Subject: [PATCH 0321/1312] removed debug print; Might solve cram tests --- src/util/terminationPreprocessing.ml | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index 1432a2da98..e20ee9b375 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -2,18 +2,7 @@ open GoblintCil module VarToStmt = Map.Make(CilType.Varinfo) (* maps varinfos (= loop counter variable) to the statement of the corresponding loop*) -let specialFunction name = - print_endline @@ "specialfunction done"; - { svar = makeGlobalVar name (TFun(voidType, Some [("exp", intType, [])], false,[])); - smaxid = 0; - slocals = []; - sformals = []; - sbody = mkBlock []; - smaxstmtid = None; - sallstmts = []; - } -let f_bounded = Lval (var (specialFunction "__goblint_bounded").svar) let extract_file_name s = (*There still may be a need to filter more chars*) let ls = String.split_on_char '/' s in (*Assuming '/' as path seperator*) @@ -30,7 +19,21 @@ let show_location_id l = class loopCounterVisitor lc lg (fd : fundec) = object(self) inherit nopCilVisitor + method! vstmt s = + + let specialFunction name = + { svar = makeGlobalVar name (TFun(voidType, Some [("exp", intType, [])], false,[])); + smaxid = 0; + slocals = []; + sformals = []; + sbody = mkBlock []; + smaxstmtid = None; + sallstmts = []; + } in + + let f_bounded = Lval (var (specialFunction "__goblint_bounded").svar) in + let action s = match s.skind with | Loop (b, loc, eloc, _, _) -> let name = "term"^show_location_id loc in From 021ddaddbd44c0498324439a0672b42efb2dbd1e Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Mon, 17 Jul 2023 14:28:27 +0200 Subject: [PATCH 0322/1312] Remove debug output --- src/util/terminationPreprocessing.ml | 97 ++++++++++++++-------------- 1 file changed, 48 insertions(+), 49 deletions(-) diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index 1432a2da98..c3c6e0500c 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -3,60 +3,59 @@ open GoblintCil module VarToStmt = Map.Make(CilType.Varinfo) (* maps varinfos (= loop counter variable) to the statement of the corresponding loop*) let specialFunction name = - print_endline @@ "specialfunction done"; - { svar = makeGlobalVar name (TFun(voidType, Some [("exp", intType, [])], false,[])); - smaxid = 0; - slocals = []; - sformals = []; - sbody = mkBlock []; - smaxstmtid = None; - sallstmts = []; - } + { svar = makeGlobalVar name (TFun(voidType, Some [("exp", intType, [])], false,[])); + smaxid = 0; + slocals = []; + sformals = []; + sbody = mkBlock []; + smaxstmtid = None; + sallstmts = []; + } let f_bounded = Lval (var (specialFunction "__goblint_bounded").svar) let extract_file_name s = (*There still may be a need to filter more chars*) - let ls = String.split_on_char '/' s in (*Assuming '/' as path seperator*) - let ls = List.rev ls in - let s' = List.nth ls 0 in - let ls = String.split_on_char '.' s' in - let s' = List.nth ls 0 in - let without_spaces = String.split_on_char ' ' s' in - let s' = String.concat "" without_spaces in - s' + let ls = String.split_on_char '/' s in (*Assuming '/' as path seperator*) + let ls = List.rev ls in + let s' = List.nth ls 0 in + let ls = String.split_on_char '.' s' in + let s' = List.nth ls 0 in + let without_spaces = String.split_on_char ' ' s' in + let s' = String.concat "" without_spaces in + s' let show_location_id l = - string_of_int l.line ^ "_" ^ string_of_int l.column ^ "-file" ^ "_" ^ extract_file_name l.file + string_of_int l.line ^ "_" ^ string_of_int l.column ^ "-file" ^ "_" ^ extract_file_name l.file class loopCounterVisitor lc lg (fd : fundec) = object(self) - inherit nopCilVisitor - method! vstmt s = - let action s = match s.skind with - | Loop (b, loc, eloc, _, _) -> - let name = "term"^show_location_id loc in - let typ = Cil.intType in - let v = (Cil.makeLocalVar fd name typ) in (*Not tested for incremental mode*) - let init_stmt = mkStmtOneInstr @@ Set (var v, zero, loc, eloc) in - let inc_stmt = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in - let inc_stmt2 = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in - let exit_stmt = mkStmtOneInstr @@ Call (None, f_bounded, [Lval (var v)], loc, locUnknown) in - (match b.bstmts with - | s :: ss -> (*duplicate increment statement here to fix inconsistencies in nested loops*) - b.bstmts <- inc_stmt :: exit_stmt :: s :: inc_stmt2 :: ss; - | ss -> - b.bstmts <- inc_stmt :: exit_stmt :: ss; - ); - lc := VarToStmt.add (v: varinfo) (s: stmt) !lc; - let nb = mkBlock [init_stmt; mkStmt s.skind] in - s.skind <- Block nb; - s - | Goto (sref, l) -> - let goto_jmp_stmt = sref.contents.skind in - let loc_stmt = get_stmtLoc goto_jmp_stmt in - if CilType.Location.compare l loc_stmt >= 0 (*is pos if first loc is greater -> below the second loc*) - then - lg := List.append !lg ([l] : location list); (*problem: the program might not terminate!*) - s - | _ -> s - in ChangeDoChildrenPost (s, action); - end \ No newline at end of file + inherit nopCilVisitor + method! vstmt s = + let action s = match s.skind with + | Loop (b, loc, eloc, _, _) -> + let name = "term"^show_location_id loc in + let typ = Cil.intType in + let v = (Cil.makeLocalVar fd name typ) in (*Not tested for incremental mode*) + let init_stmt = mkStmtOneInstr @@ Set (var v, zero, loc, eloc) in + let inc_stmt = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in + let inc_stmt2 = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in + let exit_stmt = mkStmtOneInstr @@ Call (None, f_bounded, [Lval (var v)], loc, locUnknown) in + (match b.bstmts with + | s :: ss -> (*duplicate increment statement here to fix inconsistencies in nested loops*) + b.bstmts <- inc_stmt :: exit_stmt :: s :: inc_stmt2 :: ss; + | ss -> + b.bstmts <- inc_stmt :: exit_stmt :: ss; + ); + lc := VarToStmt.add (v: varinfo) (s: stmt) !lc; + let nb = mkBlock [init_stmt; mkStmt s.skind] in + s.skind <- Block nb; + s + | Goto (sref, l) -> + let goto_jmp_stmt = sref.contents.skind in + let loc_stmt = get_stmtLoc goto_jmp_stmt in + if CilType.Location.compare l loc_stmt >= 0 (*is pos if first loc is greater -> below the second loc*) + then + lg := List.append !lg ([l] : location list); (*problem: the program might not terminate!*) + s + | _ -> s + in ChangeDoChildrenPost (s, action); +end From 47440a3ec68a72e60e4597a679c8362807406584 Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Mon, 17 Jul 2023 14:55:13 +0200 Subject: [PATCH 0323/1312] Code style optimization --- .../35-goto-out-of-inner-loop-with-print-terminating.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression/75-termination/35-goto-out-of-inner-loop-with-print-terminating.c b/tests/regression/75-termination/35-goto-out-of-inner-loop-with-print-terminating.c index f564354e51..3bd6e53d2d 100644 --- a/tests/regression/75-termination/35-goto-out-of-inner-loop-with-print-terminating.c +++ b/tests/regression/75-termination/35-goto-out-of-inner-loop-with-print-terminating.c @@ -18,7 +18,7 @@ int main() } printf("(%d, %d) ", i, j); } - outer_loop:; // Label for the outer loop + outer_loop: // Label for the outer loop printf("\n"); } From 91d331d588bae35c973b5c238a0dfe3a19a24f3b Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Mon, 17 Jul 2023 16:10:51 +0200 Subject: [PATCH 0324/1312] Indentation --- src/framework/constraints.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 2217fab707..ad05bbbcc6 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1719,7 +1719,7 @@ struct (* Tuple containing the fundec and context of the caller *) module CallGraphTuple = Printable.Prod (CilType.Fundec) (S.C) - + (* Set containing multiple caller tuples *) module CallGraphSet = SetDomain.Make (CallGraphTuple) From fc427bc264800660f1dd250230fdb0b70803a279 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Mon, 17 Jul 2023 16:18:23 +0200 Subject: [PATCH 0325/1312] Fix indentation --- src/framework/constraints.ml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 2217fab707..6883cc72d9 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1718,16 +1718,16 @@ struct module V = GVarF(S.V) (* Tuple containing the fundec and context of the caller *) - module CallGraphTuple = Printable.Prod (CilType.Fundec) (S.C) - + module CallGraphTuple = Printable.Prod (CilType.Fundec) (S.C) + (* Set containing multiple caller tuples *) module CallGraphSet = SetDomain.Make (CallGraphTuple) (* Mapping from the callee context to the set of all caller tuples*) module CallGraphMap = MapDomain.MapBot (S.C) (CallGraphSet) - module G = - struct + module G = + struct include Lattice.Lift2 (G) (CallGraphMap) (Printable.DefaultNames) let spec = function From 3fb5d1442335772e389e4a4a9b8e17342b0a52dc Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Mon, 17 Jul 2023 17:45:23 +0200 Subject: [PATCH 0326/1312] Test case 48 --- .../48-dynamic-recursion-nonterminating.c | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 tests/regression/75-termination/48-dynamic-recursion-nonterminating.c diff --git a/tests/regression/75-termination/48-dynamic-recursion-nonterminating.c b/tests/regression/75-termination/48-dynamic-recursion-nonterminating.c new file mode 100644 index 0000000000..066c2f51b1 --- /dev/null +++ b/tests/regression/75-termination/48-dynamic-recursion-nonterminating.c @@ -0,0 +1,10 @@ +// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +void troll(void (*f) ()) +{ + f(f); +} + +int main() +{ + troll(troll); +} From 601698fa8bf3c25a0f1b739e38ac2c6b9eac7bdf Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Tue, 18 Jul 2023 15:37:07 +0200 Subject: [PATCH 0327/1312] Remove unused code; introduce basic error-handling --- src/analyses/loopTermination.ml | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/analyses/loopTermination.ml b/src/analyses/loopTermination.ml index b07fb143b4..afd86fc483 100644 --- a/src/analyses/loopTermination.ml +++ b/src/analyses/loopTermination.ml @@ -14,9 +14,6 @@ let loop_counters : stmt VarToStmt.t ref = ref VarToStmt.empty (** Contains the locations of the upjumping gotos *) let upjumping_gotos : location list ref = ref [] -let is_loop_counter_var (x : varinfo) = - VarToStmt.mem x !loop_counters - let no_upjumping_gotos () = upjumping_gotos.contents = [] @@ -78,18 +75,23 @@ struct if !AnalysisState.postsolving then match f.vname, arglist with "__goblint_bounded", [Lval (Var x, NoOffset)] -> - let is_bounded = check_bounded ctx x in - let loop_statement = VarToStmt.find x !loop_counters in - ctx.sideg () (G.add (`Lifted loop_statement) is_bounded (ctx.global ())); - (* In case the loop is not bounded, a warning is created. *) - if not (is_bounded) then ( - let msgs = - [(Pretty.dprintf - "The program might not terminate! (Loop analysis)", - Some (M.Location.CilLocation (Cilfacade.get_stmtLoc loop_statement)) - );] in - M.msg_group Warning ~category:NonTerminating "Possibly non terminating loops" msgs); - () + (try + let loop_statement = VarToStmt.find x !loop_counters in + let is_bounded = check_bounded ctx x in + ctx.sideg () (G.add (`Lifted loop_statement) is_bounded (ctx.global ())); + (* In case the loop is not bounded, a warning is created. *) + if not (is_bounded) then ( + let msgs = + [(Pretty.dprintf + "The program might not terminate! (Loop analysis)", + Some (M.Location.CilLocation (Cilfacade.get_stmtLoc loop_statement)) + );] in + M.msg_group Warning ~category:NonTerminating "Possibly non terminating loops" msgs); + () + with Not_found -> + (* This should not happen as long as __goblint_bounded is only used + * for this analysis. *) + ()) | _ -> () else () From 780e02a6eea74b9e8064bda119e9b48ebd0eea0b Mon Sep 17 00:00:00 2001 From: Nathan Schmidt <73504207+nathanschmidt@users.noreply.github.com> Date: Thu, 20 Jul 2023 22:09:36 +0200 Subject: [PATCH 0328/1312] Update condition for non-zero return by strncmp --- src/cdomains/arrayDomain.ml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index f1bab39208..7772cec8d4 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1687,8 +1687,9 @@ struct Idx.starting IInt Z.one else (* if first null bytes are certain, have different indexes and are before index n if n present, return integer <> 0 *) - (try if Z.equal (must_nulls_min_elt must_nulls_set1) (may_nulls_min_elt may_nulls_set1) && (not n_exists || Z.lt (must_nulls_min_elt must_nulls_set1) n) - && Z.equal (must_nulls_min_elt must_nulls_set2) (may_nulls_min_elt may_nulls_set2) && (not n_exists || Z.lt (must_nulls_min_elt must_nulls_set2) n) + (try if Z.equal (must_nulls_min_elt must_nulls_set1) (may_nulls_min_elt may_nulls_set1) + && Z.equal (must_nulls_min_elt must_nulls_set2) (may_nulls_min_elt may_nulls_set2) + && (not n_exists || Z.lt (must_nulls_min_elt must_nulls_set1) n || Z.lt (must_nulls_min_elt must_nulls_set2) n ) && not (Z.equal (must_nulls_min_elt must_nulls_set1) (must_nulls_min_elt must_nulls_set2)) then Idx.of_excl_list IInt [Z.zero] else From 1bf625d8528cf59f3b8b0fac47ca68ded7c57d57 Mon Sep 17 00:00:00 2001 From: Nathan Schmidt Date: Thu, 20 Jul 2023 22:19:10 +0200 Subject: [PATCH 0329/1312] Fix indentation --- src/cdomains/arrayDomain.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 7772cec8d4..7892826e57 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1688,8 +1688,8 @@ struct else (* if first null bytes are certain, have different indexes and are before index n if n present, return integer <> 0 *) (try if Z.equal (must_nulls_min_elt must_nulls_set1) (may_nulls_min_elt may_nulls_set1) - && Z.equal (must_nulls_min_elt must_nulls_set2) (may_nulls_min_elt may_nulls_set2) - && (not n_exists || Z.lt (must_nulls_min_elt must_nulls_set1) n || Z.lt (must_nulls_min_elt must_nulls_set2) n ) + && Z.equal (must_nulls_min_elt must_nulls_set2) (may_nulls_min_elt may_nulls_set2) + && (not n_exists || Z.lt (must_nulls_min_elt must_nulls_set1) n || Z.lt (must_nulls_min_elt must_nulls_set2) n ) && not (Z.equal (must_nulls_min_elt must_nulls_set1) (must_nulls_min_elt must_nulls_set2)) then Idx.of_excl_list IInt [Z.zero] else From 66115b1da109bf028bf6648ebb2724af95ab1fcb Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Mon, 24 Jul 2023 13:34:35 +0200 Subject: [PATCH 0330/1312] Annotate tests with skip as Apron ist used --- tests/regression/75-termination/01-simple-loop-terminating.c | 2 +- .../regression/75-termination/02-simple-loop-nonterminating.c | 2 +- tests/regression/75-termination/03-nested-loop-terminating.c | 2 +- .../regression/75-termination/04-nested-loop-nonterminating.c | 2 +- tests/regression/75-termination/05-for-loop-terminating.c | 2 +- tests/regression/75-termination/06-for-loop-nonterminating.c | 2 +- .../75-termination/07-nested-for-loop-terminating.c | 2 +- .../75-termination/08-nested-for-loop-nonterminating.c | 2 +- .../75-termination/09-complex-for-loop-terminating.c | 2 +- tests/regression/75-termination/10-complex-loop-terminating.c | 2 +- tests/regression/75-termination/11-loopless-termination.c | 2 +- .../75-termination/12-do-while-instant-terminating.c | 2 +- tests/regression/75-termination/13-do-while-terminating.c | 2 +- tests/regression/75-termination/14-do-while-nonterminating.c | 2 +- .../75-termination/15-complex-loop-combination-terminating.c | 2 +- .../75-termination/16-nested-loop-nontrivial-nonterminating.c | 2 +- tests/regression/75-termination/17-goto-terminating.c | 2 +- tests/regression/75-termination/18-goto-nonterminating.c | 2 +- tests/regression/75-termination/19-rand-terminating.c | 2 +- tests/regression/75-termination/20-rand-nonterminating.c | 2 +- .../75-termination/21-no-exit-on-rand-unproofable.c | 2 +- tests/regression/75-termination/22-exit-on-rand-unproofable.c | 2 +- tests/regression/75-termination/23-exit-on-rand-terminating.c | 2 +- .../75-termination/24-upjumping-goto-loopless-terminating.c | 2 +- .../75-termination/25-leave-loop-goto-terminating.c | 2 +- .../75-termination/26-enter-loop-goto-terminating.c | 2 +- .../75-termination/27-upjumping-goto-nonterminating.c | 2 +- .../75-termination/28-do-while-continue-terminating.c | 2 +- .../75-termination/29-do-while-continue-nonterminating.c | 2 +- .../75-termination/30-goto-out-of-inner-loop-terminating.c | 2 +- .../75-termination/31-goto-out-of-inner-loop-nonterminating.c | 2 +- tests/regression/75-termination/32-multithread-terminating.c | 2 +- .../regression/75-termination/33-multithread-nonterminating.c | 2 +- .../75-termination/34-nested-for-loop-nonterminating.c | 2 +- .../35-goto-out-of-inner-loop-with-print-terminating.c | 2 +- tests/regression/75-termination/36-recursion-terminating.c | 2 +- tests/regression/75-termination/37-recursion-nonterminating.c | 2 +- .../75-termination/38-recursion-nested-terminating.c | 2 +- .../75-termination/39-recursion-nested-nonterminating.c | 4 ++-- .../40-multi-expression-conditions-terminating.c | 2 +- tests/regression/75-termination/41-for-continue-terminating.c | 2 +- .../75-termination/42-downjumping-goto-loopless-terminating.c | 2 +- .../75-termination/43-return-from-endless-loop-terminating.c | 2 +- .../44-recursion-multiple-functions-terminating.c | 2 +- .../45-recursion-multiple-functions-nonterminating.c | 2 +- .../46-recursion-different-context-terminating.c | 2 +- .../47-recursion-different-context-nonterminating.c | 2 +- .../75-termination/48-dynamic-recursion-nonterminating.c | 2 +- 48 files changed, 49 insertions(+), 49 deletions(-) diff --git a/tests/regression/75-termination/01-simple-loop-terminating.c b/tests/regression/75-termination/01-simple-loop-terminating.c index aaa2a7a895..8ca4610057 100644 --- a/tests/regression/75-termination/01-simple-loop-terminating.c +++ b/tests/regression/75-termination/01-simple-loop-terminating.c @@ -1,4 +1,4 @@ -// TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/75-termination/02-simple-loop-nonterminating.c b/tests/regression/75-termination/02-simple-loop-nonterminating.c index c6e1c6c8d6..d8847e2b74 100644 --- a/tests/regression/75-termination/02-simple-loop-nonterminating.c +++ b/tests/regression/75-termination/02-simple-loop-nonterminating.c @@ -1,4 +1,4 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/75-termination/03-nested-loop-terminating.c b/tests/regression/75-termination/03-nested-loop-terminating.c index 70327c1016..fd1ee14f39 100644 --- a/tests/regression/75-termination/03-nested-loop-terminating.c +++ b/tests/regression/75-termination/03-nested-loop-terminating.c @@ -1,4 +1,4 @@ -// TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/75-termination/04-nested-loop-nonterminating.c b/tests/regression/75-termination/04-nested-loop-nonterminating.c index 21a6d47051..21b6014509 100644 --- a/tests/regression/75-termination/04-nested-loop-nonterminating.c +++ b/tests/regression/75-termination/04-nested-loop-nonterminating.c @@ -1,4 +1,4 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/75-termination/05-for-loop-terminating.c b/tests/regression/75-termination/05-for-loop-terminating.c index bf58408487..7a2b789496 100644 --- a/tests/regression/75-termination/05-for-loop-terminating.c +++ b/tests/regression/75-termination/05-for-loop-terminating.c @@ -1,4 +1,4 @@ -// TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/75-termination/06-for-loop-nonterminating.c b/tests/regression/75-termination/06-for-loop-nonterminating.c index 0f96209e35..6c6123251c 100644 --- a/tests/regression/75-termination/06-for-loop-nonterminating.c +++ b/tests/regression/75-termination/06-for-loop-nonterminating.c @@ -1,4 +1,4 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/75-termination/07-nested-for-loop-terminating.c b/tests/regression/75-termination/07-nested-for-loop-terminating.c index 1c43eeaada..f1dde17dc5 100644 --- a/tests/regression/75-termination/07-nested-for-loop-terminating.c +++ b/tests/regression/75-termination/07-nested-for-loop-terminating.c @@ -1,4 +1,4 @@ -// TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/75-termination/08-nested-for-loop-nonterminating.c b/tests/regression/75-termination/08-nested-for-loop-nonterminating.c index ec76f31534..cb65a0d267 100644 --- a/tests/regression/75-termination/08-nested-for-loop-nonterminating.c +++ b/tests/regression/75-termination/08-nested-for-loop-nonterminating.c @@ -1,4 +1,4 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/75-termination/09-complex-for-loop-terminating.c b/tests/regression/75-termination/09-complex-for-loop-terminating.c index 9767b4bc1c..264c08f6ed 100644 --- a/tests/regression/75-termination/09-complex-for-loop-terminating.c +++ b/tests/regression/75-termination/09-complex-for-loop-terminating.c @@ -1,4 +1,4 @@ -// TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --set sem.int.signed_overflow assume_none +// SKIP TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --set sem.int.signed_overflow assume_none #include int main() diff --git a/tests/regression/75-termination/10-complex-loop-terminating.c b/tests/regression/75-termination/10-complex-loop-terminating.c index 19091b1033..90317d5209 100644 --- a/tests/regression/75-termination/10-complex-loop-terminating.c +++ b/tests/regression/75-termination/10-complex-loop-terminating.c @@ -1,4 +1,4 @@ -// TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --set sem.int.signed_overflow assume_none +// SKIP TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --set sem.int.signed_overflow assume_none #include int main() diff --git a/tests/regression/75-termination/11-loopless-termination.c b/tests/regression/75-termination/11-loopless-termination.c index 51c0605757..9f1a7e0f13 100644 --- a/tests/regression/75-termination/11-loopless-termination.c +++ b/tests/regression/75-termination/11-loopless-termination.c @@ -1,4 +1,4 @@ -// TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/75-termination/12-do-while-instant-terminating.c b/tests/regression/75-termination/12-do-while-instant-terminating.c index 3767430a51..5bc18902b3 100644 --- a/tests/regression/75-termination/12-do-while-instant-terminating.c +++ b/tests/regression/75-termination/12-do-while-instant-terminating.c @@ -1,4 +1,4 @@ -// TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/75-termination/13-do-while-terminating.c b/tests/regression/75-termination/13-do-while-terminating.c index 8faeec1e64..6ac6946495 100644 --- a/tests/regression/75-termination/13-do-while-terminating.c +++ b/tests/regression/75-termination/13-do-while-terminating.c @@ -1,4 +1,4 @@ -// TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/75-termination/14-do-while-nonterminating.c b/tests/regression/75-termination/14-do-while-nonterminating.c index 5522b61d88..0a9df3421f 100644 --- a/tests/regression/75-termination/14-do-while-nonterminating.c +++ b/tests/regression/75-termination/14-do-while-nonterminating.c @@ -1,4 +1,4 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/75-termination/15-complex-loop-combination-terminating.c b/tests/regression/75-termination/15-complex-loop-combination-terminating.c index d987397dd7..c2ab718200 100644 --- a/tests/regression/75-termination/15-complex-loop-combination-terminating.c +++ b/tests/regression/75-termination/15-complex-loop-combination-terminating.c @@ -1,4 +1,4 @@ -// TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --set sem.int.signed_overflow assume_none +// SKIP TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --set sem.int.signed_overflow assume_none #include int main() diff --git a/tests/regression/75-termination/16-nested-loop-nontrivial-nonterminating.c b/tests/regression/75-termination/16-nested-loop-nontrivial-nonterminating.c index bded788a90..267a2d2fd8 100644 --- a/tests/regression/75-termination/16-nested-loop-nontrivial-nonterminating.c +++ b/tests/regression/75-termination/16-nested-loop-nontrivial-nonterminating.c @@ -1,4 +1,4 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/75-termination/17-goto-terminating.c b/tests/regression/75-termination/17-goto-terminating.c index 941db0c601..2f678d294b 100644 --- a/tests/regression/75-termination/17-goto-terminating.c +++ b/tests/regression/75-termination/17-goto-terminating.c @@ -1,4 +1,4 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra // The program terminates but the analysis is currently only meant to detect up-jumping gotos as potentially NonTerminating, therefore we expect an NonTerm #include diff --git a/tests/regression/75-termination/18-goto-nonterminating.c b/tests/regression/75-termination/18-goto-nonterminating.c index f88088ad12..6de80effd7 100644 --- a/tests/regression/75-termination/18-goto-nonterminating.c +++ b/tests/regression/75-termination/18-goto-nonterminating.c @@ -1,4 +1,4 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/75-termination/19-rand-terminating.c b/tests/regression/75-termination/19-rand-terminating.c index 06deac6c34..a5b6c22941 100644 --- a/tests/regression/75-termination/19-rand-terminating.c +++ b/tests/regression/75-termination/19-rand-terminating.c @@ -1,4 +1,4 @@ -// TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include #include #include diff --git a/tests/regression/75-termination/20-rand-nonterminating.c b/tests/regression/75-termination/20-rand-nonterminating.c index 394bce7709..21b25ed9dd 100644 --- a/tests/regression/75-termination/20-rand-nonterminating.c +++ b/tests/regression/75-termination/20-rand-nonterminating.c @@ -1,4 +1,4 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include #include #include diff --git a/tests/regression/75-termination/21-no-exit-on-rand-unproofable.c b/tests/regression/75-termination/21-no-exit-on-rand-unproofable.c index 902ef2a4e2..5f82d91079 100644 --- a/tests/regression/75-termination/21-no-exit-on-rand-unproofable.c +++ b/tests/regression/75-termination/21-no-exit-on-rand-unproofable.c @@ -1,4 +1,4 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/75-termination/22-exit-on-rand-unproofable.c b/tests/regression/75-termination/22-exit-on-rand-unproofable.c index f14f7d4e3f..33838ca83d 100644 --- a/tests/regression/75-termination/22-exit-on-rand-unproofable.c +++ b/tests/regression/75-termination/22-exit-on-rand-unproofable.c @@ -1,4 +1,4 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/75-termination/23-exit-on-rand-terminating.c b/tests/regression/75-termination/23-exit-on-rand-terminating.c index 013aff2dd5..e65c064c40 100644 --- a/tests/regression/75-termination/23-exit-on-rand-terminating.c +++ b/tests/regression/75-termination/23-exit-on-rand-terminating.c @@ -1,4 +1,4 @@ -// TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include #include diff --git a/tests/regression/75-termination/24-upjumping-goto-loopless-terminating.c b/tests/regression/75-termination/24-upjumping-goto-loopless-terminating.c index 3f4e115445..ce257d11ef 100644 --- a/tests/regression/75-termination/24-upjumping-goto-loopless-terminating.c +++ b/tests/regression/75-termination/24-upjumping-goto-loopless-terminating.c @@ -1,4 +1,4 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra // The program terminates but the analysis is currently only meant to detect up-jumping gotos as potentially NonTerminating, therefore we expect an NonTerm #include diff --git a/tests/regression/75-termination/25-leave-loop-goto-terminating.c b/tests/regression/75-termination/25-leave-loop-goto-terminating.c index 2cda3d3a03..b882759bff 100644 --- a/tests/regression/75-termination/25-leave-loop-goto-terminating.c +++ b/tests/regression/75-termination/25-leave-loop-goto-terminating.c @@ -1,4 +1,4 @@ -// TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/75-termination/26-enter-loop-goto-terminating.c b/tests/regression/75-termination/26-enter-loop-goto-terminating.c index 0de9a95d6c..aa85f22b3e 100644 --- a/tests/regression/75-termination/26-enter-loop-goto-terminating.c +++ b/tests/regression/75-termination/26-enter-loop-goto-terminating.c @@ -1,4 +1,4 @@ -// TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/75-termination/27-upjumping-goto-nonterminating.c b/tests/regression/75-termination/27-upjumping-goto-nonterminating.c index 5ce295872c..e0eb633b11 100644 --- a/tests/regression/75-termination/27-upjumping-goto-nonterminating.c +++ b/tests/regression/75-termination/27-upjumping-goto-nonterminating.c @@ -1,4 +1,4 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/75-termination/28-do-while-continue-terminating.c b/tests/regression/75-termination/28-do-while-continue-terminating.c index 61bd578dcf..a61174d295 100644 --- a/tests/regression/75-termination/28-do-while-continue-terminating.c +++ b/tests/regression/75-termination/28-do-while-continue-terminating.c @@ -1,4 +1,4 @@ -// TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/75-termination/29-do-while-continue-nonterminating.c b/tests/regression/75-termination/29-do-while-continue-nonterminating.c index 34766ab2e7..dd931c012f 100644 --- a/tests/regression/75-termination/29-do-while-continue-nonterminating.c +++ b/tests/regression/75-termination/29-do-while-continue-nonterminating.c @@ -1,4 +1,4 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/75-termination/30-goto-out-of-inner-loop-terminating.c b/tests/regression/75-termination/30-goto-out-of-inner-loop-terminating.c index 5cdadf4396..999ee6d3fd 100644 --- a/tests/regression/75-termination/30-goto-out-of-inner-loop-terminating.c +++ b/tests/regression/75-termination/30-goto-out-of-inner-loop-terminating.c @@ -1,4 +1,4 @@ -// TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/75-termination/31-goto-out-of-inner-loop-nonterminating.c b/tests/regression/75-termination/31-goto-out-of-inner-loop-nonterminating.c index d7ff329396..f9b9275620 100644 --- a/tests/regression/75-termination/31-goto-out-of-inner-loop-nonterminating.c +++ b/tests/regression/75-termination/31-goto-out-of-inner-loop-nonterminating.c @@ -1,4 +1,4 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/75-termination/32-multithread-terminating.c b/tests/regression/75-termination/32-multithread-terminating.c index fbac273776..eb8b796a47 100644 --- a/tests/regression/75-termination/32-multithread-terminating.c +++ b/tests/regression/75-termination/32-multithread-terminating.c @@ -1,4 +1,4 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra // The program terminates but as the termination analysis is meant to not handle multithreaded programs we expect NonTerm here #include #include diff --git a/tests/regression/75-termination/33-multithread-nonterminating.c b/tests/regression/75-termination/33-multithread-nonterminating.c index dad62aa0f4..8a6274c7ab 100644 --- a/tests/regression/75-termination/33-multithread-nonterminating.c +++ b/tests/regression/75-termination/33-multithread-nonterminating.c @@ -1,4 +1,4 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include #include #include diff --git a/tests/regression/75-termination/34-nested-for-loop-nonterminating.c b/tests/regression/75-termination/34-nested-for-loop-nonterminating.c index 24605ad478..2f21f9e996 100644 --- a/tests/regression/75-termination/34-nested-for-loop-nonterminating.c +++ b/tests/regression/75-termination/34-nested-for-loop-nonterminating.c @@ -1,4 +1,4 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/75-termination/35-goto-out-of-inner-loop-with-print-terminating.c b/tests/regression/75-termination/35-goto-out-of-inner-loop-with-print-terminating.c index 3bd6e53d2d..0554a0bc25 100644 --- a/tests/regression/75-termination/35-goto-out-of-inner-loop-with-print-terminating.c +++ b/tests/regression/75-termination/35-goto-out-of-inner-loop-with-print-terminating.c @@ -1,4 +1,4 @@ -// TERM PARAM: --set "ana.activated[+]" termination --set "ana.activated[+]" apron --enable ana.int.interval --set ana.apron.domain polyhedra --set sem.int.signed_overflow assume_none +// SKIP TERM PARAM: --set "ana.activated[+]" termination --set "ana.activated[+]" apron --enable ana.int.interval --set ana.apron.domain polyhedra --set sem.int.signed_overflow assume_none #include int main() diff --git a/tests/regression/75-termination/36-recursion-terminating.c b/tests/regression/75-termination/36-recursion-terminating.c index 7336417c91..179efabeea 100644 --- a/tests/regression/75-termination/36-recursion-terminating.c +++ b/tests/regression/75-termination/36-recursion-terminating.c @@ -1,4 +1,4 @@ -// TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include void recursiveFunction(int n) diff --git a/tests/regression/75-termination/37-recursion-nonterminating.c b/tests/regression/75-termination/37-recursion-nonterminating.c index 21316de9bd..c47fbcdd49 100644 --- a/tests/regression/75-termination/37-recursion-nonterminating.c +++ b/tests/regression/75-termination/37-recursion-nonterminating.c @@ -1,4 +1,4 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --enable ana.context.widen +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --enable ana.context.widen #include void recursiveFunction(int n) // NONTERMFUNDEC termination analysis shall mark fundec of non-terminating function diff --git a/tests/regression/75-termination/38-recursion-nested-terminating.c b/tests/regression/75-termination/38-recursion-nested-terminating.c index bef05eb1a0..a471cfc386 100644 --- a/tests/regression/75-termination/38-recursion-nested-terminating.c +++ b/tests/regression/75-termination/38-recursion-nested-terminating.c @@ -1,4 +1,4 @@ -// TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include void innerRecursiveFunction(int n) diff --git a/tests/regression/75-termination/39-recursion-nested-nonterminating.c b/tests/regression/75-termination/39-recursion-nested-nonterminating.c index b3aa8bf21b..a8d7107442 100644 --- a/tests/regression/75-termination/39-recursion-nested-nonterminating.c +++ b/tests/regression/75-termination/39-recursion-nested-nonterminating.c @@ -1,7 +1,7 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include -void innerRecursiveFunction() // TODO NONTERMFUNDEC termination analysis shall mark fundec of non-terminating function +void innerRecursiveFunction() // TODO NONTERMFUNDEC termination analysis shall mark fundec of non-terminating function but can not as dead code is not analysed { printf("Nested recursive call\n"); diff --git a/tests/regression/75-termination/40-multi-expression-conditions-terminating.c b/tests/regression/75-termination/40-multi-expression-conditions-terminating.c index 8e7b4e273d..80f8c5a1e8 100644 --- a/tests/regression/75-termination/40-multi-expression-conditions-terminating.c +++ b/tests/regression/75-termination/40-multi-expression-conditions-terminating.c @@ -1,4 +1,4 @@ -// TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/75-termination/41-for-continue-terminating.c b/tests/regression/75-termination/41-for-continue-terminating.c index 1d3b96fcf8..d87a705868 100644 --- a/tests/regression/75-termination/41-for-continue-terminating.c +++ b/tests/regression/75-termination/41-for-continue-terminating.c @@ -1,4 +1,4 @@ -// TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/75-termination/42-downjumping-goto-loopless-terminating.c b/tests/regression/75-termination/42-downjumping-goto-loopless-terminating.c index 54bcfdc508..48864883f7 100644 --- a/tests/regression/75-termination/42-downjumping-goto-loopless-terminating.c +++ b/tests/regression/75-termination/42-downjumping-goto-loopless-terminating.c @@ -1,4 +1,4 @@ -// TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { // Currently not able to detect up-jumping loop free gotos diff --git a/tests/regression/75-termination/43-return-from-endless-loop-terminating.c b/tests/regression/75-termination/43-return-from-endless-loop-terminating.c index 06bda24bd7..fb48e1cdbe 100644 --- a/tests/regression/75-termination/43-return-from-endless-loop-terminating.c +++ b/tests/regression/75-termination/43-return-from-endless-loop-terminating.c @@ -1,4 +1,4 @@ -// TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() { diff --git a/tests/regression/75-termination/44-recursion-multiple-functions-terminating.c b/tests/regression/75-termination/44-recursion-multiple-functions-terminating.c index c112c72a73..7f9b63527e 100644 --- a/tests/regression/75-termination/44-recursion-multiple-functions-terminating.c +++ b/tests/regression/75-termination/44-recursion-multiple-functions-terminating.c @@ -1,4 +1,4 @@ -// TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include void functionB(int n); diff --git a/tests/regression/75-termination/45-recursion-multiple-functions-nonterminating.c b/tests/regression/75-termination/45-recursion-multiple-functions-nonterminating.c index 47c13c0dca..be47fde704 100644 --- a/tests/regression/75-termination/45-recursion-multiple-functions-nonterminating.c +++ b/tests/regression/75-termination/45-recursion-multiple-functions-nonterminating.c @@ -1,4 +1,4 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include void functionB(int n); diff --git a/tests/regression/75-termination/46-recursion-different-context-terminating.c b/tests/regression/75-termination/46-recursion-different-context-terminating.c index 4c5dd13035..2fa42f58fc 100644 --- a/tests/regression/75-termination/46-recursion-different-context-terminating.c +++ b/tests/regression/75-termination/46-recursion-different-context-terminating.c @@ -1,4 +1,4 @@ -// TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include void functionC(int n); diff --git a/tests/regression/75-termination/47-recursion-different-context-nonterminating.c b/tests/regression/75-termination/47-recursion-different-context-nonterminating.c index 3216275748..b0e44bce92 100644 --- a/tests/regression/75-termination/47-recursion-different-context-nonterminating.c +++ b/tests/regression/75-termination/47-recursion-different-context-nonterminating.c @@ -1,4 +1,4 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include void functionC(int n); diff --git a/tests/regression/75-termination/48-dynamic-recursion-nonterminating.c b/tests/regression/75-termination/48-dynamic-recursion-nonterminating.c index 066c2f51b1..d54c49fb43 100644 --- a/tests/regression/75-termination/48-dynamic-recursion-nonterminating.c +++ b/tests/regression/75-termination/48-dynamic-recursion-nonterminating.c @@ -1,4 +1,4 @@ -// NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra void troll(void (*f) ()) { f(f); From 3c31cb7846582c9f37e62a32ba04b2ec4a4a0c59 Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Mon, 24 Jul 2023 17:34:55 +0200 Subject: [PATCH 0331/1312] Revert src/analyses/termination.ml --- src/analyses/termination.ml | 169 +++++++++--------------------------- 1 file changed, 43 insertions(+), 126 deletions(-) diff --git a/src/analyses/termination.ml b/src/analyses/termination.ml index 6112eb0eaf..6da9225d3f 100644 --- a/src/analyses/termination.ml +++ b/src/analyses/termination.ml @@ -5,8 +5,6 @@ open GoblintCil open Analyses module M = Messages -(* J: returns if a and b contain a value - if yes: return this x, otherwise nothing *) let (||?) a b = match a,b with Some x,_ | _, Some x -> Some x | _ -> None module TermDomain = struct @@ -14,22 +12,15 @@ module TermDomain = struct end (* some kind of location string suitable for variable names? *) -(* J: returns a string_ "lineNr_columnNr" *) -(* J: for location (10,5) it evaluates to: 10_5*) let show_location_id l = string_of_int l.line ^ "_" ^ string_of_int l.column -(* J: the new variable is created here and inserted into the code - in the code the variable is set to 0 (before the loop) and incremented (after the loop) - is it ever checked if the newly created variable is really new???*) -(* J: ??? Who defines the Loop, what are the variables*) class loopCounterVisitor (fd : fundec) = object(self) inherit nopCilVisitor method! vstmt s = let action s = match s.skind with | Loop (b, loc, eloc, _, _) -> (* insert loop counter variable *) - (* J: for location (10,5) it evaluates to: term10_5*) let name = "term"^show_location_id loc in let typ = intType in (* TODO the type should be the same as the one of the original loop counter *) let v = Cilfacade.create_var (makeLocalVar fd name ~init:(SingleInit zero) typ) in @@ -38,20 +29,14 @@ class loopCounterVisitor (fd : fundec) = object(self) (* increment it every iteration *) let inc_stmt = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in b.bstmts <- inc_stmt :: b.bstmts; - let nb = mkBlock [init_stmt; mkStmt s.skind] in (* J: s.kind = Loop(b, loc, eloc, ...)*) + let nb = mkBlock [init_stmt; mkStmt s.skind] in s.skind <- Block nb; s | _ -> s in ChangeDoChildrenPost (s, action) end -(* J: creates a new hash table with size 13 for loop breaks*) -(* J: int: is a number which is unique in a function*) -(* J: ??? Why 13*) let loopBreaks : (int, location) Hashtbl.t = Hashtbl.create 13 (* break stmt sid -> corresponding loop *) -(* J: if there is some break associated with the loop (?) we add this break to the hash table - key = break.sid - data = location *) class loopBreaksVisitor (fd : fundec) = object(self) inherit nopCilVisitor method! vstmt s = @@ -63,15 +48,10 @@ class loopBreaksVisitor (fd : fundec) = object(self) end (* if the given block contains a goto while_break.* we have the termination condition for a loop *) -(* J: returns binding from loopBreaks hash table associated with a number which is uniquely identifie with the statement - if there is a Goto, otherwise nothing*) -(* J: stmt.sid = A number (>= 0) that is unique in a function. *) -(* J: ???*) let exits = function | { bstmts = [{ skind = Goto (stmt, loc); _ }]; _ } -> Hashtbl.find_option loopBreaks !stmt.sid | _ -> None (* TODO handle return (need to find out what loop we are in) *) -(* J: ???*) let lvals_of_expr = let rec f a = function | Const _ | SizeOf _ | SizeOfStr _ | AlignOf _ | AddrOfLabel _ -> a @@ -81,187 +61,125 @@ let lvals_of_expr = | Question (c,t,e,_) -> f a c @ f a t @ f a e in f [] -(* J: create hash table of size 13 for variables*) let loopVars : (location, lval) Hashtbl.t = Hashtbl.create 13 (* loop location -> lval used for exit *) -(* J: adds the location and left varibale to the loopVars, if one block of the if statement contains a goto block*) class loopVarsVisitor (fd : fundec) = object inherit nopCilVisitor method! vstmt s = let add_exit_cond e loc = match lvals_of_expr e with - (* J: ??? Same as when isArithmeticType Cilfacade.typeOf e*) | [lval] when Cilfacade.typeOf e |> isArithmeticType -> Hashtbl.add loopVars loc lval - (* J : add lval to hash table when the expression on location loc is of arithmetic type*) | _ -> () in (match s.skind with - (* J: map_default f x (Some v) returns f v and map_default f x None returns x.*) - (* J: If there exists a goto statement: call add_exit_cond e (SOME exits tb ||? exits fb) - If e is of arithmetic type: add the location to the loopVars hash table *) | If (e, tb, fb, loc, eloc) -> Option.map_default (add_exit_cond e) () (exits tb ||? exits fb) | _ -> ()); DoChildren end -(* J: ??? visits the expression e and removes all casts from the expression*) let stripCastsDeep e = let v = object inherit nopCilVisitor - (* J: ChangeTo: Replace the expression with the given one*) - (* J: Removes casts from this expression, but ignores casts within other expression constructs. - So we delete the (A) and (B) casts from "(A)(B)(x + (C)y)", but leave the (C) cast.*) method! vexpr e = ChangeTo (stripCasts e) end in visitCilExpr v e (* keep the enclosing loop for statements *) -(* J: store pointer pointing to Nothing for loops*) let cur_loop = ref None (* current loop *) let cur_loop' = ref None (* for nested loops *) -(* J: searches if the variable name___ for the given location is present in the function definition - if not: the variable is created and initialized to 0*) let makeVar fd loc name = - (* J: for location = (10,5) and name = "hi" the id evaluates to: hi__10_5*) let id = name ^ "__" ^ show_location_id loc in - (* J: fd.slocals = Locals of the function definition*) - (* J: returns the first element which is a local and which name is id (for example hi__10_5)*) try List.find (fun v -> v.vname = id) fd.slocals - (* J: when the variable is not found in the function definition then it is newly created and initialized with 0*) with Not_found -> let typ = intType in (* TODO the type should be the same as the one of the original loop counter *) Cilfacade.create_var (makeLocalVar fd id ~init:(SingleInit zero) typ) -(* J: creates an empty function with name "__goblint_assume" and makes a lvalue out of it*) let f_assume = Lval (var (emptyFunction "__goblint_assume").svar) -(* J: creates an empty function with name "__goblint_check" and makes a lvalue out of it*) let f_check = Lval (var (emptyFunction "__goblint_check").svar) -(* J: ??? Loop pointer handling: Why do we fist set cur_loop' = cur_loop and then reverse this operation???*) -(* J: inserts new variable t with init and increment and adds a check if t is bounded*) class loopInstrVisitor (fd : fundec) = object(self) inherit nopCilVisitor method! vstmt s = (* TODO: use Loop eloc? *) (match s.skind with - (* J: if the statement is a loop adjust the loop pointer*) | Loop (_, loc, eloc, _, _) -> - cur_loop' := !cur_loop; (* J: set the nested loop to the current loop*) - cur_loop := Some loc (* J: set the newly found loop as current loop*) + cur_loop' := !cur_loop; + cur_loop := Some loc | _ -> ()); - (* J: adds the variable t to the loop, increments it and checks after the loop if it is bounded - adds also the variables d1 and d2, with incrementation, which affects t - depending on the structure, d1 and d2 are set differently*) let action s = (* first, restore old cur_loop *) - (* J: if the statement is a loop set the nested loop as the current loop*) (match s.skind with | Loop (_, loc, eloc, _, _) -> - cur_loop := !cur_loop'; (* J: current loop is the nested loop*) + cur_loop := !cur_loop'; | _ -> ()); - (* J: true if the current loop variable is set and this variable is bound in the hash table*) let in_loop () = Option.is_some !cur_loop && Hashtbl.mem loopVars (Option.get !cur_loop) in - (* J: t is the new variable which should be bounded - if there is a loop with bounded variable: add code for init and increment (for t, d1, d2) - if there is a loop with unbounded variable: do nothing - if a loop ended: check if t is bounded - if there is an instruction with an assignment: increment d1 and d2 if the assignment affects the loop var - do this recursively for all children*) match s.skind with - (* J: if the statement is a loop, and when the location is bound in the hash table: - - add the creational and initializational code for t, d1, d2 before the loop - - add the incrementational code for t, d1, d2 in the loop - *) | Loop (b, loc, eloc, Some continue, Some break) when Hashtbl.mem loopVars loc -> (* find loop var for current loop *) let x = Hashtbl.find loopVars loc in (* insert loop counter and diff to loop var *) - (* J: create variables t, d1, d2 with names: t___, d1___, d2___*) let t = var @@ makeVar fd loc "t" in let d1 = var @@ makeVar fd loc "d1" in let d2 = var @@ makeVar fd loc "d2" in (* make init stmts *) - (* J: set t=0, d1, d2 = lvalue of x = value in the hashtable associated with the loop variable*) let t_init = mkStmtOneInstr @@ Set (t, zero, loc, eloc) in let d1_init = mkStmtOneInstr @@ Set (d1, Lval x, loc, eloc) in let d2_init = mkStmtOneInstr @@ Set (d2, Lval x, loc, eloc) in (* increment/decrement in every iteration *) - (* J: increment t and d2, decrement d1*) - let t_inc = mkStmtOneInstr @@ Set (t, increm (Lval t) 1, loc, eloc) in (* J: t = t + 1*) - let d1_inc = mkStmtOneInstr @@ Set (d1, increm (Lval d1) (-1), loc, eloc) in (* J: d1 = d1 - 1*) - let d2_inc = mkStmtOneInstr @@ Set (d2, increm (Lval d2) 1 , loc, eloc) in (* J: d2 = d2 + 1*) - let typ = intType in (* J: Note: x is the loop variable*) - let e1 = BinOp (Eq, Lval t, BinOp (MinusA, Lval x, Lval d1, typ), typ) in (* J: t = (x - d1) *) - let e2 = BinOp (Eq, Lval t, BinOp (MinusA, Lval d2, Lval x, typ), typ) in (* J: t = (d2 - x) *) - (* J: make a statement for e1 and e2*) - (* J: ??? what happens with the call*) + let t_inc = mkStmtOneInstr @@ Set (t, increm (Lval t) 1, loc, eloc) in + let d1_inc = mkStmtOneInstr @@ Set (d1, increm (Lval d1) (-1), loc, eloc) in + let d2_inc = mkStmtOneInstr @@ Set (d2, increm (Lval d2) 1 , loc, eloc) in + let typ = intType in + let e1 = BinOp (Eq, Lval t, BinOp (MinusA, Lval x, Lval d1, typ), typ) in + let e2 = BinOp (Eq, Lval t, BinOp (MinusA, Lval d2, Lval x, typ), typ) in let inv1 = mkStmtOneInstr @@ Call (None, f_assume, [e1], loc, eloc) in let inv2 = mkStmtOneInstr @@ Call (None, f_assume, [e2], loc, eloc) in - (match b.bstmts with (* J: we are still in a loop*) + (match b.bstmts with | cont :: cond :: ss -> (* changing succs/preds directly doesn't work -> need to replace whole stmts *) - (* from: cont :: cond :: ss - to: cont :: - cond :: - t = (x - d1) :: t = (d2 - x) :: (??? Is this correct with the call???) - d1 = d1 - 1 :: d2 = d2 + 1 :: t = t + 1 :: - ss - *) - b.bstmts <- cont :: cond :: inv1 :: inv2 :: d1_inc :: d2_inc :: t_inc :: ss; (* J: in the loop*) - let nb = mkBlock [t_init; d1_init; d2_init; mkStmt s.skind] in (* J: make a block out of the init statements before the loop*) + b.bstmts <- cont :: cond :: inv1 :: inv2 :: d1_inc :: d2_inc :: t_inc :: ss; + let nb = mkBlock [t_init; d1_init; d2_init; mkStmt s.skind] in s.skind <- Block nb; | _ -> ()); - s (* J: return s with added code for init and increment*) - (* J: if the variable in the loops is not bounded, it is not possible to continue*) + s | Loop (b, loc, eloc, Some continue, Some break) -> print_endline @@ "WARN: Could not determine loop variable for loop at " ^ CilType.Location.show loc; s - (* J: when the statement is not a loop and a loop ended: - - add t >= 0 in the code after the loop*) | _ when Hashtbl.mem loopBreaks s.sid -> (* after a loop, we check that t is bounded/positive (no overflow happened) *) - let loc = Hashtbl.find loopBreaks s.sid in (* J: holds the current binding of the number of the current function in the hash table*) - let t = var @@ makeVar fd loc "t" in (* J: get the name for variable t = t___*) - let e3 = BinOp (Ge, Lval t, zero, intType) in (* J: t >= 0*) - let inv3 = mkStmtOneInstr @@ Call (None, f_check, [e3], loc, locUnknown) in (* J: make a statement to check t >= 0*) - let nb = mkBlock [mkStmt s.skind; inv3] in (* J: add the statement to the block*) + let loc = Hashtbl.find loopBreaks s.sid in + let t = var @@ makeVar fd loc "t" in + let e3 = BinOp (Ge, Lval t, zero, intType) in + let inv3 = mkStmtOneInstr @@ Call (None, f_check, [e3], loc, locUnknown) in + let nb = mkBlock [mkStmt s.skind; inv3] in s.skind <- Block nb; - s (* J: return s with the added check*) - (* J: If there is an instruction (containing an assignment) and it is in a loop: - If the loop variable and lvalue of Set are structural unequal - Do nothing - else - add an incrementation step for d1 and d2 (depending on the binOp)*) + s | Instr [Set (lval, e, loc, eloc)] when in_loop () -> (* find loop var for current loop *) let cur_loop = Option.get !cur_loop in - let x = Hashtbl.find loopVars cur_loop in (* J: holds the current binding of the number of the current function in the hash table*) - if x <> lval then (* J: x and lval are structural unequal*) + let x = Hashtbl.find loopVars cur_loop in + if x <> lval then s else (* we only care about the loop var *) - (* J: create the variables d1 and d2 with name: d1___, d2___*) let d1 = makeVar fd cur_loop "d1" in let d2 = makeVar fd cur_loop "d2" in (match stripCastsDeep e with - (* J: if x' + e2 or x' - e2 with x' = x and the type arithmetic:- adds incrementation for d1 and d2 to the code*) - (* J: if the loopVar is changed*) | BinOp (op, Lval x', e2, typ) when (op = PlusA || op = MinusA) && x' = x && isArithmeticType typ -> (* TODO x = 1 + x, MinusA! *) (* increase diff by same expr *) - let d1_inc = mkStmtOneInstr @@ Set (var d1, BinOp (PlusA, Lval (var d1), e2, typ), loc, eloc) in (* J: d1 = d1 + e2*) - let d2_inc = mkStmtOneInstr @@ Set (var d2, BinOp (PlusA, Lval (var d2), e2, typ), loc, eloc) in (* J: d2 = d2 + e2*) - let nb = mkBlock [d1_inc; d2_inc; mkStmt s.skind] in (* J: add the incrementation steps at the front*) + let d1_inc = mkStmtOneInstr @@ Set (var d1, BinOp (PlusA, Lval (var d1), e2, typ), loc, eloc) in + let d2_inc = mkStmtOneInstr @@ Set (var d2, BinOp (PlusA, Lval (var d2), e2, typ), loc, eloc) in + let nb = mkBlock [d1_inc; d2_inc; mkStmt s.skind] in s.skind <- Block nb; - s (* J: return s with the added incrementation*) + s | _ -> (* otherwise diff is e - counter *) - let t = makeVar fd cur_loop "t" in (* J: varibale name for t*) - let te = Cilfacade.typeOf e in (* J: type of e*) - let dt1 = mkStmtOneInstr @@ Set (var d1, BinOp (MinusA, Lval x, Lval (var t), te), loc, eloc) in (* J: d1 = x - t*) - let dt2 = mkStmtOneInstr @@ Set (var d2, BinOp (MinusA, Lval x, Lval (var t), te), loc, eloc) in (* J: d2 = x - t*) - let nb = mkBlock [mkStmt s.skind; dt1; dt2] in (* J: add the incrementation steps at the end*) + let t = makeVar fd cur_loop "t" in + let te = Cilfacade.typeOf e in + let dt1 = mkStmtOneInstr @@ Set (var d1, BinOp (MinusA, Lval x, Lval (var t), te), loc, eloc) in + let dt2 = mkStmtOneInstr @@ Set (var d2, BinOp (MinusA, Lval x, Lval (var t), te), loc, eloc) in + let nb = mkBlock [mkStmt s.skind; dt1; dt2] in s.skind <- Block nb; s ) | _ -> s in - (* J: *) - ChangeDoChildrenPost (s, action) (* J: continue with the children*) + ChangeDoChildrenPost (s, action) end @@ -298,25 +216,24 @@ struct (* ctx.local *) (* | _ -> ctx.local *) - let startstate v = D.bot () (* J: start with bottom*) - let threadenter ctx lval f args = [D.bot ()] (* J: enter threads with bottom*) + let startstate v = D.bot () + let threadenter ctx lval f args = [D.bot ()] let exitstate v = D.bot () end class recomputeVisitor (fd : fundec) = object(self) inherit nopCilVisitor method! vfunc fd = - computeCFGInfo fd true; (* J: make the cfg and return a list of statements with global statement number*) - SkipChildren (* J: don't visit children*) + computeCFGInfo fd true; + SkipChildren end let _ = (* Cilfacade.register_preprocess Spec.name (new loopCounterVisitor); *) - Cilfacade.register_preprocess (Spec.name ()) (new loopBreaksVisitor); (* J: fill hash table loopBreaks: containing breaks ?*) - Cilfacade.register_preprocess (Spec.name ()) (new loopVarsVisitor); (* J: fill hash table loopVars: containing varibales identified with loops ?*) - Cilfacade.register_preprocess (Spec.name ()) (new loopInstrVisitor); (* J: inserts new variable with init, increment, and bounded check to code*) - Cilfacade.register_preprocess (Spec.name ()) (new recomputeVisitor); (* J: ??? *) - Hashtbl.clear loopBreaks; (* because the sids are now different *) (* J: delete entries in loopBreaks*) - Cilfacade.register_preprocess (Spec.name ()) (new loopBreaksVisitor); (* J: newly set hash table loopBreaks with goto statements*) - MCP.register_analysis (module Spec : MCPSpec) (* A: register this (termination) analysis withing the master control program, which - collects all active analyses and represents the combination of them as a new, single analysis to FromSpec *) + Cilfacade.register_preprocess (Spec.name ()) (new loopBreaksVisitor); + Cilfacade.register_preprocess (Spec.name ()) (new loopVarsVisitor); + Cilfacade.register_preprocess (Spec.name ()) (new loopInstrVisitor); + Cilfacade.register_preprocess (Spec.name ()) (new recomputeVisitor); + Hashtbl.clear loopBreaks; (* because the sids are now different *) + Cilfacade.register_preprocess (Spec.name ()) (new loopBreaksVisitor); + MCP.register_analysis (module Spec : MCPSpec) From a9c9c7d2f84d10945ec0031f2433048dd0ad0cb8 Mon Sep 17 00:00:00 2001 From: Isidor Zweckstetter Date: Tue, 25 Jul 2023 19:46:48 +0200 Subject: [PATCH 0332/1312] changed initial value of lcv to min int; swaped sequence of increment and special function call, to retain top of intdomain at special function call --- runningGob.sh | 8 ++++---- src/maingoblint.ml | 2 +- src/util/terminationPreprocessing.ml | 10 ++++++---- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/runningGob.sh b/runningGob.sh index 61dbad53b6..e70b34af7c 100755 --- a/runningGob.sh +++ b/runningGob.sh @@ -13,7 +13,7 @@ cfile_loop26="tests/regression/74-loop_termination/26-enter-loop-goto-terminatin cfile_loop28="tests/regression/74-loop_termination/28-do-while-continue-terminating.c" cfile_loop7="tests/regression/74-loop_termination/07-nested-for-loop-terminating.c" cfile_loop5="tests/regression/74-loop_termination/05-for-loop-terminating.c" -cfile_loop1="tests/regression/74-loop_termination/01-simple-loop-terminating.c" +cfile_loop1="tests/regression/75-termination/02-simple-loop-nonterminating.c" cfile_signs="tests/regression/99-tutorials/01-first.c" cfile_goto="tests/incremental/02-cfg-comparison/01-added-return-stmt.c" @@ -22,10 +22,10 @@ cfile_goto="tests/incremental/02-cfg-comparison/01-added-return-stmt.c" #./goblint $cfile_loops $options_apron --html # run analysis, write cil output to file and enable visualization via html -./goblint $cfile_loop30 $options_term --enable justcil > output.txt -./goblint -v $cfile_loop30 $options_term --html +./goblint $cfile_loop1 $options_term --enable justcil > output.txt +./goblint $cfile_loop1 $options_term --html # set up server to see visualizatino -python3 -m http.server --directory result 8080 +python3 -m http.server --directory result 8082 #./goblint --enable dbg.debug tests/regression/55-loop-unrolling/01-simple-cases.c --enable ana.int.interval --set "ana.activated[+]" signs --enable justcil > output.txt diff --git a/src/maingoblint.ml b/src/maingoblint.ml index 371deb989a..f9abd9637b 100644 --- a/src/maingoblint.ml +++ b/src/maingoblint.ml @@ -162,7 +162,7 @@ let check_arguments () = if get_bool "solvers.td3.space" && get_string "solvers.td3.side_widen" = "sides-local" then fail "solvers.td3.space is incompatible with solvers.td3.side_widen = 'sides-local'"; if List.mem "termination" @@ get_string_list "ana.activated" then ( set_list "ana.activated" (GobConfig.get_list "ana.activated" @ [`String ("evermultithreaded")]); - (*set_string "sem.int.signed_overflow" "assume_none";*) + set_string "sem.int.signed_overflow" "assume_none"; warn "termination analysis implicitly activates evermultithreaded analysis and set sem.int.signed_overflow to assume_none" ) diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index 0ff459718a..768dac0df9 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -31,7 +31,9 @@ class loopCounterVisitor lc lg (fd : fundec) = object(self) smaxstmtid = None; sallstmts = []; } in - + + let min_int_exp = Const(CInt(Cilint.shift_left_cilint Cilint.mone_cilint ((bytesSizeOfInt IInt)*8-1), IInt, None)) in + let f_bounded = Lval (var (specialFunction "__goblint_bounded").svar) in let action s = match s.skind with @@ -39,15 +41,15 @@ class loopCounterVisitor lc lg (fd : fundec) = object(self) let name = "term"^show_location_id loc in let typ = Cil.intType in let v = (Cil.makeLocalVar fd name typ) in (*Not tested for incremental mode*) - let init_stmt = mkStmtOneInstr @@ Set (var v, zero, loc, eloc) in + let init_stmt = mkStmtOneInstr @@ Set (var v, min_int_exp, loc, eloc) in let inc_stmt = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in let inc_stmt2 = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in let exit_stmt = mkStmtOneInstr @@ Call (None, f_bounded, [Lval (var v)], loc, locUnknown) in (match b.bstmts with | s :: ss -> (*duplicate increment statement here to fix inconsistencies in nested loops*) - b.bstmts <- inc_stmt :: exit_stmt :: s :: inc_stmt2 :: ss; + b.bstmts <- exit_stmt :: inc_stmt :: s :: inc_stmt2 :: ss; | ss -> - b.bstmts <- inc_stmt :: exit_stmt :: ss; + b.bstmts <- exit_stmt :: inc_stmt :: ss; ); lc := VarToStmt.add (v: varinfo) (s: stmt) !lc; let nb = mkBlock [init_stmt; mkStmt s.skind] in From 782b21c4a65521e0104f789df853e4bf140669f1 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Tue, 25 Jul 2023 21:31:18 +0200 Subject: [PATCH 0333/1312] Fix indentation --- src/util/terminationPreprocessing.ml | 90 ++++++++++++++-------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index 768dac0df9..74aca11824 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -18,50 +18,50 @@ let show_location_id l = string_of_int l.line ^ "_" ^ string_of_int l.column ^ "-file" ^ "_" ^ extract_file_name l.file class loopCounterVisitor lc lg (fd : fundec) = object(self) - inherit nopCilVisitor - - method! vstmt s = + inherit nopCilVisitor - let specialFunction name = - { svar = makeGlobalVar name (TFun(voidType, Some [("exp", intType, [])], false,[])); - smaxid = 0; - slocals = []; - sformals = []; - sbody = mkBlock []; - smaxstmtid = None; - sallstmts = []; - } in + method! vstmt s = - let min_int_exp = Const(CInt(Cilint.shift_left_cilint Cilint.mone_cilint ((bytesSizeOfInt IInt)*8-1), IInt, None)) in - - let f_bounded = Lval (var (specialFunction "__goblint_bounded").svar) in - - let action s = match s.skind with - | Loop (b, loc, eloc, _, _) -> - let name = "term"^show_location_id loc in - let typ = Cil.intType in - let v = (Cil.makeLocalVar fd name typ) in (*Not tested for incremental mode*) - let init_stmt = mkStmtOneInstr @@ Set (var v, min_int_exp, loc, eloc) in - let inc_stmt = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in - let inc_stmt2 = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in - let exit_stmt = mkStmtOneInstr @@ Call (None, f_bounded, [Lval (var v)], loc, locUnknown) in - (match b.bstmts with - | s :: ss -> (*duplicate increment statement here to fix inconsistencies in nested loops*) - b.bstmts <- exit_stmt :: inc_stmt :: s :: inc_stmt2 :: ss; - | ss -> - b.bstmts <- exit_stmt :: inc_stmt :: ss; - ); - lc := VarToStmt.add (v: varinfo) (s: stmt) !lc; - let nb = mkBlock [init_stmt; mkStmt s.skind] in - s.skind <- Block nb; - s - | Goto (sref, l) -> - let goto_jmp_stmt = sref.contents.skind in - let loc_stmt = get_stmtLoc goto_jmp_stmt in - if CilType.Location.compare l loc_stmt >= 0 (*is pos if first loc is greater -> below the second loc*) - then - lg := List.append !lg ([l] : location list); (*problem: the program might not terminate!*) - s - | _ -> s - in ChangeDoChildrenPost (s, action); - end \ No newline at end of file + let specialFunction name = + { svar = makeGlobalVar name (TFun(voidType, Some [("exp", intType, [])], false,[])); + smaxid = 0; + slocals = []; + sformals = []; + sbody = mkBlock []; + smaxstmtid = None; + sallstmts = []; + } in + + let min_int_exp = Const(CInt(Cilint.shift_left_cilint Cilint.mone_cilint ((bytesSizeOfInt IInt)*8-1), IInt, None)) in + + let f_bounded = Lval (var (specialFunction "__goblint_bounded").svar) in + + let action s = match s.skind with + | Loop (b, loc, eloc, _, _) -> + let name = "term"^show_location_id loc in + let typ = Cil.intType in + let v = (Cil.makeLocalVar fd name typ) in (*Not tested for incremental mode*) + let init_stmt = mkStmtOneInstr @@ Set (var v, min_int_exp, loc, eloc) in + let inc_stmt = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in + let inc_stmt2 = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in + let exit_stmt = mkStmtOneInstr @@ Call (None, f_bounded, [Lval (var v)], loc, locUnknown) in + (match b.bstmts with + | s :: ss -> (*duplicate increment statement here to fix inconsistencies in nested loops*) + b.bstmts <- exit_stmt :: inc_stmt :: s :: inc_stmt2 :: ss; + | ss -> + b.bstmts <- exit_stmt :: inc_stmt :: ss; + ); + lc := VarToStmt.add (v: varinfo) (s: stmt) !lc; + let nb = mkBlock [init_stmt; mkStmt s.skind] in + s.skind <- Block nb; + s + | Goto (sref, l) -> + let goto_jmp_stmt = sref.contents.skind in + let loc_stmt = get_stmtLoc goto_jmp_stmt in + if CilType.Location.compare l loc_stmt >= 0 (*is pos if first loc is greater -> below the second loc*) + then + lg := List.append !lg ([l] : location list); (*problem: the program might not terminate!*) + s + | _ -> s + in ChangeDoChildrenPost (s, action); +end From 5d5afffbe85250e3542c8c7e5adf82ed7427ff9d Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 26 Jul 2023 13:46:28 +0300 Subject: [PATCH 0334/1312] Fix indentation --- src/analyses/base.ml | 8 ++++---- src/cdomains/valueDomain.ml | 20 ++++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 3a231ea396..0e766401d9 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2348,10 +2348,10 @@ struct set ~ctx ~t_override:t ask ctx.global ctx.local (AD.of_var !longjmp_return) t rv (* Not raising Deadcode here, deadcode is raised at a higher level! *) | Rand, _ -> begin match lv with - | Some x -> - let result:value = (Int (ID.starting IInt Z.zero)) in - set ~ctx (Analyses.ask_of_ctx ctx) gs st (eval_lv (Analyses.ask_of_ctx ctx) ctx.global st x) (Cilfacade.typeOfLval x) result - | None -> st + | Some x -> + let result:value = (Int (ID.starting IInt Z.zero)) in + set ~ctx (Analyses.ask_of_ctx ctx) gs st (eval_lv (Analyses.ask_of_ctx ctx) ctx.global st x) (Cilfacade.typeOfLval x) result + | None -> st end | _, _ -> let st = diff --git a/src/cdomains/valueDomain.ml b/src/cdomains/valueDomain.ml index 4e40f3a287..11763ce5c7 100644 --- a/src/cdomains/valueDomain.ml +++ b/src/cdomains/valueDomain.ml @@ -963,16 +963,16 @@ struct Top end | JmpBuf _, _ -> - (* hack for jmp_buf variables *) - begin match value with - | JmpBuf t -> value (* if actually assigning jmpbuf, use value *) - | Blob(Bot, _, _) -> Bot (* TODO: Stopgap for malloced jmp_bufs, there is something fundamentally flawed somewhere *) - | _ -> - if !AnalysisState.global_initialization then - JmpBuf (JmpBufs.Bufs.empty (), false) (* if assigning global init, use empty set instead *) - else - Top - end + (* hack for jmp_buf variables *) + begin match value with + | JmpBuf t -> value (* if actually assigning jmpbuf, use value *) + | Blob(Bot, _, _) -> Bot (* TODO: Stopgap for malloced jmp_bufs, there is something fundamentally flawed somewhere *) + | _ -> + if !AnalysisState.global_initialization then + JmpBuf (JmpBufs.Bufs.empty (), false) (* if assigning global init, use empty set instead *) + else + Top + end | _ -> let result = match offs with From 9f1e5f96eb7f95fbc3e13c693e7874bf4b21db6b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 26 Jul 2023 13:47:57 +0300 Subject: [PATCH 0335/1312] Fix unused-rec-flag warning in ValueDomain --- src/cdomains/valueDomain.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cdomains/valueDomain.ml b/src/cdomains/valueDomain.ml index 11763ce5c7..20c4f3bf21 100644 --- a/src/cdomains/valueDomain.ml +++ b/src/cdomains/valueDomain.ml @@ -564,7 +564,7 @@ struct warn_type "join" x y; Top - let rec widen x y = + let widen x y = match (x,y) with | (Top, _) -> Top | (_, Top) -> Top @@ -582,7 +582,7 @@ struct | (Struct x, Struct y) -> Struct (Structs.widen x y) | (Union x, Union y) -> Union (Unions.widen x y) | (Array x, Array y) -> Array (CArrays.widen x y) - | (Blob x, Blob y) -> Blob (Blobs.widen x y) + | (Blob x, Blob y) -> Blob (Blobs.widen x y) (* TODO: why no blob special cases like in join? *) | (Thread x, Thread y) -> Thread (Threads.widen x y) | (Int x, Thread y) | (Thread y, Int x) -> From c53f0b84d29dc077d9eeddf354dd10acb572b826 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Wed, 26 Jul 2023 14:38:32 +0200 Subject: [PATCH 0336/1312] Polish comments --- src/analyses/loopTermination.ml | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/analyses/loopTermination.ml b/src/analyses/loopTermination.ml index afd86fc483..89976b7627 100644 --- a/src/analyses/loopTermination.ml +++ b/src/analyses/loopTermination.ml @@ -4,20 +4,20 @@ open Analyses open GoblintCil open TerminationPreprocessing -(** Stores the result of the query if the program is single threaded or not - since finalize does not has ctx as an argument*) +(** Stores the result of the query whether the program is single threaded or not + since finalize does not have access to ctx. *) let single_thread : bool ref = ref false (** Contains all loop counter variables (varinfo) and maps them to their corresponding loop statement. *) let loop_counters : stmt VarToStmt.t ref = ref VarToStmt.empty -(** Contains the locations of the upjumping gotos *) +(** Contains the locations of the upjumping gotos. *) let upjumping_gotos : location list ref = ref [] let no_upjumping_gotos () = upjumping_gotos.contents = [] -(** Checks whether a variable can be bounded *) +(** Checks whether a variable can be bounded. *) let check_bounded ctx varinfo = let open IntDomain.IntDomTuple in let exp = Lval (Var varinfo, NoOffset) in @@ -35,7 +35,6 @@ module Statements = Lattice.Flat (CilType.Stmt) (Printable.DefaultNames) module Spec : Analyses.MCPSpec = struct - (** Provides some default implementations *) include Analyses.IdentitySpec let name () = "termination" @@ -52,8 +51,8 @@ struct let startstate _ = () let exitstate = startstate + (** Warnings for detected possible non-termination *) let finalize () = - (* Warning for detected possible non-termination *) (* Upjumping gotos *) if not (no_upjumping_gotos ()) then ( List.iter @@ -71,6 +70,8 @@ struct M.warn ~category:NonTerminating "The program might not terminate! (Multithreaded)\n" ) + (** Recognizes a call of [__goblint_bounded] to check the EvalInt of the + * respective loop counter variable at that position. *) let special ctx (lval : lval option) (f : varinfo) (arglist : exp list) = if !AnalysisState.postsolving then match f.vname, arglist with @@ -95,14 +96,13 @@ struct | _ -> () else () - (** Checks whether a new thread was spawned some time. We want to discard - * any knowledge about termination then (see query function) *) + (** Checks whether a new thread was spawned some time. We want to always + * assume non-termination then (see query function). *) let must_be_single_threaded_since_start ctx = let single_threaded = not (ctx.ask Queries.IsEverMultiThreaded) in single_thread := single_threaded; single_threaded - (** Provides information to Goblint *) let query ctx (type a) (q: a Queries.t): a Queries.result = match q with | Queries.MustTermLoop loop_statement -> @@ -124,7 +124,5 @@ struct end let () = - (* Register the preprocessing *) Cilfacade.register_preprocess_cil (Spec.name ()) (new loopCounterVisitor loop_counters upjumping_gotos); - (* Register this analysis within the master control program *) MCP.register_analysis (module Spec : MCPSpec) From c52cf476cf170a9b1374adf03ef31283e1565435 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Wed, 26 Jul 2023 14:58:27 +0200 Subject: [PATCH 0337/1312] Remove runningGob.sh --- runningGob.sh | 31 ------------------------------- 1 file changed, 31 deletions(-) delete mode 100755 runningGob.sh diff --git a/runningGob.sh b/runningGob.sh deleted file mode 100755 index e70b34af7c..0000000000 --- a/runningGob.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash -make -make install -clear - -# set options and file for apron execution -options_apron="--set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --enable warn.debug" #note: preprocessing first needs to be added to apron -options_signs="--set "ana.activated[+]" signs --enable warn.debug" -options_term="--set "ana.activated[+]" termination --enable warn.debug --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra" - -cfile_loop30="tests/regression/74-loop_termination/30-goto-out-of-inner-loop-terminating.c" -cfile_loop26="tests/regression/74-loop_termination/26-enter-loop-goto-terminating.c" -cfile_loop28="tests/regression/74-loop_termination/28-do-while-continue-terminating.c" -cfile_loop7="tests/regression/74-loop_termination/07-nested-for-loop-terminating.c" -cfile_loop5="tests/regression/74-loop_termination/05-for-loop-terminating.c" -cfile_loop1="tests/regression/75-termination/02-simple-loop-nonterminating.c" -cfile_signs="tests/regression/99-tutorials/01-first.c" -cfile_goto="tests/incremental/02-cfg-comparison/01-added-return-stmt.c" - -# run analysis, write cil output to file and enable visualization via html -#./goblint $cfile_loops $options_apron --enable justcil > output.txt -#./goblint $cfile_loops $options_apron --html - -# run analysis, write cil output to file and enable visualization via html -./goblint $cfile_loop1 $options_term --enable justcil > output.txt -./goblint $cfile_loop1 $options_term --html - -# set up server to see visualizatino -python3 -m http.server --directory result 8082 -#./goblint --enable dbg.debug tests/regression/55-loop-unrolling/01-simple-cases.c --enable ana.int.interval --set "ana.activated[+]" signs --enable justcil > output.txt - From 867e6ca5e408a973a2c5150395132deb032b8d24 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Wed, 26 Jul 2023 16:33:53 +0200 Subject: [PATCH 0338/1312] Restore indentation; remove unused include Printf NOTE: This commit makes the indendation of several files wrong again on purpose! --- src/cdomains/intDomain.mli | 10 +-- src/framework/analyses.ml | 2 + src/framework/constraints.ml | 12 ++-- src/framework/control.ml | 118 +++++++++++++++++------------------ src/util/cilCfg.ml | 5 +- src/witness/witness.ml | 44 ++++++------- 6 files changed, 95 insertions(+), 96 deletions(-) diff --git a/src/cdomains/intDomain.mli b/src/cdomains/intDomain.mli index c7b59e4c23..a853c8acca 100644 --- a/src/cdomains/intDomain.mli +++ b/src/cdomains/intDomain.mli @@ -232,7 +232,7 @@ sig val invariant: Cil.exp -> t -> Invariant.t end (** Interface of IntDomain implementations that do not take ikinds for arithmetic operations yet. - TODO: Should be ported to S in the future. *) + TODO: Should be ported to S in the future. *) module type S = sig @@ -412,10 +412,10 @@ module IntervalSetFunctor(Ints_t : IntOps.IntOps): SOverflow with type int_t = I module Interval32 :Y with (* type t = (IntOps.Int64Ops.t * IntOps.Int64Ops.t) option and *) type int_t = IntOps.Int64Ops.t module BigInt: -sig - include Printable.S with type t = Z.t (* TODO: why doesn't this have a more useful signature like IntOps.BigIntOps? *) - val cast_to: Cil.ikind -> Z.t -> Z.t -end + sig + include Printable.S with type t = Z.t (* TODO: why doesn't this have a more useful signature like IntOps.BigIntOps? *) + val cast_to: Cil.ikind -> Z.t -> Z.t + end module Interval : SOverflow with type int_t = IntOps.BigIntOps.t diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 23b75a4097..dd57f40c70 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -42,6 +42,7 @@ struct let var_id = Node.show_id end + module VarF (LD: Printable.S) = struct type t = Node.t * LD.t [@@deriving eq, ord, hash] @@ -118,6 +119,7 @@ struct | x -> BatPrintf.fprintf f "%a" printXml x end + exception Deadcode (** [Dom (D)] produces D lifted where bottom means dead-code *) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 6883cc72d9..5e99720364 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -6,7 +6,6 @@ open GoblintCil open MyCFG open Analyses open GobConfig -include Printf module M = Messages @@ -1127,7 +1126,7 @@ struct | `Lifted2 d -> LH.replace l' x d (* | `Bot -> () *) (* Since Verify2 is broken and only checks existing keys, add it with local bottom value. - This works around some cases, where Verify2 would not detect a problem due to completely missing variable. *) + This works around some cases, where Verify2 would not detect a problem due to completely missing variable. *) | `Bot -> LH.replace l' x (S.D.bot ()) | `Top -> failwith "GlobConstrSolFromEqConstrSolBase.split_vars: local variable has top value" | `Lifted1 _ -> failwith "GlobConstrSolFromEqConstrSolBase.split_vars: local variable has global value" @@ -1457,7 +1456,6 @@ struct let event ctx e octx = S.event (conv ctx) e (conv octx) end - module LongjmpLifter (S: Spec): Spec = struct include S @@ -1920,10 +1918,10 @@ struct in PP.iter f h1; (* let k1 = Set.of_enum @@ PP.keys h1 in - let k2 = Set.of_enum @@ PP.keys h2 in - let o1 = Set.cardinal @@ Set.diff k1 k2 in - let o2 = Set.cardinal @@ Set.diff k2 k1 in - Printf.printf "locals: \tequal = %d\tleft = %d[%d]\tright = %d[%d]\tincomparable = %d\n" !eq !le o1 !gr o2 !uk *) + let k2 = Set.of_enum @@ PP.keys h2 in + let o1 = Set.cardinal @@ Set.diff k1 k2 in + let o2 = Set.cardinal @@ Set.diff k2 k1 in + Printf.printf "locals: \tequal = %d\tleft = %d[%d]\tright = %d[%d]\tincomparable = %d\n" !eq !le o1 !gr o2 !uk *) Printf.printf "locals: \tequal = %d\tleft = %d\tright = %d\tincomparable = %d\n" !eq !le !gr !uk let compare_locals_ctx h1 h2 = diff --git a/src/framework/control.ml b/src/framework/control.ml index b98ec20e8d..69cdf1bdac 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -20,25 +20,25 @@ let spec_module: (module Spec) Lazy.t = lazy ( (* apply functor F on module X if opt is true *) let lift opt (module F : S2S) (module X : Spec) = (module (val if opt then (module F (X)) else (module X) : Spec) : Spec) in let module S1 = (val - (module MCP.MCP2 : Spec) - |> lift true (module WidenContextLifterSide) (* option checked in functor *) - (* hashcons before witness to reduce duplicates, because witness re-uses contexts in domain and requires tag for PathSensitive3 *) - |> lift (get_bool "ana.opt.hashcons" || arg_enabled) (module HashconsContextLifter) - |> lift arg_enabled (module HashconsLifter) - |> lift arg_enabled (module WitnessConstraints.PathSensitive3) - |> lift (not arg_enabled) (module PathSensitive2) - |> lift (get_bool "ana.dead-code.branches") (module DeadBranchLifter) - |> lift true (module DeadCodeLifter) - |> lift (get_bool "dbg.slice.on") (module LevelSliceLifter) - |> lift (get_int "dbg.limit.widen" > 0) (module LimitLifter) - |> lift (get_bool "ana.opt.equal" && not (get_bool "ana.opt.hashcons")) (module OptEqual) - |> lift (get_bool "ana.opt.hashcons") (module HashconsLifter) - (* Widening tokens must be outside of hashcons, because widening token domain ignores token sets for identity, so hashcons doesn't allow adding tokens. - Also must be outside of deadcode, because deadcode splits (like mutex lock event) don't pass on tokens. *) - |> lift (get_bool "ana.widen.tokens") (module WideningTokens.Lifter) - |> lift true (module LongjmpLifter) - |> lift termination_enabled (module RecursionTermLifter) (* Always activate the recursion termination analysis, when the loop termination analysis is activated*) - ) in + (module MCP.MCP2 : Spec) + |> lift true (module WidenContextLifterSide) (* option checked in functor *) + (* hashcons before witness to reduce duplicates, because witness re-uses contexts in domain and requires tag for PathSensitive3 *) + |> lift (get_bool "ana.opt.hashcons" || arg_enabled) (module HashconsContextLifter) + |> lift arg_enabled (module HashconsLifter) + |> lift arg_enabled (module WitnessConstraints.PathSensitive3) + |> lift (not arg_enabled) (module PathSensitive2) + |> lift (get_bool "ana.dead-code.branches") (module DeadBranchLifter) + |> lift true (module DeadCodeLifter) + |> lift (get_bool "dbg.slice.on") (module LevelSliceLifter) + |> lift (get_int "dbg.limit.widen" > 0) (module LimitLifter) + |> lift (get_bool "ana.opt.equal" && not (get_bool "ana.opt.hashcons")) (module OptEqual) + |> lift (get_bool "ana.opt.hashcons") (module HashconsLifter) + (* Widening tokens must be outside of hashcons, because widening token domain ignores token sets for identity, so hashcons doesn't allow adding tokens. + Also must be outside of deadcode, because deadcode splits (like mutex lock event) don't pass on tokens. *) + |> lift (get_bool "ana.widen.tokens") (module WideningTokens.Lifter) + |> lift true (module LongjmpLifter) + |> lift termination_enabled (module RecursionTermLifter) (* Always activate the recursion termination analysis, when the loop termination analysis is activated*) + ) in GobConfig.building_spec := false; ControlSpecC.control_spec_c := (module S1.C); (module S1) @@ -303,10 +303,10 @@ struct | MyCFG.Assign (lval,exp) -> if M.tracing then M.trace "global_inits" "Assign %a = %a\n" d_lval lval d_exp exp; (match lval, exp with - | (Var v,o), (AddrOf (Var f,NoOffset)) - when v.vstorage <> Static && isFunctionType f.vtype -> - (try funs := Cilfacade.find_varinfo_fundec f :: !funs with Not_found -> ()) - | _ -> () + | (Var v,o), (AddrOf (Var f,NoOffset)) + when v.vstorage <> Static && isFunctionType f.vtype -> + (try funs := Cilfacade.find_varinfo_fundec f :: !funs with Not_found -> ()) + | _ -> () ); let res = Spec.assign {ctx with local = st} lval exp in (* Needed for privatizations (e.g. None) that do not side immediately *) @@ -530,9 +530,9 @@ struct GobSys.mkdir_or_exists save_run; GobConfig.write_file config; let module Meta = struct - type t = { command : string; version: string; timestamp : float; localtime : string } [@@deriving to_yojson] - let json = to_yojson { command = GobSys.command_line; version = Version.goblint; timestamp = Unix.time (); localtime = GobUnix.localtime () } - end + type t = { command : string; version: string; timestamp : float; localtime : string } [@@deriving to_yojson] + let json = to_yojson { command = GobSys.command_line; version = Version.goblint; timestamp = Unix.time (); localtime = GobUnix.localtime () } + end in (* Yojson.Safe.to_file meta Meta.json; *) Yojson.Safe.pretty_to_channel (Stdlib.open_out (Fpath.to_string meta)) Meta.json; (* the above is compact, this is pretty-printed *) @@ -584,10 +584,10 @@ struct in let print_and_calculate_uncalled = function | GFun (fn, loc) when is_bad_uncalled fn.svar loc-> - let cnt = Cilfacade.countLoc fn in - uncalled_dead := !uncalled_dead + cnt; - if get_bool "ana.dead-code.functions" then - M.warn ~loc:(CilLocation loc) ~category:Deadcode "Function '%a' is uncalled: %d LLoC" CilType.Fundec.pretty fn cnt (* CilLocation is fine because always printed from scratch *) + let cnt = Cilfacade.countLoc fn in + uncalled_dead := !uncalled_dead + cnt; + if get_bool "ana.dead-code.functions" then + M.warn ~loc:(CilLocation loc) ~category:Deadcode "Function '%a' is uncalled: %d LLoC" CilType.Fundec.pretty fn cnt (* CilLocation is fine because always printed from scratch *) | _ -> () in List.iter print_and_calculate_uncalled file.globals; @@ -619,35 +619,35 @@ struct NodeH.modify_opt node join by_node; ); by_loc, by_node - in - - let ask ?(node = MyCFG.dummy_node) loc = - let f (type a) (q : a Queries.t) : a = - match Hashtbl.find_option joined_by_loc loc with - | None -> Queries.Result.bot q - | Some local -> Query.ask_local_node gh node local q - in - ({ f } : Queries.ask) - in - - (* A node is dead when its abstract value is bottom in all contexts; - it holds that: bottom in all contexts iff. bottom in the join of all contexts. - Therefore, we just answer whether the (stored) join is bottom. *) - let must_be_dead node = - NodeH.find_option joined_by_node node - (* nodes that didn't make it into the result are definitely dead (hence for_all) *) - |> GobOption.for_all Spec.D.is_bot - in - - let must_be_uncalled fd = not @@ BatSet.Int.mem fd.svar.vid calledFuns in - - let skipped_statements from_node edge to_node = - CfgTools.CfgEdgeH.find_default skippedByEdge (from_node, edge, to_node) [] - in - - Transform.run_transformations file active_transformations - { ask ; must_be_dead ; must_be_uncalled ; - cfg_forward = Cfg.next ; cfg_backward = Cfg.prev ; skipped_statements }; + in + + let ask ?(node = MyCFG.dummy_node) loc = + let f (type a) (q : a Queries.t) : a = + match Hashtbl.find_option joined_by_loc loc with + | None -> Queries.Result.bot q + | Some local -> Query.ask_local_node gh node local q + in + ({ f } : Queries.ask) + in + + (* A node is dead when its abstract value is bottom in all contexts; + it holds that: bottom in all contexts iff. bottom in the join of all contexts. + Therefore, we just answer whether the (stored) join is bottom. *) + let must_be_dead node = + NodeH.find_option joined_by_node node + (* nodes that didn't make it into the result are definitely dead (hence for_all) *) + |> GobOption.for_all Spec.D.is_bot + in + + let must_be_uncalled fd = not @@ BatSet.Int.mem fd.svar.vid calledFuns in + + let skipped_statements from_node edge to_node = + CfgTools.CfgEdgeH.find_default skippedByEdge (from_node, edge, to_node) [] + in + + Transform.run_transformations file active_transformations + { ask ; must_be_dead ; must_be_uncalled ; + cfg_forward = Cfg.next ; cfg_backward = Cfg.prev ; skipped_statements }; ); lh, gh diff --git a/src/util/cilCfg.ml b/src/util/cilCfg.ml index 0d4d5f3a1e..0d1f06da01 100644 --- a/src/util/cilCfg.ml +++ b/src/util/cilCfg.ml @@ -42,7 +42,6 @@ let loopCount file = let createCFG (fileAST: file) = - (* The analyzer keeps values only for blocks. So if you want a value for every program point, each instruction *) (* needs to be in its own block. end_basic_blocks does that. *) (* After adding support for VLAs, there are new VarDecl instructions at the point where a variable was declared and *) @@ -59,7 +58,7 @@ let createCFG (fileAST: file) = * See https://github.com/goblint/cil/issues/31#issuecomment-824939793. *) let loops = loopCount fileAST in - + iterGlobals fileAST (fun glob -> match glob with | GFun(fd,_) -> @@ -70,5 +69,5 @@ let createCFG (fileAST: file) = | _ -> () ); - if get_bool "dbg.run_cil_check" then assert (Check.checkFile [] fileAST); + if get_bool "dbg.run_cil_check" then assert (Check.checkFile [] fileAST); Cilfacade.do_preprocess fileAST diff --git a/src/witness/witness.ml b/src/witness/witness.ml index 94ffea0a0a..ae6b80b3cb 100644 --- a/src/witness/witness.ml +++ b/src/witness/witness.ml @@ -23,7 +23,7 @@ let write_file filename (module Task:Task) (module TaskResult:WitnessTaskResult) let module IsInteresting = struct (* type node = N.t - type edge = TaskResult.Arg.Edge.t *) + type edge = TaskResult.Arg.Edge.t *) let minwitness = get_bool "witness.minimize" let is_interesting_real from_node edge to_node = (* TODO: don't duplicate this logic with write_node, write_edge *) @@ -59,11 +59,11 @@ let write_file filename (module Task:Task) (module TaskResult:WitnessTaskResult) let module GML = XmlGraphMlWriter in let module GML = (val match get_string "witness.id" with - | "node" -> - (module ArgNodeGraphMlWriter (N) (GML) : GraphMlWriter with type node = N.t) - | "enumerate" -> - (module EnumerateNodeGraphMlWriter (N) (GML)) - | _ -> failwith "witness.id: illegal value" + | "node" -> + (module ArgNodeGraphMlWriter (N) (GML) : GraphMlWriter with type node = N.t) + | "enumerate" -> + (module EnumerateNodeGraphMlWriter (N) (GML)) + | _ -> failwith "witness.id: illegal value" ) in let module GML = DeDupGraphMlWriter (N) (GML) in @@ -107,16 +107,16 @@ let write_file filename (module Task:Task) (module TaskResult:WitnessTaskResult) GML.write_key g "edge" "goblintLine" "string" None; (* TODO: remove *) (* GML.write_key g "edge" "enterFunction2" "string" None; - GML.write_key g "edge" "returnFromFunction2" "string" None; *) + GML.write_key g "edge" "returnFromFunction2" "string" None; *) GML.start_graph g; GML.write_metadata g "witness-type" ( - match TaskResult.result with - | Result.True -> "correctness_witness" - | Result.False _ -> "violation_witness" - | Result.Unknown -> "unknown_witness" - ); + match TaskResult.result with + | Result.True -> "correctness_witness" + | Result.False _ -> "violation_witness" + | Result.Unknown -> "unknown_witness" + ); GML.write_metadata g "sourcecodelang" "C"; GML.write_metadata g "producer" (Printf.sprintf "Goblint (%s)" Version.goblint); GML.write_metadata g "specification" (Svcomp.Specification.to_string Task.specification); @@ -141,7 +141,7 @@ let write_file filename (module Task:Task) (module TaskResult:WitnessTaskResult) | Statement _, `Lifted i -> let i = InvariantCil.exp_replace_original_name i in [("invariant", CilType.Exp.show i); - ("invariant.scope", (Node.find_fundec cfgnode).svar.vname)] + ("invariant.scope", (Node.find_fundec cfgnode).svar.vname)] | _ -> (* ignore entry and return invariants, variables of wrong scopes *) (* TODO: don't? fix scopes? *) @@ -150,10 +150,10 @@ let write_file filename (module Task:Task) (module TaskResult:WitnessTaskResult) [] end; (* begin match cfgnode with - | Statement s -> + | Statement s -> [("sourcecode", GobPretty.sprint Basetype.CilStmt.pretty s)] (* TODO: sourcecode not official? especially on node? *) - | _ -> [] - end; *) + | _ -> [] + end; *) (* violation actually only allowed in violation witness *) (* maybe should appear on from_node of entry edge instead *) begin if TaskResult.is_violation node then @@ -170,7 +170,7 @@ let write_file filename (module Task:Task) (module TaskResult:WitnessTaskResult) | Statement stmt -> Printf.sprintf "s%d" stmt.sid | Function f -> Printf.sprintf "ret%d%s" f.vid f.vname | FunctionEntry f -> Printf.sprintf "fun%d%s" f.vid f.vname - )] *) + )] *) (* [("goblintNode", N.to_string node)] *) ]) in @@ -213,9 +213,9 @@ let write_file filename (module Task:Task) (module TaskResult:WitnessTaskResult) (* enter and return on other side of nodes, more correct loc (startline) but had some scope problem? *) (* | MyARG.CFGEdge (Entry f) -> - [("enterFunction2", f.svar.vname)] - | MyARG.CFGEdge (Ret (_, f)) -> - [("returnFromFunction2", f.svar.vname)] *) + [("enterFunction2", f.svar.vname)] + | MyARG.CFGEdge (Ret (_, f)) -> + [("returnFromFunction2", f.svar.vname)] *) | _ -> [] end; [("goblintEdge", Arg.Edge.to_string edge)] @@ -394,12 +394,12 @@ struct struct let path = observer_path end - ) + ) in MCP.register_analysis (module Spec); (* TODO: don't modify JSON but have ref vars for these instead *) (* GobConfig.set_list "ana.activated" (Json.Build.string (Spec.name ()) :: GobConfig.get_list "ana.activated"); - GobConfig.set_list "ana.path_sens" (Json.Build.string (Spec.name ()) :: GobConfig.get_list "ana.path_sens"); *) + GobConfig.set_list "ana.path_sens" (Json.Build.string (Spec.name ()) :: GobConfig.get_list "ana.path_sens"); *) (* TODO: don't append to end; currently done to get observer order to be nice *) GobConfig.set_list "ana.activated" (GobConfig.get_list "ana.activated" @ [`String (Spec.name ())]); GobConfig.set_list "ana.path_sens" (GobConfig.get_list "ana.path_sens" @ [`String (Spec.name ())]); From 0f94aaa98f2b463963f113a17e1a5e1fdf208ff0 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Wed, 26 Jul 2023 16:41:58 +0200 Subject: [PATCH 0339/1312] More indentation restoring --- src/util/cilCfg.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/cilCfg.ml b/src/util/cilCfg.ml index 0d1f06da01..72143e97d7 100644 --- a/src/util/cilCfg.ml +++ b/src/util/cilCfg.ml @@ -68,6 +68,6 @@ let createCFG (fileAST: file) = computeCFGInfo fd true | _ -> () ); - if get_bool "dbg.run_cil_check" then assert (Check.checkFile [] fileAST); + Cilfacade.do_preprocess fileAST From 4c093726b52ba1e3e0966ec5cded850d36bc87b6 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 26 Jul 2023 16:46:50 +0200 Subject: [PATCH 0340/1312] Add OOB memory access regression case with a loop --- tests/regression/73-mem-oob/03-oob-loop.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 tests/regression/73-mem-oob/03-oob-loop.c diff --git a/tests/regression/73-mem-oob/03-oob-loop.c b/tests/regression/73-mem-oob/03-oob-loop.c new file mode 100644 index 0000000000..5e0e08c381 --- /dev/null +++ b/tests/regression/73-mem-oob/03-oob-loop.c @@ -0,0 +1,16 @@ +// PARAM: --set ana.activated[+] memOutOfBounds +#include +#include + +int main(int argc, char const *argv[]) { + char *ptr = malloc(5 * sizeof(char)); + + for (int i = 0; i < 10; i++) { + ptr++; + } + + printf("%s", *ptr); //WARN + free(ptr); //WARN + + return 0; +} From f4fd0c22d3bf65e16bfbfcb15d6063bbd5ed0af3 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Wed, 26 Jul 2023 16:48:30 +0200 Subject: [PATCH 0341/1312] Fix a typo --- src/framework/analysisState.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/analysisState.ml b/src/framework/analysisState.ml index c2d977af9c..913fe88a4e 100644 --- a/src/framework/analysisState.ml +++ b/src/framework/analysisState.ml @@ -7,7 +7,7 @@ let should_warn = ref false (** Whether signed overflow or underflow happened *) let svcomp_may_overflow = ref false -(** Whether the termination analysis detectes the program as non-terminating**) +(** Whether the termination analysis detects the program as non-terminating *) let svcomp_may_not_terminate = ref false (** A hack to see if we are currently doing global inits *) From a57112b0316c9000c35ee639198a299e5059fb7c Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 26 Jul 2023 16:54:03 +0200 Subject: [PATCH 0342/1312] Add regression test case with a joined thread --- ...13-multi-threaded-uaf-with-joined-thread.c | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 tests/regression/74-use_after_free/13-multi-threaded-uaf-with-joined-thread.c diff --git a/tests/regression/74-use_after_free/13-multi-threaded-uaf-with-joined-thread.c b/tests/regression/74-use_after_free/13-multi-threaded-uaf-with-joined-thread.c new file mode 100644 index 0000000000..140e66eff8 --- /dev/null +++ b/tests/regression/74-use_after_free/13-multi-threaded-uaf-with-joined-thread.c @@ -0,0 +1,33 @@ +//PARAM: --set ana.activated[+] useAfterFree +#include +#include +#include + +int* gptr; + +// Mutex to ensure we don't get race warnings, but the UAF warnings we actually care about +pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; + +void *t_use(void* p) { + pthread_mutex_lock(&mtx); + *gptr = 0; //NOWARN + pthread_mutex_unlock(&mtx); +} + +int main() { + gptr = malloc(sizeof(int)); + *gptr = 42; + + pthread_t using_thread; + pthread_create(&using_thread, NULL, t_use, NULL); + + // Join using_thread before freeing gptr in the main thread + pthread_join(using_thread, NULL); + + pthread_mutex_lock(&mtx); + *gptr = 43; //NOWARN + free(gptr); //WARN + pthread_mutex_unlock(&mtx); + + return 0; +} \ No newline at end of file From 0b3bfc99d1c261be0806de1f2e45e6fe7b5eea78 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 26 Jul 2023 17:31:25 +0200 Subject: [PATCH 0343/1312] Add global variable for detection of UAF occurrences --- src/analyses/useAfterFree.ml | 14 +++++++++++--- src/framework/analysisState.ml | 3 +++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index c3aebc985e..591cbba448 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -50,17 +50,24 @@ struct match get_current_threadid ctx with | `Lifted current -> let possibly_started = ThreadIdSet.exists (possibly_started current) freeing_threads in - if possibly_started then + if possibly_started then begin + AnalysisState.svcomp_may_use_after_free := true; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "There's a thread that's been started in parallel with the memory-freeing threads for heap variable %a. Use-After-Free might occur" CilType.Varinfo.pretty heap_var + end else begin let current_is_unique = ThreadId.Thread.is_unique current in let any_equal_current threads = ThreadIdSet.exists (equal_current current) threads in - if not current_is_unique && any_equal_current freeing_threads then + if not current_is_unique && any_equal_current freeing_threads then begin + AnalysisState.svcomp_may_use_after_free := true; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Current thread is not unique and a Use-After-Free might occur for heap variable %a" CilType.Varinfo.pretty heap_var - else if D.mem heap_var ctx.local then + end + else if D.mem heap_var ctx.local then begin + AnalysisState.svcomp_may_use_after_free := true; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Use-After-Free might occur in current unique thread %a for heap variable %a" ThreadIdDomain.FlagConfiguredTID.pretty current CilType.Varinfo.pretty heap_var + end end | `Top -> + AnalysisState.svcomp_may_use_after_free := true; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "CurrentThreadId is top. A Use-After-Free might occur for heap variable %a" CilType.Varinfo.pretty heap_var | `Bot -> M.warn ~category:MessageCategory.Analyzer "CurrentThreadId is bottom" @@ -86,6 +93,7 @@ struct | a when not (Queries.LS.is_top a) && not (Queries.LS.mem (dummyFunDec.svar, `NoOffset) a) -> let warn_for_heap_var var = if D.mem var state then + AnalysisState.svcomp_may_use_after_free := true; M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "lval (%s) in \"%s\" points to a maybe freed memory region" var.vname transfer_fn_name in let pointed_to_heap_vars = diff --git a/src/framework/analysisState.ml b/src/framework/analysisState.ml index 0f3a9f55bc..385fa26aef 100644 --- a/src/framework/analysisState.ml +++ b/src/framework/analysisState.ml @@ -7,6 +7,9 @@ let should_warn = ref false (** Whether signed overflow or underflow happened *) let svcomp_may_overflow = ref false +(** Whether a Use-After-Free (UAF) happened *) +let svcomp_may_use_after_free = ref false + (** A hack to see if we are currently doing global inits *) let global_initialization = ref false From 77f7e89a6cd42ab2052cb5afab7b816918b8f1e3 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 26 Jul 2023 17:31:49 +0200 Subject: [PATCH 0344/1312] Add SVComp result generation for memory safety Currently only considering UAF --- src/autoTune.ml | 4 ++++ src/witness/svcomp.ml | 1 + src/witness/svcompSpec.ml | 4 ++++ src/witness/witness.ml | 30 ++++++++++++++++++++++++++++++ 4 files changed, 39 insertions(+) diff --git a/src/autoTune.ml b/src/autoTune.ml index a267c3bf9b..bce12302cd 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -185,6 +185,7 @@ let enableAnalyses anas = (*does not consider dynamic calls!*) let notNeccessaryThreadAnalyses = ["race"; "deadlock"; "maylocks"; "symb_locks"; "thread"; "threadid"; "threadJoins"; "threadreturn"] +let memorySafetyAnalyses = ["useAfterFree"] let reduceThreadAnalyses () = let isThreadCreate = function | LibraryDesc.ThreadCreate _ -> true @@ -219,6 +220,9 @@ let focusOnSpecification () = | NoOverflow -> (*We focus on integer analysis*) set_bool "ana.int.def_exc" true; set_bool "ana.int.interval" true + | MemorySafety -> (* Enable the memory safety analyses *) + print_endline @@ "Specification: MemorySafety -> enabling memory safety analyses \"" ^ (String.concat ", " memorySafetyAnalyses) ^ "\""; + enableAnalyses memorySafetyAnalyses (*Detect enumerations and enable the "ana.int.enums" option*) exception EnumFound diff --git a/src/witness/svcomp.ml b/src/witness/svcomp.ml index a5a572d1c2..b762d2eb5d 100644 --- a/src/witness/svcomp.ml +++ b/src/witness/svcomp.ml @@ -52,6 +52,7 @@ struct | UnreachCall _ -> "unreach-call" | NoOverflow -> "no-overflow" | NoDataRace -> "no-data-race" (* not yet in SV-COMP/Benchexec *) + | MemorySafety -> "memory-safety" in "false(" ^ result_spec ^ ")" | Unknown -> "unknown" diff --git a/src/witness/svcompSpec.ml b/src/witness/svcompSpec.ml index 464c170251..784155ab9b 100644 --- a/src/witness/svcompSpec.ml +++ b/src/witness/svcompSpec.ml @@ -6,6 +6,7 @@ type t = | UnreachCall of string | NoDataRace | NoOverflow + | MemorySafety let of_string s = let s = String.strip s in @@ -16,6 +17,8 @@ let of_string s = NoDataRace else if global_not = "overflow" then NoOverflow + else if global_not = "memory-safety" then + MemorySafety else let call_regex = Str.regexp "call(\\(.*\\)())" in if Str.string_match call_regex global_not 0 then @@ -42,5 +45,6 @@ let to_string spec = | UnreachCall f -> "call(" ^ f ^ "())" | NoDataRace -> "data-race" | NoOverflow -> "overflow" + | MemorySafety -> "memory-safety" in "CHECK( init(main()), LTL(G ! " ^ global_not ^ ") )" diff --git a/src/witness/witness.ml b/src/witness/witness.ml index aff9a01383..6dc0d04034 100644 --- a/src/witness/witness.ml +++ b/src/witness/witness.ml @@ -472,6 +472,36 @@ struct in (module TaskResult:WitnessTaskResult) ) + | MemorySafety -> + let module TrivialArg = + struct + include Arg + let next _ = [] + end + in + if not !AnalysisState.svcomp_may_use_after_free then + let module TaskResult = + struct + module Arg = Arg + let result = Result.True + let invariant _ = Invariant.none + let is_violation _ = false + let is_sink _ = false + end + in + (module TaskResult:WitnessTaskResult) + else ( + let module TaskResult = + struct + module Arg = TrivialArg + let result = Result.Unknown + let invariant _ = Invariant.none + let is_violation _ = false + let is_sink _ = false + end + in + (module TaskResult:WitnessTaskResult) + ) let write entrystates = From 1be8c9c94ae7c4c83064b858cb8147ea674fead8 Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Wed, 26 Jul 2023 17:58:08 +0200 Subject: [PATCH 0345/1312] Remove goto stuff from loopTermination analysis --- src/analyses/loopTermination.ml | 21 +-------------------- src/util/terminationPreprocessing.ml | 4 +++- 2 files changed, 4 insertions(+), 21 deletions(-) diff --git a/src/analyses/loopTermination.ml b/src/analyses/loopTermination.ml index 89976b7627..563d6574bb 100644 --- a/src/analyses/loopTermination.ml +++ b/src/analyses/loopTermination.ml @@ -11,12 +11,6 @@ let single_thread : bool ref = ref false (** Contains all loop counter variables (varinfo) and maps them to their corresponding loop statement. *) let loop_counters : stmt VarToStmt.t ref = ref VarToStmt.empty -(** Contains the locations of the upjumping gotos. *) -let upjumping_gotos : location list ref = ref [] - -let no_upjumping_gotos () = - upjumping_gotos.contents = [] - (** Checks whether a variable can be bounded. *) let check_bounded ctx varinfo = let open IntDomain.IntDomTuple in @@ -53,18 +47,6 @@ struct (** Warnings for detected possible non-termination *) let finalize () = - (* Upjumping gotos *) - if not (no_upjumping_gotos ()) then ( - List.iter - (fun x -> - let msgs = - [(Pretty.dprintf - "The program might not terminate! (Upjumping Goto)", - Some (M.Location.CilLocation x) - );] in - M.msg_group Warning ~category:NonTerminating "Possibly non terminating loops" msgs) - (!upjumping_gotos) - ); (* Multithreaded *) if not (!single_thread) then ( M.warn ~category:NonTerminating "The program might not terminate! (Multithreaded)\n" @@ -117,12 +99,11 @@ struct * evaluation, the correct value of single_thread can not be guaranteed! * Therefore, we use a let-in clause here. *) always_single_threaded - && no_upjumping_gotos () && G.for_all (fun _ term_info -> term_info) (ctx.global ()) | _ -> Queries.Result.top q end let () = - Cilfacade.register_preprocess_cil (Spec.name ()) (new loopCounterVisitor loop_counters upjumping_gotos); + Cilfacade.register_preprocess_cil (Spec.name ()) (new loopCounterVisitor loop_counters); MCP.register_analysis (module Spec : MCPSpec) diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index 74aca11824..f5e520104b 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -17,7 +17,7 @@ let extract_file_name s = (*There still may be a need to filt let show_location_id l = string_of_int l.line ^ "_" ^ string_of_int l.column ^ "-file" ^ "_" ^ extract_file_name l.file -class loopCounterVisitor lc lg (fd : fundec) = object(self) +class loopCounterVisitor lc (fd : fundec) = object(self) inherit nopCilVisitor method! vstmt s = @@ -55,6 +55,7 @@ class loopCounterVisitor lc lg (fd : fundec) = object(self) let nb = mkBlock [init_stmt; mkStmt s.skind] in s.skind <- Block nb; s + (* | Goto (sref, l) -> let goto_jmp_stmt = sref.contents.skind in let loc_stmt = get_stmtLoc goto_jmp_stmt in @@ -62,6 +63,7 @@ class loopCounterVisitor lc lg (fd : fundec) = object(self) then lg := List.append !lg ([l] : location list); (*problem: the program might not terminate!*) s + *) | _ -> s in ChangeDoChildrenPost (s, action); end From cf594b66a11bcef2fe12892fb54c1ae385c9d07d Mon Sep 17 00:00:00 2001 From: Alexander Schlenga Date: Wed, 26 Jul 2023 18:03:56 +0200 Subject: [PATCH 0346/1312] Add goto-fundec list in cilfacade.ml --- src/util/cilfacade.ml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/util/cilfacade.ml b/src/util/cilfacade.ml index 2a81444e41..284c01a76c 100644 --- a/src/util/cilfacade.ml +++ b/src/util/cilfacade.ml @@ -606,3 +606,7 @@ let add_function_declarations (file: Cil.file): unit = let fun_decls = List.filter_map declaration_from_GFun functions in let globals = upto_last_type @ fun_decls @ non_types @ functions in file.globals <- globals + +(** Contains the locations of the upjumping gotos and the respective functions + * they are being called in. *) +let upjumping_gotos : (location * fundec) list ref = ref [] From eabf6b7888911d862d8ab745903a9e8bb4f7ae14 Mon Sep 17 00:00:00 2001 From: Isidor Zweckstetter Date: Wed, 26 Jul 2023 19:05:17 +0200 Subject: [PATCH 0347/1312] add gotos with their respective function to new list in Cilfacade --- src/util/terminationPreprocessing.ml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index f5e520104b..32a9468233 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -2,8 +2,6 @@ open GoblintCil module VarToStmt = Map.Make(CilType.Varinfo) (* maps varinfos (= loop counter variable) to the statement of the corresponding loop*) - - let extract_file_name s = (*There still may be a need to filter more chars*) let ls = String.split_on_char '/' s in (*Assuming '/' as path seperator*) let ls = List.rev ls in @@ -55,15 +53,13 @@ class loopCounterVisitor lc (fd : fundec) = object(self) let nb = mkBlock [init_stmt; mkStmt s.skind] in s.skind <- Block nb; s - (* | Goto (sref, l) -> let goto_jmp_stmt = sref.contents.skind in - let loc_stmt = get_stmtLoc goto_jmp_stmt in + let loc_stmt = Cil.get_stmtLoc goto_jmp_stmt in if CilType.Location.compare l loc_stmt >= 0 (*is pos if first loc is greater -> below the second loc*) then - lg := List.append !lg ([l] : location list); (*problem: the program might not terminate!*) + Cilfacade.upjumping_gotos := List.append !Cilfacade.upjumping_gotos ([(l, fd)] : (location * fundec) list); (*problem: the program might not terminate!*) s - *) | _ -> s in ChangeDoChildrenPost (s, action); end From 19addab0d94f75486291e2bef9b9dff7e644a1a8 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Wed, 26 Jul 2023 19:14:06 +0200 Subject: [PATCH 0348/1312] added code for control to only print upjumping goto message when goto is not in dead code function --- src/framework/control.ml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/framework/control.ml b/src/framework/control.ml index 69cdf1bdac..dfb0859438 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -136,6 +136,25 @@ struct try StringMap.find fn (StringMap.find file !live_lines) with Not_found -> BatISet.empty in + (*check if we have upjumping gotos*) + List.iter + (fun x -> + let ((l: location), (fd: fundec)) = x in (*unpack tuple for later use*) + let fname = fd.svar.vname in + StringMap.iter + (fun fi _ -> + let fundec_live = live fi fname in + if (not (BatISet.is_empty fundec_live)) then ( + let msgs = + [(Pretty.dprintf + "The program might not terminate! (Upjumping Goto)", + Some (M.Location.CilLocation l) + );] in + M.msg_group Warning ~category:NonTerminating "Possibly non terminating loops" msgs); + ) + (!dead_lines)) + (!Cilfacade.upjumping_gotos); + dead_lines := StringMap.mapi (fun fi -> StringMap.mapi (fun fu ded -> BatISet.diff ded (live fi fu))) !dead_lines; dead_lines := StringMap.map (StringMap.filter (fun _ x -> not (BatISet.is_empty x))) !dead_lines; dead_lines := StringMap.filter (fun _ x -> not (StringMap.is_empty x)) !dead_lines; From 72e2e22b62f026693c404b6edf3b41930fef7e18 Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Wed, 26 Jul 2023 19:29:17 +0200 Subject: [PATCH 0349/1312] now everything should work :), now iterating over live_lines --- src/framework/control.ml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/framework/control.ml b/src/framework/control.ml index dfb0859438..b2125ef9a7 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -139,12 +139,15 @@ struct (*check if we have upjumping gotos*) List.iter (fun x -> + print_endline @@ "halloooo"; let ((l: location), (fd: fundec)) = x in (*unpack tuple for later use*) - let fname = fd.svar.vname in + let fname = fd.svar.vname in + print_endline @@ "halloooo2"; StringMap.iter (fun fi _ -> let fundec_live = live fi fname in - if (not (BatISet.is_empty fundec_live)) then ( + print_endline @@ "halloooo3"; + if ( not (BatISet.is_empty fundec_live)) then ( let msgs = [(Pretty.dprintf "The program might not terminate! (Upjumping Goto)", @@ -152,9 +155,8 @@ struct );] in M.msg_group Warning ~category:NonTerminating "Possibly non terminating loops" msgs); ) - (!dead_lines)) + (!live_lines)) (!Cilfacade.upjumping_gotos); - dead_lines := StringMap.mapi (fun fi -> StringMap.mapi (fun fu ded -> BatISet.diff ded (live fi fu))) !dead_lines; dead_lines := StringMap.map (StringMap.filter (fun _ x -> not (BatISet.is_empty x))) !dead_lines; dead_lines := StringMap.filter (fun _ x -> not (StringMap.is_empty x)) !dead_lines; From 6090c62b650696aa9ddb915cd4ab6da494e5e23b Mon Sep 17 00:00:00 2001 From: Johanna Schinabeck Date: Wed, 26 Jul 2023 19:31:05 +0200 Subject: [PATCH 0350/1312] now without prints :))) --- src/framework/control.ml | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/framework/control.ml b/src/framework/control.ml index b2125ef9a7..8185dc6053 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -139,14 +139,11 @@ struct (*check if we have upjumping gotos*) List.iter (fun x -> - print_endline @@ "halloooo"; let ((l: location), (fd: fundec)) = x in (*unpack tuple for later use*) let fname = fd.svar.vname in - print_endline @@ "halloooo2"; StringMap.iter (fun fi _ -> let fundec_live = live fi fname in - print_endline @@ "halloooo3"; if ( not (BatISet.is_empty fundec_live)) then ( let msgs = [(Pretty.dprintf From e96433b1ab65970da5aa1d4388d88676c0aedef5 Mon Sep 17 00:00:00 2001 From: Thomas Lagemann Date: Wed, 26 Jul 2023 20:52:56 +0200 Subject: [PATCH 0351/1312] Handle skipped (NON-)TERM-Tests other than TODO --- scripts/update_suite.rb | 2 +- .../75-termination/09-complex-for-loop-terminating.c | 3 ++- tests/regression/75-termination/10-complex-loop-terminating.c | 3 ++- .../75-termination/15-complex-loop-combination-terminating.c | 3 ++- .../regression/75-termination/25-leave-loop-goto-terminating.c | 2 +- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/scripts/update_suite.rb b/scripts/update_suite.rb index 60a7ec06be..313b6b75f9 100755 --- a/scripts/update_suite.rb +++ b/scripts/update_suite.rb @@ -331,7 +331,7 @@ def parse_tests (lines) end end case lines[0] - when /TODO|SKIP/ + when /TODO/ case lines[0] when /NONTERM/ tests[-1] = "nonterm" diff --git a/tests/regression/75-termination/09-complex-for-loop-terminating.c b/tests/regression/75-termination/09-complex-for-loop-terminating.c index 264c08f6ed..fb2acaf569 100644 --- a/tests/regression/75-termination/09-complex-for-loop-terminating.c +++ b/tests/regression/75-termination/09-complex-for-loop-terminating.c @@ -1,4 +1,5 @@ -// SKIP TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --set sem.int.signed_overflow assume_none +// SKIP TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --set sem.int.signed_overflow assume_none +// Apron is not precise enough for some nested loops #include int main() diff --git a/tests/regression/75-termination/10-complex-loop-terminating.c b/tests/regression/75-termination/10-complex-loop-terminating.c index 90317d5209..738fe78683 100644 --- a/tests/regression/75-termination/10-complex-loop-terminating.c +++ b/tests/regression/75-termination/10-complex-loop-terminating.c @@ -1,4 +1,5 @@ -// SKIP TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --set sem.int.signed_overflow assume_none +// SKIP TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --set sem.int.signed_overflow assume_none +// Apron is not precise enough for some nested loops #include int main() diff --git a/tests/regression/75-termination/15-complex-loop-combination-terminating.c b/tests/regression/75-termination/15-complex-loop-combination-terminating.c index c2ab718200..1365611410 100644 --- a/tests/regression/75-termination/15-complex-loop-combination-terminating.c +++ b/tests/regression/75-termination/15-complex-loop-combination-terminating.c @@ -1,4 +1,5 @@ -// SKIP TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --set sem.int.signed_overflow assume_none +// SKIP TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --set sem.int.signed_overflow assume_none +// Apron is not precise enough for some nested loops #include int main() diff --git a/tests/regression/75-termination/25-leave-loop-goto-terminating.c b/tests/regression/75-termination/25-leave-loop-goto-terminating.c index b882759bff..61c8b8f58d 100644 --- a/tests/regression/75-termination/25-leave-loop-goto-terminating.c +++ b/tests/regression/75-termination/25-leave-loop-goto-terminating.c @@ -1,4 +1,4 @@ -// SKIP TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() From 91388efe40f0ea7f432068bfa34c91cce00a82b1 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 28 Jul 2023 12:42:59 +0300 Subject: [PATCH 0352/1312] Fix UnionDomain crash on chrony --- src/analyses/base.ml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 0e766401d9..1808014654 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1411,9 +1411,13 @@ struct let new_value = VD.update_offset (Queries.to_value_domain_ask a) old_value offs projected_value lval_raw ((Var x), cil_offset) t in if WeakUpdates.mem x st.weak then VD.join old_value new_value - else if invariant then + else if invariant then ( (* without this, invariant for ambiguous pointer might worsen precision for each individual address to their join *) - VD.meet old_value new_value + try + VD.meet old_value new_value + with Lattice.Uncomparable -> + new_value + ) else new_value in From 99fb013038d3b6c333f236537668e3513d8faceb Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 28 Jul 2023 13:59:17 +0200 Subject: [PATCH 0353/1312] Refactor and avoid unnecessary queries and conversions --- src/analyses/base.ml | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index a4a674cf2d..3a2f4b52bd 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1998,18 +1998,14 @@ struct invalidate ~deep:true ~ctx (Analyses.ask_of_ctx ctx) gs st' deep_addrs let check_free_of_non_heap_mem ctx special_fn ptr = - let points_to_set = ctx.ask (Queries.MayPointTo ptr) in - begin try - let exists_non_heap_var = - (* elements throws Unsupported if the points-to set is top *) - Queries.LS.elements points_to_set - |> List.map fst - |> List.exists (fun var -> not (ctx.ask (Queries.IsHeapVar var))) - in - if exists_non_heap_var then - M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Free of non-dynamically allocated memory in function %s for pointer %a" special_fn.vname d_exp ptr - with _ -> M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Points-to set for pointer %a in function %s is top. Potential free of non-dynamically allocated memory may occur" d_exp ptr special_fn.vname - end + match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local ptr with + | Address a -> + let points_to_set = addrToLvalSet a in + if Q.LS.is_top points_to_set then + M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Points-to set for pointer %a in function %s is top. Potential free of non-dynamically allocated memory may occur" d_exp ptr special_fn.vname + else if Q.LS.exists (fun (v, _) -> not (ctx.ask (Q.IsHeapVar v))) points_to_set then + M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Free of non-dynamically allocated memory in function %s for pointer %a" special_fn.vname d_exp ptr + | _ -> M.warn ~category:MessageCategory.Analyzer "Pointer %a in function %s doesn't evaluate to a valid address." d_exp ptr special_fn.vname let special ctx (lv:lval option) (f: varinfo) (args: exp list) = let invalidate_ret_lv st = match lv with From 97cbb4e73fbef33d4e576bab373dfa1c9b0f7aa4 Mon Sep 17 00:00:00 2001 From: Nathan Schmidt Date: Fri, 28 Jul 2023 14:15:39 +0200 Subject: [PATCH 0354/1312] Added examples of thesis --- .../73-strings/01-string_literals.c | 28 ++++++++++++- tests/regression/73-strings/04-char_arrays.c | 42 +++++++++++++++---- 2 files changed, 61 insertions(+), 9 deletions(-) diff --git a/tests/regression/73-strings/01-string_literals.c b/tests/regression/73-strings/01-string_literals.c index 159ca57f1c..9366b516df 100644 --- a/tests/regression/73-strings/01-string_literals.c +++ b/tests/regression/73-strings/01-string_literals.c @@ -18,7 +18,28 @@ void id(char* s) { ID; // WARN } -int main() { +void example1() { + char* s1 = "bc\0test"; + char* s2 = "bc"; + char* s3; + if (rand()) + s3 = "aabbcc"; + else + s3 = "ebcdf"; + + int i = strcmp(s1, s2); + __goblint_check(i == 0); + + char* s4 = strstr(s3, s1); + __goblint_check(s4 != NULL); + + size_t len = strlen(s4); + __goblint_check(len >= 3); + __goblint_check(len <= 4); + __goblint_check(len == 3); // UNKNOWN! +} + +void example2() { char* s1 = "abcde"; char* s2 = "abcdfg"; char* s3 = hello_world(); @@ -109,6 +130,11 @@ int main() { strcat(s5, " world"); // NOWARN strncat(s5, "! some further text", 1); // NOWARN #endif +} + +int main() { + example1(); + example2(); return 0; } diff --git a/tests/regression/73-strings/04-char_arrays.c b/tests/regression/73-strings/04-char_arrays.c index 0af19ba968..c86a0b1ebc 100644 --- a/tests/regression/73-strings/04-char_arrays.c +++ b/tests/regression/73-strings/04-char_arrays.c @@ -14,11 +14,37 @@ int main() { example7(); example8(); example9(); + example10(); return 0; } void example1() { + char s1[] = "user1_"; // must and may null at 6 and 7 + char s2[] = "pwd:\0abc"; // must and may null at 4 and 8 + char s3[20]; // no must nulls, all may nulls + + strcpy(s3, s1); // must null at 6, may nulls starting from 6 + + if (rand()) { + s2[4] = ' '; + strncat(s3, s2, 10); // must null at 14, may nulls starting from 14 + } else + strcat(s3, s2); // must null at 10, may nulls starting from 10 + + // s3: no must nulls, may nulls starting from 10 + + s3[14] = '\0'; // must null at 14, may nulls starting from 10 + + size_t len = strlen(s3); + __goblint_check(len >= 10); + __goblint_check(len <= 14); + __goblint_check(len == 10); // UNKNOWN! + + strcpy(s1, s3); // WARN +} + +void example2() { char s1[42]; char s2[20] = "testing"; // must null at 7, may null starting from 7 @@ -33,7 +59,7 @@ void example1() { __goblint_check(len == 14); } -void example2() { +void example3() { char s1[42]; char s2[20] = "testing"; // must null at 7, may null starting from 7 @@ -49,7 +75,7 @@ void example2() { strcpy(s2, s1); // WARN: no must null in s1 } -void example3() { +void example4() { char s1[5] = "abc\0d"; // must and may null at 3 char s2[] = "a"; // must and may null at 1 @@ -63,7 +89,7 @@ void example3() { __goblint_check(len == 3); } -void example4() { +void example5() { char s1[7] = "hello!"; // must and may null at 6 char s2[8] = "goblint"; // must and may null at 7 @@ -73,7 +99,7 @@ void example4() { __goblint_check(len >= 7); // no null byte in s1 } -void example5() { +void example6() { char s1[42] = "a string, i.e. null-terminated char array"; // must and may null at 42 for (int i = 0; i < 42; i += 3) { if (rand() != 42) @@ -97,7 +123,7 @@ void example5() { __goblint_check(len > 40); // UNKNOWN } -void example6() { +void example7() { char s1[50] = "hello"; // must and may null at 5 char s2[] = " world!"; // must and may null at 7 char s3[] = " goblint."; // must and may null at 9 @@ -127,7 +153,7 @@ void example6() { __goblint_check(len < 20); // UNKNOWN } -void example7() { +void example8() { char s1[6] = "abc"; // must and may null at 3 if (rand() == 42) s1[5] = '\0'; // must null at 3, may nulls at 3 and 5 @@ -158,7 +184,7 @@ void example7() { __goblint_check(len >= 12); } -void example8() { +void example9() { char empty[] = ""; char s1[] = "hello world"; // must and may null at 11 char s2[] = "test"; // must and may null at 4 @@ -176,7 +202,7 @@ void example8() { __goblint_check(cmp_ptr == NULL); } -void example9() { +void example10() { char empty1[] = ""; char empty2[] = "\0 also empty"; char s1[] = "hi"; From 2d5604657c5d3e48a3827ae56fca3c5c0d02b10f Mon Sep 17 00:00:00 2001 From: karoliineh Date: Fri, 28 Jul 2023 15:57:25 +0300 Subject: [PATCH 0355/1312] Convert memmove library functions. fixes #1121 --- src/analyses/libraryFunctions.ml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index e3fce8718a..3cd3b308cd 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -17,6 +17,9 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__builtin___memcpy_chk", special [__ "dest" [w]; __ "src" [r]; drop "n" []; drop "os" []] @@ fun dest src -> Memcpy { dest; src }); ("mempcpy", unknown [drop "dest" [w]; drop "src" [r]; drop "n" []]); ("__builtin___mempcpy_chk", unknown [drop "dest" [w]; drop "src" [r]; drop "n" []; drop "os" []]); + ("memmove", unknown [drop "dest" [w]; drop "src" [r]; drop "count" []]); + ("__builtin_memmove", unknown [drop "dest" [w]; drop "src" [r]; drop "count" []]); + ("__builtin___memmove_chk", unknown [drop "dest" [w]; drop "src" [r]; drop "count" []]); ("strcpy", special [__ "dest" [w]; __ "src" [r]] @@ fun dest src -> Strcpy { dest; src; n = None; }); ("__builtin_strcpy", special [__ "dest" [w]; __ "src" [r]] @@ fun dest src -> Strcpy { dest; src; n = None; }); ("__builtin___strcpy_chk", special [__ "dest" [w]; __ "src" [r]; drop "os" []] @@ fun dest src -> Strcpy { dest; src; n = None; }); @@ -1001,9 +1004,6 @@ let invalidate_actions = [ "getpgrp", readsAll;(*safe*) "umount2", readsAll;(*safe*) "memchr", readsAll;(*safe*) - "memmove", writes [2;3];(*keep [2;3]*) - "__builtin_memmove", writes [2;3];(*keep [2;3]*) - "__builtin___memmove_chk", writes [2;3];(*keep [2;3]*) "waitpid", readsAll;(*safe*) "statfs", writes [1;3;4];(*keep [1;3;4]*) "mount", readsAll;(*safe*) From 89dde4c8314b9aa80c79ce00088157ec84d34bc5 Mon Sep 17 00:00:00 2001 From: FelixKrayer Date: Tue, 27 Jun 2023 17:58:22 +0200 Subject: [PATCH 0356/1312] implement suggestions from Github --- src/analyses/baseInvariant.ml | 8 ++-- src/analyses/libraryDesc.ml | 39 ++++--------------- src/analyses/tmpSpecial.ml | 8 +--- .../57-floats/19-library-invariant.c | 3 +- 4 files changed, 13 insertions(+), 45 deletions(-) diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index 1d0f34ef14..35e9bdc5b0 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -703,11 +703,11 @@ struct | TInt (ik, _) -> begin match x with | ((Var v), offs) -> - if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, TmpSpecial.Spec.resolve offs))); + if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, Lval.CilLval.of_ciloffs offs))); let tv_opt = ID.to_bool c in begin match tv_opt with | Some tv -> - begin match ctx.ask (Queries.TmpSpecial (v, TmpSpecial.Spec.resolve offs)) with + begin match ctx.ask (Queries.TmpSpecial (v, Lval.CilLval.of_ciloffs offs)) with | `Lifted (Isfinite xFloat) when tv -> inv_exp (`Float (FD.finite (unroll_fk_of_exp xFloat))) xFloat st | `Lifted (Isnan xFloat) when tv -> inv_exp (`Float (FD.nan_of (unroll_fk_of_exp xFloat))) xFloat st (* should be correct according to C99 standard*) @@ -739,8 +739,8 @@ struct | TFloat (fk, _) -> begin match x with | ((Var v), offs) -> - if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, TmpSpecial.Spec.resolve offs))); - begin match ctx.ask (Queries.TmpSpecial (v, TmpSpecial.Spec.resolve offs)) with + if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, Lval.CilLval.of_ciloffs offs))); + begin match ctx.ask (Queries.TmpSpecial (v, Lval.CilLval.of_ciloffs offs)) with | `Lifted (Ceil (ret_fk, xFloat)) -> inv_exp (`Float (FD.inv_ceil (FD.cast_to ret_fk c))) xFloat st | `Lifted (Floor (ret_fk, xFloat)) -> inv_exp (`Float (FD.inv_floor (FD.cast_to ret_fk c))) xFloat st | `Lifted (Fabs (ret_fk, xFloat)) -> diff --git a/src/analyses/libraryDesc.ml b/src/analyses/libraryDesc.ml index 903360a603..5268e7e1f1 100644 --- a/src/analyses/libraryDesc.ml +++ b/src/analyses/libraryDesc.ml @@ -145,40 +145,11 @@ let of_old ?(attrs: attr list=[]) (old_accesses: Accesses.old) (classify_name): } module MathPrintable = struct - include Printable.Std + include Printable.StdLeaf type t = math [@@deriving eq, ord, hash] let name () = "MathPrintable" - let relift ml = ml - - let show = function - | Nan _ -> "nan" - | Inf _ -> "inf" - | Isfinite _ -> "isFinite" - | Isinf _ -> "isInf" - | Isnan _ -> "isNan" - | Isnormal _ -> "isNormal" - | Signbit _ -> "signbit" - | Isgreater _ -> "isGreater" - | Isgreaterequal _ -> "isGreaterEqual" - | Isless _ -> "isLess" - | Islessequal _ -> "isLessEqual" - | Islessgreater _ -> "isLessGreater" - | Isunordered _ -> "isUnordered" - | Ceil _ -> "ceil" - | Floor _ -> "floor" - | Fabs _ -> "fabs" - | Fmax _ -> "fmax" - | Fmin _ -> "fmin" - | Acos _ -> "acos" - | Asin _ -> "asin" - | Atan _ -> "atan" - | Atan2 _ -> "atan2" - | Cos _ -> "cos" - | Sin _ -> "sin" - | Tan _ -> "tan" - let pretty () = function | Nan (fk, exp) -> Pretty.dprintf "(%a )nan(%a)" d_fkind fk d_exp exp | Inf fk -> Pretty.dprintf "(%a )inf()" d_fkind fk @@ -206,8 +177,12 @@ module MathPrintable = struct | Sin (fk, exp) -> Pretty.dprintf "(%a )sin(%a)" d_fkind fk d_exp exp | Tan (fk, exp) -> Pretty.dprintf "(%a )tan(%a)" d_fkind fk d_exp exp - let printXml f x = BatPrintf.fprintf f "\n\n%s\n\n\n" (show x) - let to_yojson _ = failwith "ToDo Implement in future" + include Printable.SimplePretty ( + struct + type nonrec t = t + let pretty = pretty + end + ) end module MathLifted = Lattice.Flat (MathPrintable) (struct diff --git a/src/analyses/tmpSpecial.ml b/src/analyses/tmpSpecial.ml index 9b88ab0317..4e00b58c92 100644 --- a/src/analyses/tmpSpecial.ml +++ b/src/analyses/tmpSpecial.ml @@ -17,12 +17,6 @@ struct module D = MapDomain.MapBot (Lval.CilLval) (MLDeps) module C = Lattice.Unit - let rec resolve (offs : offset) : (CilType.Fieldinfo.t, Basetype.CilExp.t) Lval.offs = - match offs with - | NoOffset -> `NoOffset - | Field (f_info, f_offs) -> `Field (f_info, (resolve f_offs)) - | Index (i_exp, i_offs) -> `Index (i_exp, (resolve i_offs)) - let invalidate ask exp_w st = D.filter (fun _ (ml, deps) -> (Deps.for_all (fun arg -> not (VarEq.may_change ask exp_w arg)) deps)) st @@ -85,7 +79,7 @@ struct if List.exists (fun arg -> VarEq.may_change ask (mkAddrOf lv) arg) arglist then d else - D.add (v, resolve offs) ((ML.lift fun_args, Deps.of_list ((Lval lv)::arglist))) d + D.add (v, Lval.CilLval.of_ciloffs offs) ((ML.lift fun_args, Deps.of_list ((Lval lv)::arglist))) d | _ -> d in diff --git a/tests/regression/57-floats/19-library-invariant.c b/tests/regression/57-floats/19-library-invariant.c index 535a306174..1bf332aba9 100644 --- a/tests/regression/57-floats/19-library-invariant.c +++ b/tests/regression/57-floats/19-library-invariant.c @@ -46,8 +46,7 @@ void main() { __goblint_check(f >= -5.); __goblint_check(f <= 5.); } - if(__builtin_fabs(f) == -6.) { - // DEAD + if(__builtin_fabs(f) == -6.) { //WARN (dead branch) g = 0.; } From ff511297dc353ab7c0db09bdcf01f27617802eb1 Mon Sep 17 00:00:00 2001 From: FelixKrayer Date: Fri, 28 Jul 2023 19:11:43 +0200 Subject: [PATCH 0357/1312] removed unnecessary update_lval --- src/analyses/baseInvariant.ml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index 35e9bdc5b0..25731f7127 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -697,7 +697,6 @@ struct | TFloat (fk, _) -> `Float (FD.of_int fk c) | _ -> `Int c in - let st = update_lval c x c' ID.pretty in (* handle special calls *) begin match t with | TInt (ik, _) -> @@ -716,13 +715,13 @@ struct | `Lifted (Isless (xFloat, yFloat)) -> inv_exp (`Int (ID.of_bool ik tv)) (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))) st | `Lifted (Islessequal (xFloat, yFloat)) -> inv_exp (`Int (ID.of_bool ik tv)) (BinOp (Le, xFloat, yFloat, (typeOf xFloat))) st | `Lifted (Islessgreater (xFloat, yFloat)) -> inv_exp (`Int (ID.of_bool ik tv)) (BinOp (LOr, (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))), (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))), (TInt (IBool, [])))) st - | _ -> st + | _ -> update_lval c x c' ID.pretty end - | None -> st + | None -> update_lval c x c' ID.pretty end - | _ -> st + | _ -> update_lval c x c' ID.pretty end - | _ -> st + | _ -> update_lval c x c' ID.pretty end | `Float c -> let c' = match t with @@ -733,7 +732,6 @@ struct | TFloat (fk, _) -> `Float (FD.cast_to fk c) | _ -> `Float c in - let st = update_lval c x c' FD.pretty in (* handle special calls *) begin match t with | TFloat (fk, _) -> @@ -749,11 +747,11 @@ struct raise Analyses.Deadcode else inv_exp (`Float inv) xFloat st - | _ -> st + | _ -> update_lval c x c' FD.pretty end - | _ -> st + | _ -> update_lval c x c' FD.pretty end - | _ -> st + | _ -> update_lval c x c' FD.pretty end | `Address c -> let c' = c_typed in (* TODO: need any of the type-matching nonsense? *) From e23c42d570ee2539a0f04c10ebd175254eab5065 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 30 Jul 2023 11:43:18 +0200 Subject: [PATCH 0358/1312] Add check for UnknownPtr --- src/analyses/base.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 3a2f4b52bd..d4ba662e97 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2003,7 +2003,7 @@ struct let points_to_set = addrToLvalSet a in if Q.LS.is_top points_to_set then M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Points-to set for pointer %a in function %s is top. Potential free of non-dynamically allocated memory may occur" d_exp ptr special_fn.vname - else if Q.LS.exists (fun (v, _) -> not (ctx.ask (Q.IsHeapVar v))) points_to_set then + else if (Q.LS.exists (fun (v, _) -> not (ctx.ask (Q.IsHeapVar v))) points_to_set) || (AD.mem Addr.UnknownPtr a) then M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Free of non-dynamically allocated memory in function %s for pointer %a" special_fn.vname d_exp ptr | _ -> M.warn ~category:MessageCategory.Analyzer "Pointer %a in function %s doesn't evaluate to a valid address." d_exp ptr special_fn.vname From cfef9bd7d3f08fb578bc44608bd6af549785d48e Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 30 Jul 2023 14:13:32 +0200 Subject: [PATCH 0359/1312] Fix multi-line if expression --- src/analyses/useAfterFree.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index 591cbba448..3b189a6dea 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -92,9 +92,10 @@ struct match ctx.ask (Queries.MayPointTo lval_to_query) with | a when not (Queries.LS.is_top a) && not (Queries.LS.mem (dummyFunDec.svar, `NoOffset) a) -> let warn_for_heap_var var = - if D.mem var state then + if D.mem var state then begin AnalysisState.svcomp_may_use_after_free := true; M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "lval (%s) in \"%s\" points to a maybe freed memory region" var.vname transfer_fn_name + end in let pointed_to_heap_vars = Queries.LS.elements a From cbaffbb8de505036a1058d42376ded1630116965 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 30 Jul 2023 15:00:07 +0200 Subject: [PATCH 0360/1312] Side-effect a tuple of tids and sets of joined threads This aims to improve precision --- src/analyses/useAfterFree.ml | 56 +++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index 3b189a6dea..31203e505d 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -5,7 +5,7 @@ open Analyses open MessageCategory module ToppedVarInfoSet = SetDomain.ToppedSet(CilType.Varinfo)(struct let topname = "All Heap Variables" end) -module ThreadIdSet = SetDomain.Make(ThreadIdDomain.ThreadLifted) +module ThreadIdWithJoinedThreads = SetDomain.Make(Lattice.Prod(ThreadIdDomain.ThreadLifted)(ConcDomain.MustThreadSet)) module Spec : Analyses.MCPSpec = struct @@ -15,7 +15,7 @@ struct module D = ToppedVarInfoSet module C = Lattice.Unit - module G = ThreadIdSet + module G = ThreadIdWithJoinedThreads module V = VarinfoV (** TODO: Try out later in benchmarks to see how we perform with and without context-sensititivty *) @@ -27,36 +27,41 @@ struct let get_current_threadid ctx = ctx.ask Queries.CurrentThreadId + let get_joined_threads ctx = + ctx.ask Queries.MustJoinedThreads + let warn_for_multi_threaded_access ctx (heap_var:varinfo) behavior cwe_number = let freeing_threads = ctx.global heap_var in (* If we're single-threaded or there are no threads freeing the memory, we have nothing to WARN about *) - if ctx.ask (Queries.MustBeSingleThreaded { since_start = true }) || ThreadIdSet.is_empty freeing_threads then () + if ctx.ask (Queries.MustBeSingleThreaded { since_start = true }) || ThreadIdWithJoinedThreads.is_empty freeing_threads then () else begin let possibly_started current = function - | `Lifted tid -> - let threads = ctx.ask Queries.CreatedThreads in + | `Lifted tid, joined_threads -> + let created_threads = ctx.ask Queries.CreatedThreads in + (* Discard joined threads, as they're supposed to be joined before the point of freeing the memory *) + let threads = ConcDomain.MustThreadSet.diff created_threads joined_threads in let not_started = MHP.definitely_not_started (current, threads) tid in let possibly_started = not not_started in possibly_started - | `Top -> true - | `Bot -> false + | `Top, _ -> true + | `Bot, _ -> false in let equal_current current = function - | `Lifted tid -> + | `Lifted tid, _ -> ThreadId.Thread.equal current tid - | `Top -> true - | `Bot -> false + | `Top, _ -> true + | `Bot, _ -> false in match get_current_threadid ctx with | `Lifted current -> - let possibly_started = ThreadIdSet.exists (possibly_started current) freeing_threads in + let possibly_started = ThreadIdWithJoinedThreads.exists (possibly_started current) freeing_threads in if possibly_started then begin AnalysisState.svcomp_may_use_after_free := true; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "There's a thread that's been started in parallel with the memory-freeing threads for heap variable %a. Use-After-Free might occur" CilType.Varinfo.pretty heap_var end else begin let current_is_unique = ThreadId.Thread.is_unique current in - let any_equal_current threads = ThreadIdSet.exists (equal_current current) threads in + let any_equal_current threads = ThreadIdWithJoinedThreads.exists (equal_current current) threads in if not current_is_unique && any_equal_current freeing_threads then begin AnalysisState.svcomp_may_use_after_free := true; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Current thread is not unique and a Use-After-Free might occur for heap variable %a" CilType.Varinfo.pretty heap_var @@ -134,9 +139,25 @@ struct | StartOf lval | AddrOf lval -> warn_lval_might_contain_freed ~is_double_free transfer_fn_name ctx lval - let side_effect_mem_free ctx freed_heap_vars threadid = - let threadid = G.singleton threadid in - D.iter (fun var -> ctx.sideg var threadid) freed_heap_vars + let side_effect_mem_free ctx freed_heap_vars threadid joined_threads = + let side_effect_globals_to_heap_var heap_var globals_to_add = + let (tid_to_add, joined_threads_to_add) = globals_to_add in + let current_globals = ctx.global heap_var in + (* + * Check if there are tuples with the same first component (i.e., tid) + * If no, side-effect the globals that we receive here and be done + * If yes, join all second components together and side-effect a single global with + the tid as first component and the joined second components as a single second component + *) + let globals_with_same_tid = G.filter (fun (tid, _) -> ThreadIdDomain.ThreadLifted.equal tid tid_to_add) current_globals in + if G.is_empty globals_with_same_tid then + ctx.sideg heap_var (G.singleton globals_to_add) + else + let globals_to_add = G.fold (fun (t, j) (t_acc, j_acc) -> (t_acc, ConcDomain.MustThreadSet.join j j_acc)) globals_with_same_tid (tid_to_add, joined_threads_to_add) in + ctx.sideg heap_var (G.singleton globals_to_add) + in + let globals_to_side_effect = (threadid, joined_threads) in + D.iter (fun var -> side_effect_globals_to_heap_var var globals_to_side_effect) freed_heap_vars (* TRANSFER FUNCTIONS *) @@ -196,8 +217,9 @@ struct |> D.of_list in (* Side-effect the tid that's freeing all the heap vars collected here *) - side_effect_mem_free ctx pointed_to_heap_vars (get_current_threadid ctx); - D.join state (pointed_to_heap_vars) (* Add all heap vars, which ptr points to, to the state *) + side_effect_mem_free ctx pointed_to_heap_vars (get_current_threadid ctx) (get_joined_threads ctx); + (* Add all heap vars, which ptr points to, to the state *) + D.join state (pointed_to_heap_vars) | _ -> state end | _ -> state From c01585dac6ecc8d2efb42ff21c302ddcb675d705 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Mon, 31 Jul 2023 18:50:25 +0200 Subject: [PATCH 0361/1312] Warn if threadJoins is deactivated --- src/analyses/useAfterFree.ml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index 31203e505d..9a6484f624 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -1,5 +1,6 @@ (** An analysis for the detection of use-after-free vulnerabilities ([useAfterFree]). *) +open GobConfig open GoblintCil open Analyses open MessageCategory @@ -30,7 +31,16 @@ struct let get_joined_threads ctx = ctx.ask Queries.MustJoinedThreads + let warn_about_deactivated_thread_joins () = + if not @@ List.mem "threadJoins" @@ get_string_list "ana.activated" then + M.warn "Running without thread joins analysis. Multi-threaded UAF detection might be imprecise!" + let warn_for_multi_threaded_access ctx (heap_var:varinfo) behavior cwe_number = + (* + * We need the [threadJoins] analysis for making the multi-threaded UAF detection more precise + * Warn the user in case [threadJoins] is disabled + *) + warn_about_deactivated_thread_joins (); let freeing_threads = ctx.global heap_var in (* If we're single-threaded or there are no threads freeing the memory, we have nothing to WARN about *) if ctx.ask (Queries.MustBeSingleThreaded { since_start = true }) || ThreadIdWithJoinedThreads.is_empty freeing_threads then () From 49a6d5440099df0f3e730a6c7949c680809a5f7e Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Mon, 31 Jul 2023 18:54:20 +0200 Subject: [PATCH 0362/1312] Update joined thread test case annotations --- .../13-multi-threaded-uaf-with-joined-thread.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/regression/74-use_after_free/13-multi-threaded-uaf-with-joined-thread.c b/tests/regression/74-use_after_free/13-multi-threaded-uaf-with-joined-thread.c index 140e66eff8..2ce291f9d1 100644 --- a/tests/regression/74-use_after_free/13-multi-threaded-uaf-with-joined-thread.c +++ b/tests/regression/74-use_after_free/13-multi-threaded-uaf-with-joined-thread.c @@ -1,4 +1,4 @@ -//PARAM: --set ana.activated[+] useAfterFree +//PARAM: --set ana.activated[+] useAfterFree --set ana.activated[+] threadJoins #include #include #include @@ -26,7 +26,7 @@ int main() { pthread_mutex_lock(&mtx); *gptr = 43; //NOWARN - free(gptr); //WARN + free(gptr); //NOWARN pthread_mutex_unlock(&mtx); return 0; From 49fa590a1b481b90de416fc93cc4f13b8f86b489 Mon Sep 17 00:00:00 2001 From: FelixKrayer Date: Fri, 28 Jul 2023 20:02:28 +0200 Subject: [PATCH 0363/1312] better inversion of floor/ceil --- src/cdomains/floatDomain.ml | 26 +++++++++++++++---- .../57-floats/19-library-invariant.c | 4 ++- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/cdomains/floatDomain.ml b/src/cdomains/floatDomain.ml index e7117c9b62..1b674a667f 100644 --- a/src/cdomains/floatDomain.ml +++ b/src/cdomains/floatDomain.ml @@ -670,11 +670,27 @@ module FloatIntervalImpl(Float_t : CFloatType) = struct | (l, h) when l = h && l = Float_t.zero -> of_const 0. (*tan(0) = 0*) | _ -> top () (**could be exact for intervals where l=h, or even for some intervals *) - let eval_inv_ceil = function (*TODO: can probably be more precise*) - | (l, h) -> Interval (Float_t.lower_bound, h) - - let eval_inv_floor = function (*TODO: can probably be more precise*) - | (l, h) -> Interval (l, Float_t.upper_bound) + let eval_inv_ceil = function + | (l, h) -> + if (Float_t.sub Up (Float_t.ceil l) (Float_t.sub Down (Float_t.ceil l) (Float_t.of_float Nearest 1.0)) = (Float_t.of_float Nearest 1.0)) then + (* if [ceil(l) - (ceil(l) - 1.0) = 1.0], then we are in a range, where each int is expressable as float. + With that we can say, that [(ceil(x) >= l) => (x > (ceil(l) - 1.0)] *) + Interval (Float_t.sub Down (Float_t.ceil l) (Float_t.of_float Nearest 1.0), h) + (* [succ(ceil(l) - 1.0), h] would be even more precise, in case abstract and concrete have same precision (float/double). Does not work for more precise type though (e.g. long double) *) + else + (* if we knew the abstract and concrete precision are the same, we could return [l, h] as an interval, since no x in [l - 1.0, l] could exist such that ceil(x) = l appart from l itself *) + Interval (Float_t.pred l, h) + + let eval_inv_floor = function + | (l, h) -> + if (Float_t.sub Up (Float_t.add Up (Float_t.floor h) (Float_t.of_float Nearest 1.0)) (Float_t.floor h) = (Float_t.of_float Nearest 1.0)) then + (* if [(floor(h) + 1.0) - floor(h) = 1.0], then we are in a range, where each int is expressable as float. + With that we can say, that [(floor(x) <= h) => (x < (floor(h) + 1.0)] *) + Interval (l, Float_t.add Up (Float_t.floor h) (Float_t.of_float Nearest 1.0)) + (* [l, pred(floor(h) + 1.0)] would be even more precise, in case abstract and concrete have same precision (float/double). Does not work for more precise type though (e.g. long double) *) + else + (* if we knew the abstract and concrete precision are the same, we could return [l, h] as an interval, since no x in [h, h+1.0] could exist such that floor(x) = h appart from h itself *) + Interval (l, Float_t.succ h) let eval_inv_fabs = function | (_, h) when h < Float_t.zero -> Bot (* Result of fabs cannot be negative *) diff --git a/tests/regression/57-floats/19-library-invariant.c b/tests/regression/57-floats/19-library-invariant.c index 1bf332aba9..35ccbc8dcf 100644 --- a/tests/regression/57-floats/19-library-invariant.c +++ b/tests/regression/57-floats/19-library-invariant.c @@ -46,18 +46,20 @@ void main() { __goblint_check(f >= -5.); __goblint_check(f <= 5.); } - if(__builtin_fabs(f) == -6.) { //WARN (dead branch) + if(__builtin_fabs(f) == -6.) { // WARN (dead branch) g = 0.; } // ceil, floor if(ceil(f) == 5.) { __goblint_check(f <= 5.); + __goblint_check(f >= 4.); __goblint_check(f > 4.); // TODO __goblint_check(f >= 4.5); // UNKNOWN! } if(floor(f) == 5.) { __goblint_check(f >= 5.); + __goblint_check(f <= 6.); __goblint_check(f < 6.); // TODO __goblint_check(f >= 5.5); // UNKNOWN! } From fe2cdffb4849be983ebce91a81d48bbd224868c0 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 1 Aug 2023 14:47:19 +0300 Subject: [PATCH 0364/1312] Add missing argument to __builtin___memmove_chk --- src/analyses/libraryFunctions.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 3cd3b308cd..8d809ba46a 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -19,7 +19,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__builtin___mempcpy_chk", unknown [drop "dest" [w]; drop "src" [r]; drop "n" []; drop "os" []]); ("memmove", unknown [drop "dest" [w]; drop "src" [r]; drop "count" []]); ("__builtin_memmove", unknown [drop "dest" [w]; drop "src" [r]; drop "count" []]); - ("__builtin___memmove_chk", unknown [drop "dest" [w]; drop "src" [r]; drop "count" []]); + ("__builtin___memmove_chk", unknown [drop "dest" [w]; drop "src" [r]; drop "count" []; drop "os" []]); ("strcpy", special [__ "dest" [w]; __ "src" [r]] @@ fun dest src -> Strcpy { dest; src; n = None; }); ("__builtin_strcpy", special [__ "dest" [w]; __ "src" [r]] @@ fun dest src -> Strcpy { dest; src; n = None; }); ("__builtin___strcpy_chk", special [__ "dest" [w]; __ "src" [r]; drop "os" []] @@ fun dest src -> Strcpy { dest; src; n = None; }); From 2efaf16f5f7b2ba36008b50cec0bed405ef8bfd5 Mon Sep 17 00:00:00 2001 From: FelixKrayer Date: Tue, 1 Aug 2023 11:21:47 +0200 Subject: [PATCH 0365/1312] more sophisticated floor/ceil --- src/cdomains/floatDomain.ml | 65 +++++++--- src/cdomains/floatDomain.mli | 4 +- .../57-floats/19-library-invariant.c | 6 +- .../21-library-invariant-ceil-floor.c | 122 ++++++++++++++++++ 4 files changed, 172 insertions(+), 25 deletions(-) create mode 100644 tests/regression/57-floats/21-library-invariant-ceil-floor.c diff --git a/src/cdomains/floatDomain.ml b/src/cdomains/floatDomain.ml index 1b674a667f..baae801585 100644 --- a/src/cdomains/floatDomain.ml +++ b/src/cdomains/floatDomain.ml @@ -42,9 +42,9 @@ module type FloatArith = sig (** tan(x) *) (** {inversions of unary functions}*) - val inv_ceil : t -> t + val inv_ceil : ?asPreciseAsConcrete:bool -> t -> t (** (inv_ceil z -> x) if (z = ceil(x)) *) - val inv_floor : t -> t + val inv_floor : ?asPreciseAsConcrete:bool -> t -> t (** (inv_floor z -> x) if (z = floor(x)) *) val inv_fabs : t -> t (** (inv_fabs z -> x) if (z = fabs(x)) *) @@ -670,27 +670,39 @@ module FloatIntervalImpl(Float_t : CFloatType) = struct | (l, h) when l = h && l = Float_t.zero -> of_const 0. (*tan(0) = 0*) | _ -> top () (**could be exact for intervals where l=h, or even for some intervals *) - let eval_inv_ceil = function + let eval_inv_ceil ?(asPreciseAsConcrete=false) = function | (l, h) -> if (Float_t.sub Up (Float_t.ceil l) (Float_t.sub Down (Float_t.ceil l) (Float_t.of_float Nearest 1.0)) = (Float_t.of_float Nearest 1.0)) then (* if [ceil(l) - (ceil(l) - 1.0) = 1.0], then we are in a range, where each int is expressable as float. With that we can say, that [(ceil(x) >= l) => (x > (ceil(l) - 1.0)] *) - Interval (Float_t.sub Down (Float_t.ceil l) (Float_t.of_float Nearest 1.0), h) - (* [succ(ceil(l) - 1.0), h] would be even more precise, in case abstract and concrete have same precision (float/double). Does not work for more precise type though (e.g. long double) *) + if asPreciseAsConcrete then + (* in case abstract and concrete precision are the same, [succ(l - 1.0), h] is more precise *) + Interval (Float_t.succ (Float_t.sub Down (Float_t.ceil l) (Float_t.of_float Nearest 1.0)), h) + else + Interval (Float_t.sub Down (Float_t.ceil l) (Float_t.of_float Nearest 1.0), h) else - (* if we knew the abstract and concrete precision are the same, we could return [l, h] as an interval, since no x in [l - 1.0, l] could exist such that ceil(x) = l appart from l itself *) - Interval (Float_t.pred l, h) + (* if we know the abstract and concrete precision are the same, we return [l, h] as an interval, since no x in [l - 1.0, l] could exist such that ceil(x) = l appart from l itself *) + if asPreciseAsConcrete then + Interval (l, h) + else + Interval (Float_t.pred l, h) - let eval_inv_floor = function + let eval_inv_floor ?(asPreciseAsConcrete=false) = function | (l, h) -> if (Float_t.sub Up (Float_t.add Up (Float_t.floor h) (Float_t.of_float Nearest 1.0)) (Float_t.floor h) = (Float_t.of_float Nearest 1.0)) then (* if [(floor(h) + 1.0) - floor(h) = 1.0], then we are in a range, where each int is expressable as float. With that we can say, that [(floor(x) <= h) => (x < (floor(h) + 1.0)] *) - Interval (l, Float_t.add Up (Float_t.floor h) (Float_t.of_float Nearest 1.0)) - (* [l, pred(floor(h) + 1.0)] would be even more precise, in case abstract and concrete have same precision (float/double). Does not work for more precise type though (e.g. long double) *) + if asPreciseAsConcrete then + (* in case abstract and concrete precision are the same, [l, pred(floor(h) + 1.0)] is more precise than [l, floor(h) + 1.0] *) + Interval (l, Float_t.pred (Float_t.add Up (Float_t.floor h) (Float_t.of_float Nearest 1.0))) + else + Interval (l, Float_t.add Up (Float_t.floor h) (Float_t.of_float Nearest 1.0)) else - (* if we knew the abstract and concrete precision are the same, we could return [l, h] as an interval, since no x in [h, h+1.0] could exist such that floor(x) = h appart from h itself *) - Interval (l, Float_t.succ h) + (* if we know the abstract and concrete precision are the same, we return [l, h] as an interval, since no x in [h, h + 1.0] could exist such that floor(x) = h appart from h itself *) + if asPreciseAsConcrete then + Interval (l, h) + else + Interval (l, Float_t.succ h) let eval_inv_fabs = function | (_, h) when h < Float_t.zero -> Bot (* Result of fabs cannot be negative *) @@ -769,8 +781,8 @@ module FloatIntervalImpl(Float_t : CFloatType) = struct let sin = eval_unop eval_sin let tan = eval_unop eval_tan - let inv_ceil = eval_unop ~warn:false eval_inv_ceil - let inv_floor = eval_unop ~warn:false eval_inv_floor + let inv_ceil ?(asPreciseAsConcrete=false) = eval_unop ~warn:false (eval_inv_ceil ~asPreciseAsConcrete:asPreciseAsConcrete) + let inv_floor ?(asPreciseAsConcrete=false) = eval_unop ~warn:false (eval_inv_floor ~asPreciseAsConcrete:asPreciseAsConcrete) let inv_fabs op = match op with | Bot -> raise (ArithmeticOnFloatBot (Printf.sprintf "unop %s" (show op))) @@ -883,8 +895,19 @@ module FloatIntervalImplLifted = struct let cos = lift (F1.cos, F2.cos) let sin = lift (F1.sin, F2.sin) let tan = lift (F1.tan, F2.tan) - let inv_ceil = lift (F1.inv_ceil, F2.inv_ceil) - let inv_floor = lift (F1.inv_floor, F2.inv_floor) + + let inv_ceil ?(asPreciseAsConcrete=BoolDomain.MustBool.top ()) = function + | F32 a -> F32 (F1.inv_ceil ~asPreciseAsConcrete:true a) + | F64 a -> F64 (F2.inv_ceil ~asPreciseAsConcrete:true a) + | FLong a -> FLong (F2.inv_ceil a) + | FFloat128 a -> FFloat128 (F2.inv_ceil a) + + let inv_floor ?(asPreciseAsConcrete=BoolDomain.MustBool.top ()) = function + | F32 a -> F32 (F1.inv_floor ~asPreciseAsConcrete:true a) + | F64 a -> F64 (F2.inv_floor ~asPreciseAsConcrete:true a) + | FLong a -> FLong (F2.inv_floor a) + | FFloat128 a -> FFloat128 (F2.inv_floor a) + let inv_fabs = lift (F1.inv_fabs, F2.inv_fabs) let add = lift2 (F1.add, F2.add) let sub = lift2 (F1.sub, F2.sub) @@ -1133,10 +1156,12 @@ module FloatDomTupleImpl = struct let tan = map { f1= (fun (type a) (module F : FloatDomain with type t = a) -> F.tan); } - let inv_ceil = - map { f1= (fun (type a) (module F : FloatDomain with type t = a) -> F.inv_ceil); } - let inv_floor = - map { f1= (fun (type a) (module F : FloatDomain with type t = a) -> F.inv_floor); } + (*"asPreciseAsConcrete" has no meaning here*) + let inv_ceil ?(asPreciseAsConcrete=BoolDomain.MustBool.top ()) = + map { f1= (fun (type a) (module F : FloatDomain with type t = a) -> F.inv_ceil ~asPreciseAsConcrete:(BoolDomain.MustBool.top ())); } + (*"asPreciseAsConcrete" has no meaning here*) + let inv_floor ?(asPreciseAsConcrete=BoolDomain.MustBool.top ()) = + map { f1= (fun (type a) (module F : FloatDomain with type t = a) -> F.inv_floor ~asPreciseAsConcrete:(BoolDomain.MustBool.top ())); } let inv_fabs = map { f1= (fun (type a) (module F : FloatDomain with type t = a) -> F.inv_fabs); } diff --git a/src/cdomains/floatDomain.mli b/src/cdomains/floatDomain.mli index 4052f633a7..32b850a7b3 100644 --- a/src/cdomains/floatDomain.mli +++ b/src/cdomains/floatDomain.mli @@ -43,9 +43,9 @@ module type FloatArith = sig (** tan(x) *) (** {inversions of unary functions}*) - val inv_ceil : t -> t + val inv_ceil : ?asPreciseAsConcrete:bool -> t -> t (** (inv_ceil z -> x) if (z = ceil(x)) *) - val inv_floor : t -> t + val inv_floor : ?asPreciseAsConcrete:bool -> t -> t (** (inv_floor z -> x) if (z = floor(x)) *) val inv_fabs : t -> t (** (inv_fabs z -> x) if (z = fabs(x)) *) diff --git a/tests/regression/57-floats/19-library-invariant.c b/tests/regression/57-floats/19-library-invariant.c index 35ccbc8dcf..93c133ce19 100644 --- a/tests/regression/57-floats/19-library-invariant.c +++ b/tests/regression/57-floats/19-library-invariant.c @@ -54,13 +54,13 @@ void main() { if(ceil(f) == 5.) { __goblint_check(f <= 5.); __goblint_check(f >= 4.); - __goblint_check(f > 4.); // TODO + __goblint_check(f > 4.); __goblint_check(f >= 4.5); // UNKNOWN! } if(floor(f) == 5.) { __goblint_check(f >= 5.); __goblint_check(f <= 6.); - __goblint_check(f < 6.); // TODO - __goblint_check(f >= 5.5); // UNKNOWN! + __goblint_check(f < 6.); + __goblint_check(f <= 5.5); // UNKNOWN! } } diff --git a/tests/regression/57-floats/21-library-invariant-ceil-floor.c b/tests/regression/57-floats/21-library-invariant-ceil-floor.c new file mode 100644 index 0000000000..040f8c5566 --- /dev/null +++ b/tests/regression/57-floats/21-library-invariant-ceil-floor.c @@ -0,0 +1,122 @@ +//PARAM: --enable ana.float.interval --set ana.activated[+] tmpSpecial +#include +#include +#include + +void main() { + float f; + double d; + long double ld; + + if(ceilf(f) == 5.f) { + __goblint_check(f >= 4.f); + __goblint_check(f > 4.f); + __goblint_check(f >= 4.5f); // UNKNOWN! + } + if(floorf(f) == 5.f) { + __goblint_check(f <= 6.f); + __goblint_check(f < 6.f); + __goblint_check(f <= 5.5f); // UNKNOWN! + } + + if(ceil(d) == 5.) { + __goblint_check(d >= 4.); + __goblint_check(d > 4.); + __goblint_check(d <= 4.5); // UNKNOWN! + } + if(floor(d) == 5.) { + __goblint_check(d <= 6.); + __goblint_check(d < 6.); + __goblint_check(d <= 5.5); // UNKNOWN! + } + + if(ceill(ld) == 5.l) { + __goblint_check(ld >= 4.l); + __goblint_check(ld > 4.l); // UNKNOWN + __goblint_check(ld >= 4.5l); // UNKNOWN! + } + if(floorl(ld) == 5.l) { + __goblint_check(ld <= 6.l); + __goblint_check(ld < 6.l); // UNKNOWN + __goblint_check(ld <= 5.5l); // UNKNOWN! + } + + // Edge cases: + // 9007199254740992.0 = 2^53; up to here all integer values are representable in double. + // 2^53+1 is the first that is not representable as double, only as a long double + long double max_int_l = 9007199254740992.0l; + + if(floorl(ld) == max_int_l) { + //floorl(ld) == 2^53 => ld in [2^53, 2^53 + 1.0]. This is not representable in double, so Goblint computes with ld in [2^53, 2^53 + 2.0] + __goblint_check(ld <= (max_int_l + 2.0l)); + // as long as we abstract long doubles with intervals of doubles, the next should be UNKNOWN. + __goblint_check(ld <= (max_int_l + 1.0l)); // UNKNOWN + } + if(ceill(ld) == - max_int_l) { + // analogous to explanation above but with negative signbit + __goblint_check(ld >= (- max_int_l - 2.0l)); + // as long as we abstract long doubles with intervals of doubles, the next should be UNKNOWN + __goblint_check(ld >= (- max_int_l - 1.0l)); // UNKNOWN + } + + // 4503599627370496.0 = 2^52; from here up to 2^53 double is not able to represent any fractional part, i.e., only integers + // 2^52 + 0.5 is not representable as double, only as long double + long double no_fractional_l = 4503599627370496.0l; + + if(floorl(ld) == no_fractional_l) { + // floorl(ld) == 2^52 => ld < 2^52 + 1.0. + // If ld were a double, Goblint could compute with ld < pred(2^52 + 1.0), since we know no double can exist between pred(2^52 + 1.0) and 2^52 + 1.0. + // However for long double this does not hold, ase e.g. (2^52 + 0.5) is representable. + __goblint_check(ld <= (no_fractional_l + 1.0l)); + // as long as we abstract long doubles with intervals of doubles, the next should be UNKNOWN. + __goblint_check(ld < (no_fractional_l + 1.0l)); // UNKNOWN + } + if(ceill(ld) == - no_fractional_l) { + // analogous to explanation above but with negative signbit + __goblint_check(ld >= (- no_fractional_l - 1.0l)); + // as long as we abstract long doubles with intervals of doubles, the next should be UNKNOWN. + __goblint_check(ld > (- no_fractional_l - 1.0l)); // UNKNOWN + } + + // same tests, but this time with doubles. Here we can use the knowledge, which values are not representable + double max_int = (double)max_int_l; + if(floor(d) == max_int) { + __goblint_check(d <= (max_int + 2.0)); + __goblint_check(d <= (max_int + 1.0)); + } + if(ceil(d) == - max_int) { + __goblint_check(d >= (- max_int - 2.0)); + __goblint_check(d >= (- max_int - 1.0)); + } + + double no_fractional = (double)no_fractional_l; + if(floor(d) == no_fractional) { + __goblint_check(d <= (no_fractional + 1.0)); + __goblint_check(d < (no_fractional + 1.0)); + } + if(ceil(d) == - no_fractional) { + __goblint_check(d >= (- no_fractional - 1.0)); + __goblint_check(d > (- no_fractional - 1.0)); + } + + // same for float + float max_int_f = 16777216.0f; // 2^24 + if(floorf(f) == max_int_f) { + __goblint_check(f <= (max_int_f + 2.0f)); + __goblint_check(f <= (max_int_f + 1.0f)); + } + if(ceilf(f) == - max_int_f) { + __goblint_check(f >= (- max_int_f - 2.0f)); + __goblint_check(f >= (- max_int_f - 1.0f)); + } + + float no_fractional_f = 8388608.0f; // 2^23 + if(floorf(f) == no_fractional_f) { + __goblint_check(f <= (no_fractional_f + 1.0f)); + __goblint_check(f < (no_fractional_f + 1.0f)); + } + if(ceilf(f) == - no_fractional_f) { + __goblint_check(f >= (- no_fractional_f - 1.0f)); + __goblint_check(f > (- no_fractional_f - 1.0f)); + } +} From 07a7645df3dbdc696c31f902ae36e70d508db258 Mon Sep 17 00:00:00 2001 From: FelixKrayer Date: Tue, 1 Aug 2023 22:10:55 +0200 Subject: [PATCH 0366/1312] fix indentation --- src/analyses/baseInvariant.ml | 352 +++++++++++++++++----------------- src/analyses/tmpSpecial.ml | 209 ++++++++++---------- 2 files changed, 280 insertions(+), 281 deletions(-) diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index 9bcd011f97..096d50b89b 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -588,202 +588,202 @@ struct Some (s1 @ s2) | _ -> None - in - (* find common exp from all equality pairs and list of other sides, if possible *) - let find_common = function - | [] -> assert false - | (e1, e2) :: eqs -> - let eqs_for_all_mem e = List.for_all (fun (e1, e2) -> CilType.Exp.(equal e1 e || equal e2 e)) eqs in - let eqs_map_remove e = List.map (fun (e1, e2) -> if CilType.Exp.equal e1 e then e2 else e1) eqs in - if eqs_for_all_mem e1 then - Some (e1, e2 :: eqs_map_remove e1) - else if eqs_for_all_mem e2 then - Some (e2, e1 :: eqs_map_remove e2) - else - None - in - let eqs_st = - let* eqs = split exp in - let* (e, es) = find_common eqs in - let v = eval e st in (* value of common exp *) - let vs = List.map (fun e -> eval e st) es in (* values of other sides *) - match v with - | Address _ -> - (* get definite addrs from vs *) - let rec to_definite_ad = function - | [] -> AD.empty () - | VD.Address a :: vs when AD.is_definite a -> - AD.union a (to_definite_ad vs) - | _ :: vs -> - AD.top () - in - let definite_ad = to_definite_ad vs in - let c' = VD.Address definite_ad in - Some (inv_exp c' e st) - | Int i -> - let ik = ID.ikind i in - let module BISet = IntDomain.BISet in - (* get definite ints from vs *) - let rec to_int_id = function - | [] -> ID.bot_of ik - | VD.Int i :: vs -> - begin match ID.to_int i with - | Some i' -> ID.join i (to_int_id vs) - | None -> ID.top_of ik - end - | _ :: vs -> - ID.top_of ik - in - let int_id = to_int_id vs in - let c' = VD.Int int_id in - Some (inv_exp c' e st) - | _ -> + in + (* find common exp from all equality pairs and list of other sides, if possible *) + let find_common = function + | [] -> assert false + | (e1, e2) :: eqs -> + let eqs_for_all_mem e = List.for_all (fun (e1, e2) -> CilType.Exp.(equal e1 e || equal e2 e)) eqs in + let eqs_map_remove e = List.map (fun (e1, e2) -> if CilType.Exp.equal e1 e then e2 else e1) eqs in + if eqs_for_all_mem e1 then + Some (e1, e2 :: eqs_map_remove e1) + else if eqs_for_all_mem e2 then + Some (e2, e1 :: eqs_map_remove e2) + else None - in - begin match eqs_st with - | Some st -> st - | None when ID.to_bool c = Some true -> - begin match inv_exp (Int c) arg1 st with - | st1 -> - begin match inv_exp (Int c) arg2 st with - | st2 -> D.join st1 st2 - | exception Analyses.Deadcode -> st1 - end - | exception Analyses.Deadcode -> inv_exp (Int c) arg2 st (* Deadcode falls through *) - end - | None -> - st (* TODO: not bothering to fall back, no other case can refine LOr anyway *) - end - | (BinOp (op, e1, e2, _) as e, Float _) - | (BinOp (op, e1, e2, _) as e, Int _) -> - let invert_binary_op c pretty c_int c_float = - if M.tracing then M.tracel "inv" "binop %a with %a %a %a == %a\n" d_exp e VD.pretty (eval e1 st) d_binop op VD.pretty (eval e2 st) pretty c; - (match eval e1 st, eval e2 st with - | Int a, Int b -> - let ikind = Cilfacade.get_ikind_exp e1 in (* both operands have the same type (except for Shiftlt, Shiftrt)! *) - let ikres = Cilfacade.get_ikind_exp e in (* might be different from argument types, e.g. for LT, GT, EQ, ... *) - let a', b' = inv_bin_int (a, b) ikind (c_int ikres) op in - if M.tracing then M.tracel "inv" "binop: %a, c: %a, a': %a, b': %a\n" d_exp e ID.pretty (c_int ikind) ID.pretty a' ID.pretty b'; - let st' = inv_exp (Int a') e1 st in - let st'' = inv_exp (Int b') e2 st' in - st'' - | Float a, Float b -> - let fkind = Cilfacade.get_fkind_exp e1 in (* both operands have the same type *) - let a', b' = inv_bin_float (a, b) (c_float fkind) op in - if M.tracing then M.tracel "inv" "binop: %a, c: %a, a': %a, b': %a\n" d_exp e FD.pretty (c_float fkind) FD.pretty a' FD.pretty b'; - let st' = inv_exp (Float a') e1 st in - let st'' = inv_exp (Float b') e2 st' in - st'' - (* Mixed Float and Int cases should never happen, as there are no binary operators with one float and one int parameter ?!*) - | Int _, Float _ | Float _, Int _ -> failwith "ill-typed program"; - (* | Address a, Address b -> ... *) - | a1, a2 -> fallback (GobPretty.sprintf "binop: got abstract values that are not Int: %a and %a" VD.pretty a1 VD.pretty a2) st) - (* use closures to avoid unused casts *) - in (match c_typed with - | Int c -> invert_binary_op c ID.pretty (fun ik -> ID.cast_to ik c) (fun fk -> FD.of_int fk c) - | Float c -> invert_binary_op c FD.pretty (fun ik -> FD.to_int ik c) (fun fk -> FD.cast_to fk c) - | _ -> failwith "unreachable") - | Lval x, (Int _ | Float _ | Address _) -> (* meet x with c *) - let update_lval c x c' pretty = refine_lv ctx a gs st c x c' pretty exp in - let t = Cil.unrollType (Cilfacade.typeOfLval x) in (* unroll type to deal with TNamed *) - if M.tracing then M.trace "invSpecial" "invariant with Lval %a, c_typed %a, type %a\n" d_lval x VD.pretty c_typed d_type t; - begin match c_typed with - | Int c -> - let c' = match t with - | TPtr _ -> VD.Address (AD.of_int c) - | TInt (ik, _) - | TEnum ({ekind = ik; _}, _) -> Int (ID.cast_to ik c) - | TFloat (fk, _) -> Float (FD.of_int fk c) - | _ -> Int c - in - (* handle special calls *) - begin match t with + in + let eqs_st = + let* eqs = split exp in + let* (e, es) = find_common eqs in + let v = eval e st in (* value of common exp *) + let vs = List.map (fun e -> eval e st) es in (* values of other sides *) + match v with + | Address _ -> + (* get definite addrs from vs *) + let rec to_definite_ad = function + | [] -> AD.empty () + | VD.Address a :: vs when AD.is_definite a -> + AD.union a (to_definite_ad vs) + | _ :: vs -> + AD.top () + in + let definite_ad = to_definite_ad vs in + let c' = VD.Address definite_ad in + Some (inv_exp c' e st) + | Int i -> + let ik = ID.ikind i in + let module BISet = IntDomain.BISet in + (* get definite ints from vs *) + let rec to_int_id = function + | [] -> ID.bot_of ik + | VD.Int i :: vs -> + begin match ID.to_int i with + | Some i' -> ID.join i (to_int_id vs) + | None -> ID.top_of ik + end + | _ :: vs -> + ID.top_of ik + in + let int_id = to_int_id vs in + let c' = VD.Int int_id in + Some (inv_exp c' e st) + | _ -> + None + in + begin match eqs_st with + | Some st -> st + | None when ID.to_bool c = Some true -> + begin match inv_exp (Int c) arg1 st with + | st1 -> + begin match inv_exp (Int c) arg2 st with + | st2 -> D.join st1 st2 + | exception Analyses.Deadcode -> st1 + end + | exception Analyses.Deadcode -> inv_exp (Int c) arg2 st (* Deadcode falls through *) + end + | None -> + st (* TODO: not bothering to fall back, no other case can refine LOr anyway *) + end + | (BinOp (op, e1, e2, _) as e, Float _) + | (BinOp (op, e1, e2, _) as e, Int _) -> + let invert_binary_op c pretty c_int c_float = + if M.tracing then M.tracel "inv" "binop %a with %a %a %a == %a\n" d_exp e VD.pretty (eval e1 st) d_binop op VD.pretty (eval e2 st) pretty c; + (match eval e1 st, eval e2 st with + | Int a, Int b -> + let ikind = Cilfacade.get_ikind_exp e1 in (* both operands have the same type (except for Shiftlt, Shiftrt)! *) + let ikres = Cilfacade.get_ikind_exp e in (* might be different from argument types, e.g. for LT, GT, EQ, ... *) + let a', b' = inv_bin_int (a, b) ikind (c_int ikres) op in + if M.tracing then M.tracel "inv" "binop: %a, c: %a, a': %a, b': %a\n" d_exp e ID.pretty (c_int ikind) ID.pretty a' ID.pretty b'; + let st' = inv_exp (Int a') e1 st in + let st'' = inv_exp (Int b') e2 st' in + st'' + | Float a, Float b -> + let fkind = Cilfacade.get_fkind_exp e1 in (* both operands have the same type *) + let a', b' = inv_bin_float (a, b) (c_float fkind) op in + if M.tracing then M.tracel "inv" "binop: %a, c: %a, a': %a, b': %a\n" d_exp e FD.pretty (c_float fkind) FD.pretty a' FD.pretty b'; + let st' = inv_exp (Float a') e1 st in + let st'' = inv_exp (Float b') e2 st' in + st'' + (* Mixed Float and Int cases should never happen, as there are no binary operators with one float and one int parameter ?!*) + | Int _, Float _ | Float _, Int _ -> failwith "ill-typed program"; + (* | Address a, Address b -> ... *) + | a1, a2 -> fallback (GobPretty.sprintf "binop: got abstract values that are not Int: %a and %a" VD.pretty a1 VD.pretty a2) st) + (* use closures to avoid unused casts *) + in (match c_typed with + | Int c -> invert_binary_op c ID.pretty (fun ik -> ID.cast_to ik c) (fun fk -> FD.of_int fk c) + | Float c -> invert_binary_op c FD.pretty (fun ik -> FD.to_int ik c) (fun fk -> FD.cast_to fk c) + | _ -> failwith "unreachable") + | Lval x, (Int _ | Float _ | Address _) -> (* meet x with c *) + let update_lval c x c' pretty = refine_lv ctx a gs st c x c' pretty exp in + let t = Cil.unrollType (Cilfacade.typeOfLval x) in (* unroll type to deal with TNamed *) + if M.tracing then M.trace "invSpecial" "invariant with Lval %a, c_typed %a, type %a\n" d_lval x VD.pretty c_typed d_type t; + begin match c_typed with + | Int c -> + let c' = match t with + | TPtr _ -> VD.Address (AD.of_int c) + | TInt (ik, _) + | TEnum ({ekind = ik; _}, _) -> Int (ID.cast_to ik c) + | TFloat (fk, _) -> Float (FD.of_int fk c) + | _ -> Int c + in + (* handle special calls *) + begin match t with | TInt (ik, _) -> begin match x with | ((Var v), offs) -> if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs))); let tv_opt = ID.to_bool c in begin match tv_opt with - | Some tv -> - begin match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs)) with - | `Lifted (Isfinite xFloat) when tv -> inv_exp (Float (FD.finite (unroll_fk_of_exp xFloat))) xFloat st - | `Lifted (Isnan xFloat) when tv -> inv_exp (Float (FD.nan_of (unroll_fk_of_exp xFloat))) xFloat st - (* should be correct according to C99 standard*) - | `Lifted (Isgreater (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))) st - | `Lifted (Isgreaterequal (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Ge, xFloat, yFloat, (typeOf xFloat))) st - | `Lifted (Isless (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))) st - | `Lifted (Islessequal (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Le, xFloat, yFloat, (typeOf xFloat))) st - | `Lifted (Islessgreater (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (LOr, (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))), (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))), (TInt (IBool, [])))) st - | _ -> update_lval c x c' ID.pretty - end - | None -> update_lval c x c' ID.pretty + | Some tv -> + begin match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs)) with + | `Lifted (Isfinite xFloat) when tv -> inv_exp (Float (FD.finite (unroll_fk_of_exp xFloat))) xFloat st + | `Lifted (Isnan xFloat) when tv -> inv_exp (Float (FD.nan_of (unroll_fk_of_exp xFloat))) xFloat st + (* should be correct according to C99 standard*) + | `Lifted (Isgreater (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))) st + | `Lifted (Isgreaterequal (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Ge, xFloat, yFloat, (typeOf xFloat))) st + | `Lifted (Isless (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))) st + | `Lifted (Islessequal (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Le, xFloat, yFloat, (typeOf xFloat))) st + | `Lifted (Islessgreater (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (LOr, (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))), (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))), (TInt (IBool, [])))) st + | _ -> update_lval c x c' ID.pretty + end + | None -> update_lval c x c' ID.pretty end | _ -> update_lval c x c' ID.pretty end | _ -> update_lval c x c' ID.pretty - end - | Float c -> - let c' = match t with - (* | TPtr _ -> ..., pointer conversion from/to float is not supported *) - | TInt (ik, _) -> VD.Int (FD.to_int ik c) - (* this is theoretically possible and should be handled correctly, however i can't imagine an actual piece of c code producing this?! *) - | TEnum ({ekind = ik; _}, _) -> Int (FD.to_int ik c) - | TFloat (fk, _) -> Float (FD.cast_to fk c) - | _ -> Float c - in - (* handle special calls *) - begin match t with + end + | Float c -> + let c' = match t with + (* | TPtr _ -> ..., pointer conversion from/to float is not supported *) + | TInt (ik, _) -> VD.Int (FD.to_int ik c) + (* this is theoretically possible and should be handled correctly, however i can't imagine an actual piece of c code producing this?! *) + | TEnum ({ekind = ik; _}, _) -> Int (FD.to_int ik c) + | TFloat (fk, _) -> Float (FD.cast_to fk c) + | _ -> Float c + in + (* handle special calls *) + begin match t with | TFloat (fk, _) -> begin match x with | ((Var v), offs) -> if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs))); begin match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs)) with - | `Lifted (Ceil (ret_fk, xFloat)) -> inv_exp (Float (FD.inv_ceil (FD.cast_to ret_fk c))) xFloat st - | `Lifted (Floor (ret_fk, xFloat)) -> inv_exp (Float (FD.inv_floor (FD.cast_to ret_fk c))) xFloat st - | `Lifted (Fabs (ret_fk, xFloat)) -> - let inv = FD.inv_fabs (FD.cast_to ret_fk c) in - if FD.is_bot inv then - raise Analyses.Deadcode - else - inv_exp (Float inv) xFloat st - | _ -> update_lval c x c' FD.pretty + | `Lifted (Ceil (ret_fk, xFloat)) -> inv_exp (Float (FD.inv_ceil (FD.cast_to ret_fk c))) xFloat st + | `Lifted (Floor (ret_fk, xFloat)) -> inv_exp (Float (FD.inv_floor (FD.cast_to ret_fk c))) xFloat st + | `Lifted (Fabs (ret_fk, xFloat)) -> + let inv = FD.inv_fabs (FD.cast_to ret_fk c) in + if FD.is_bot inv then + raise Analyses.Deadcode + else + inv_exp (Float inv) xFloat st + | _ -> update_lval c x c' FD.pretty end | _ -> update_lval c x c' FD.pretty end | _ -> update_lval c x c' FD.pretty - end - | Address c -> - let c' = c_typed in (* TODO: need any of the type-matching nonsense? *) - update_lval c x c' AD.pretty - | _ -> assert false - end - | Const _ , _ -> st (* nothing to do *) - | CastE ((TFloat (_, _)), e), Float c -> - (match unrollType (Cilfacade.typeOf e), FD.get_fkind c with - | TFloat (FLongDouble as fk, _), FFloat - | TFloat (FDouble as fk, _), FFloat - | TFloat (FLongDouble as fk, _), FDouble - | TFloat (fk, _), FLongDouble - | TFloat (FDouble as fk, _), FDouble - | TFloat (FFloat as fk, _), FFloat -> inv_exp (Float (FD.cast_to fk c)) e st - | _ -> fallback ("CastE: incompatible types") st) - | CastE ((TInt (ik, _)) as t, e), Int c - | CastE ((TEnum ({ekind = ik; _ }, _)) as t, e), Int c -> (* Can only meet the t part of an Lval in e with c (unless we meet with all overflow possibilities)! Since there is no good way to do this, we only continue if e has no values outside of t. *) - (match eval e st with - | Int i -> - if ID.leq i (ID.cast_to ik i) then - match unrollType (Cilfacade.typeOf e) with - | TInt(ik_e, _) - | TEnum ({ekind = ik_e; _ }, _) -> - (* let c' = ID.cast_to ik_e c in *) - let c' = ID.cast_to ik_e (ID.meet c (ID.cast_to ik (ID.top_of ik_e))) in (* TODO: cast without overflow, is this right for normal invariant? *) - if M.tracing then M.tracel "inv" "cast: %a from %a to %a: i = %a; cast c = %a to %a = %a\n" d_exp e d_ikind ik_e d_ikind ik ID.pretty i ID.pretty c d_ikind ik_e ID.pretty c'; - inv_exp (Int c') e st - | x -> fallback (GobPretty.sprintf "CastE: e did evaluate to Int, but the type did not match %a" CilType.Typ.pretty t) st - else - fallback (GobPretty.sprintf "CastE: %a evaluates to %a which is bigger than the type it is cast to which is %a" d_plainexp e ID.pretty i CilType.Typ.pretty t) st - | v -> fallback (GobPretty.sprintf "CastE: e did not evaluate to Int, but %a" VD.pretty v) st) - | e, _ -> fallback (GobPretty.sprintf "%a not implemented" d_plainexp e) st + end + | Address c -> + let c' = c_typed in (* TODO: need any of the type-matching nonsense? *) + update_lval c x c' AD.pretty + | _ -> assert false + end + | Const _ , _ -> st (* nothing to do *) + | CastE ((TFloat (_, _)), e), Float c -> + (match unrollType (Cilfacade.typeOf e), FD.get_fkind c with + | TFloat (FLongDouble as fk, _), FFloat + | TFloat (FDouble as fk, _), FFloat + | TFloat (FLongDouble as fk, _), FDouble + | TFloat (fk, _), FLongDouble + | TFloat (FDouble as fk, _), FDouble + | TFloat (FFloat as fk, _), FFloat -> inv_exp (Float (FD.cast_to fk c)) e st + | _ -> fallback ("CastE: incompatible types") st) + | CastE ((TInt (ik, _)) as t, e), Int c + | CastE ((TEnum ({ekind = ik; _ }, _)) as t, e), Int c -> (* Can only meet the t part of an Lval in e with c (unless we meet with all overflow possibilities)! Since there is no good way to do this, we only continue if e has no values outside of t. *) + (match eval e st with + | Int i -> + if ID.leq i (ID.cast_to ik i) then + match unrollType (Cilfacade.typeOf e) with + | TInt(ik_e, _) + | TEnum ({ekind = ik_e; _ }, _) -> + (* let c' = ID.cast_to ik_e c in *) + let c' = ID.cast_to ik_e (ID.meet c (ID.cast_to ik (ID.top_of ik_e))) in (* TODO: cast without overflow, is this right for normal invariant? *) + if M.tracing then M.tracel "inv" "cast: %a from %a to %a: i = %a; cast c = %a to %a = %a\n" d_exp e d_ikind ik_e d_ikind ik ID.pretty i ID.pretty c d_ikind ik_e ID.pretty c'; + inv_exp (Int c') e st + | x -> fallback (GobPretty.sprintf "CastE: e did evaluate to Int, but the type did not match %a" CilType.Typ.pretty t) st + else + fallback (GobPretty.sprintf "CastE: %a evaluates to %a which is bigger than the type it is cast to which is %a" d_plainexp e ID.pretty i CilType.Typ.pretty t) st + | v -> fallback (GobPretty.sprintf "CastE: e did not evaluate to Int, but %a" VD.pretty v) st) + | e, _ -> fallback (GobPretty.sprintf "%a not implemented" d_plainexp e) st in if eval_bool exp st = Some (not tv) then contra st (* we already know that the branch is dead *) else diff --git a/src/analyses/tmpSpecial.ml b/src/analyses/tmpSpecial.ml index 0798c30ad1..5d43f69add 100644 --- a/src/analyses/tmpSpecial.ml +++ b/src/analyses/tmpSpecial.ml @@ -1,106 +1,105 @@ (* Analysis that tracks which variables hold the results of calls to math library functions. - For each equivalence a set of expressions is tracked, that contains the arguments of the corresponding call as well as the Lval it is assigned to, so an equivalence can be removed if one of these expressions may be changed.*) - - module VarEq = VarEq.Spec - - open GoblintCil - open Analyses - - module Spec = - struct - include Analyses.IdentitySpec - - let name () = "tmpSpecial" - module ML = LibraryDesc.MathLifted - module Deps = SetDomain.Reverse (SetDomain.ToppedSet (CilType.Exp) (struct let topname = "All" end)) - module MLDeps = Lattice.Prod (ML) (Deps) - module D = MapDomain.MapBot (Mval.Exp) (MLDeps) - module C = Lattice.Unit - - let invalidate ask exp_w st = - D.filter (fun _ (ml, deps) -> (Deps.for_all (fun arg -> not (VarEq.may_change ask exp_w arg)) deps)) st - - let context _ _ = () - - (* transfer functions *) - let assign ctx (lval:lval) (rval:exp) : D.t = - if M.tracing then M.tracel "tmpSpecial" "assignment of %a\n" d_lval lval; - (* Invalidate all entrys from the map that are possibly written by the assignment *) - invalidate (Analyses.ask_of_ctx ctx) (mkAddrOf lval) ctx.local - - let branch ctx (exp:exp) (tv:bool) : D.t = - ctx.local - - let body ctx (f:fundec) : D.t = - ctx.local - - let return ctx (exp:exp option) (f:fundec) : D.t = - ctx.local - - let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = - (* For now we only track relationships intraprocedurally. TODO: handle interprocedural tracking *) - [ctx.local, D.bot ()] - - let combine ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) f_ask : D.t = - (* For now we only track relationships intraprocedurally. TODO: handle interprocedural tracking *) - D.bot () - - let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = - let d = ctx.local in - let ask = Analyses.ask_of_ctx ctx in - - (* Just dbg prints *) - (if M.tracing then - match lval with - | Some lv -> if M.tracing then M.tracel "tmpSpecial" "Special: %s with lval %a\n" f.vname d_lval lv - | _ -> if M.tracing then M.tracel "tmpSpecial" "Special: %s\n" f.vname); - - - let desc = LibraryFunctions.find f in - - (* remove entrys, dependent on lvals that were possibly written by the special function *) - let write_args = LibraryDesc.Accesses.find_kind desc.accs Write arglist in - (* TODO similar to symbLocks->Spec->special: why doesn't invalidate involve any reachable for deep write? *) - let d = List.fold_left (fun d e -> invalidate ask e d) d write_args in - - (* same for lval assignment of the call*) - let d = - match lval with - | Some lv -> invalidate ask (mkAddrOf lv) ctx.local - | None -> d - in - - (* add new math fun desc*) - let d = - match lval, desc.special arglist with - | Some ((Var v, offs) as lv), (Math { fun_args; }) -> - (* only add descriptor, if none of the args is written by the assignment, invalidating the equivalence *) - (* actually it would be necessary to check here, if one of the arguments is written by the call. However this is not the case for any of the math functions and no other functions are covered so far *) - if List.exists (fun arg -> VarEq.may_change ask (mkAddrOf lv) arg) arglist then - d - else - D.add (v, Offset.Exp.of_cil offs) ((ML.lift fun_args, Deps.of_list ((Lval lv)::arglist))) d - | _ -> d - - in - - if M.tracing then M.tracel "tmpSpecial" "Result: %a\n\n" D.pretty d; - d - - - let query ctx (type a) (q: a Queries.t) : a Queries.result = - match q with - | TmpSpecial lv -> let ml = fst (D.find lv ctx.local) in - if ML.is_bot ml then Queries.Result.top q - else ml - | _ -> Queries.Result.top q - - let startstate v = D.bot () - let threadenter ctx lval f args = [D.bot ()] - let threadspawn ctx lval f args fctx = ctx.local - let exitstate v = D.bot () - end - - let _ = - MCP.register_analysis (module Spec : MCPSpec) - \ No newline at end of file + For each equivalence a set of expressions is tracked, that contains the arguments of the corresponding call as well as the Lval it is assigned to, so an equivalence can be removed if one of these expressions may be changed.*) + +module VarEq = VarEq.Spec + +open GoblintCil +open Analyses + +module Spec = +struct + include Analyses.IdentitySpec + + let name () = "tmpSpecial" + module ML = LibraryDesc.MathLifted + module Deps = SetDomain.Reverse (SetDomain.ToppedSet (CilType.Exp) (struct let topname = "All" end)) + module MLDeps = Lattice.Prod (ML) (Deps) + module D = MapDomain.MapBot (Mval.Exp) (MLDeps) + module C = Lattice.Unit + + let invalidate ask exp_w st = + D.filter (fun _ (ml, deps) -> (Deps.for_all (fun arg -> not (VarEq.may_change ask exp_w arg)) deps)) st + + let context _ _ = () + + (* transfer functions *) + let assign ctx (lval:lval) (rval:exp) : D.t = + if M.tracing then M.tracel "tmpSpecial" "assignment of %a\n" d_lval lval; + (* Invalidate all entrys from the map that are possibly written by the assignment *) + invalidate (Analyses.ask_of_ctx ctx) (mkAddrOf lval) ctx.local + + let branch ctx (exp:exp) (tv:bool) : D.t = + ctx.local + + let body ctx (f:fundec) : D.t = + ctx.local + + let return ctx (exp:exp option) (f:fundec) : D.t = + ctx.local + + let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = + (* For now we only track relationships intraprocedurally. TODO: handle interprocedural tracking *) + [ctx.local, D.bot ()] + + let combine ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) f_ask : D.t = + (* For now we only track relationships intraprocedurally. TODO: handle interprocedural tracking *) + D.bot () + + let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = + let d = ctx.local in + let ask = Analyses.ask_of_ctx ctx in + + (* Just dbg prints *) + (if M.tracing then + match lval with + | Some lv -> if M.tracing then M.tracel "tmpSpecial" "Special: %s with lval %a\n" f.vname d_lval lv + | _ -> if M.tracing then M.tracel "tmpSpecial" "Special: %s\n" f.vname); + + + let desc = LibraryFunctions.find f in + + (* remove entrys, dependent on lvals that were possibly written by the special function *) + let write_args = LibraryDesc.Accesses.find_kind desc.accs Write arglist in + (* TODO similar to symbLocks->Spec->special: why doesn't invalidate involve any reachable for deep write? *) + let d = List.fold_left (fun d e -> invalidate ask e d) d write_args in + + (* same for lval assignment of the call*) + let d = + match lval with + | Some lv -> invalidate ask (mkAddrOf lv) ctx.local + | None -> d + in + + (* add new math fun desc*) + let d = + match lval, desc.special arglist with + | Some ((Var v, offs) as lv), (Math { fun_args; }) -> + (* only add descriptor, if none of the args is written by the assignment, invalidating the equivalence *) + (* actually it would be necessary to check here, if one of the arguments is written by the call. However this is not the case for any of the math functions and no other functions are covered so far *) + if List.exists (fun arg -> VarEq.may_change ask (mkAddrOf lv) arg) arglist then + d + else + D.add (v, Offset.Exp.of_cil offs) ((ML.lift fun_args, Deps.of_list ((Lval lv)::arglist))) d + | _ -> d + + in + + if M.tracing then M.tracel "tmpSpecial" "Result: %a\n\n" D.pretty d; + d + + + let query ctx (type a) (q: a Queries.t) : a Queries.result = + match q with + | TmpSpecial lv -> let ml = fst (D.find lv ctx.local) in + if ML.is_bot ml then Queries.Result.top q + else ml + | _ -> Queries.Result.top q + + let startstate v = D.bot () + let threadenter ctx lval f args = [D.bot ()] + let threadspawn ctx lval f args fctx = ctx.local + let exitstate v = D.bot () +end + +let _ = + MCP.register_analysis (module Spec : MCPSpec) From b02d0a743f51fd0afd79a6ba86eb46556bfc1e1f Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 2 Aug 2023 08:53:23 +0200 Subject: [PATCH 0367/1312] Remove threadJoins activation warning --- src/analyses/useAfterFree.ml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index 9a6484f624..437f19264b 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -31,16 +31,7 @@ struct let get_joined_threads ctx = ctx.ask Queries.MustJoinedThreads - let warn_about_deactivated_thread_joins () = - if not @@ List.mem "threadJoins" @@ get_string_list "ana.activated" then - M.warn "Running without thread joins analysis. Multi-threaded UAF detection might be imprecise!" - let warn_for_multi_threaded_access ctx (heap_var:varinfo) behavior cwe_number = - (* - * We need the [threadJoins] analysis for making the multi-threaded UAF detection more precise - * Warn the user in case [threadJoins] is disabled - *) - warn_about_deactivated_thread_joins (); let freeing_threads = ctx.global heap_var in (* If we're single-threaded or there are no threads freeing the memory, we have nothing to WARN about *) if ctx.ask (Queries.MustBeSingleThreaded { since_start = true }) || ThreadIdWithJoinedThreads.is_empty freeing_threads then () From 50f20b0e1db69285385c28fe54bfc30d758fc163 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 2 Aug 2023 09:55:10 +0200 Subject: [PATCH 0368/1312] Use a map domain for the globals --- src/analyses/useAfterFree.ml | 53 ++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index 437f19264b..c1f963b466 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -6,7 +6,7 @@ open Analyses open MessageCategory module ToppedVarInfoSet = SetDomain.ToppedSet(CilType.Varinfo)(struct let topname = "All Heap Variables" end) -module ThreadIdWithJoinedThreads = SetDomain.Make(Lattice.Prod(ThreadIdDomain.ThreadLifted)(ConcDomain.MustThreadSet)) +module ThreadIdToJoinedThreadsMap = MapDomain.MapBot(ThreadIdDomain.ThreadLifted)(ConcDomain.MustThreadSet) module Spec : Analyses.MCPSpec = struct @@ -16,7 +16,7 @@ struct module D = ToppedVarInfoSet module C = Lattice.Unit - module G = ThreadIdWithJoinedThreads + module G = ThreadIdToJoinedThreadsMap module V = VarinfoV (** TODO: Try out later in benchmarks to see how we perform with and without context-sensititivty *) @@ -34,35 +34,37 @@ struct let warn_for_multi_threaded_access ctx (heap_var:varinfo) behavior cwe_number = let freeing_threads = ctx.global heap_var in (* If we're single-threaded or there are no threads freeing the memory, we have nothing to WARN about *) - if ctx.ask (Queries.MustBeSingleThreaded { since_start = true }) || ThreadIdWithJoinedThreads.is_empty freeing_threads then () + if ctx.ask (Queries.MustBeSingleThreaded { since_start = true }) || G.is_empty freeing_threads then () else begin - let possibly_started current = function - | `Lifted tid, joined_threads -> + let possibly_started current tid joined_threads = + match tid with + | `Lifted tid -> let created_threads = ctx.ask Queries.CreatedThreads in (* Discard joined threads, as they're supposed to be joined before the point of freeing the memory *) let threads = ConcDomain.MustThreadSet.diff created_threads joined_threads in let not_started = MHP.definitely_not_started (current, threads) tid in let possibly_started = not not_started in possibly_started - | `Top, _ -> true - | `Bot, _ -> false + | `Top -> true + | `Bot -> false in - let equal_current current = function - | `Lifted tid, _ -> + let equal_current current tid joined_threads = + match tid with + | `Lifted tid -> ThreadId.Thread.equal current tid - | `Top, _ -> true - | `Bot, _ -> false + | `Top -> true + | `Bot -> false in match get_current_threadid ctx with | `Lifted current -> - let possibly_started = ThreadIdWithJoinedThreads.exists (possibly_started current) freeing_threads in + let possibly_started = G.exists (possibly_started current) freeing_threads in if possibly_started then begin AnalysisState.svcomp_may_use_after_free := true; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "There's a thread that's been started in parallel with the memory-freeing threads for heap variable %a. Use-After-Free might occur" CilType.Varinfo.pretty heap_var end else begin let current_is_unique = ThreadId.Thread.is_unique current in - let any_equal_current threads = ThreadIdWithJoinedThreads.exists (equal_current current) threads in + let any_equal_current threads = G.exists (equal_current current) threads in if not current_is_unique && any_equal_current freeing_threads then begin AnalysisState.svcomp_may_use_after_free := true; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Current thread is not unique and a Use-After-Free might occur for heap variable %a" CilType.Varinfo.pretty heap_var @@ -141,24 +143,17 @@ struct | AddrOf lval -> warn_lval_might_contain_freed ~is_double_free transfer_fn_name ctx lval let side_effect_mem_free ctx freed_heap_vars threadid joined_threads = - let side_effect_globals_to_heap_var heap_var globals_to_add = - let (tid_to_add, joined_threads_to_add) = globals_to_add in + let side_effect_globals_to_heap_var heap_var = let current_globals = ctx.global heap_var in - (* - * Check if there are tuples with the same first component (i.e., tid) - * If no, side-effect the globals that we receive here and be done - * If yes, join all second components together and side-effect a single global with - the tid as first component and the joined second components as a single second component - *) - let globals_with_same_tid = G.filter (fun (tid, _) -> ThreadIdDomain.ThreadLifted.equal tid tid_to_add) current_globals in - if G.is_empty globals_with_same_tid then - ctx.sideg heap_var (G.singleton globals_to_add) - else - let globals_to_add = G.fold (fun (t, j) (t_acc, j_acc) -> (t_acc, ConcDomain.MustThreadSet.join j j_acc)) globals_with_same_tid (tid_to_add, joined_threads_to_add) in - ctx.sideg heap_var (G.singleton globals_to_add) + let joined_threads_to_add = match G.find_opt threadid current_globals with + | Some threads -> ConcDomain.ThreadSet.inter joined_threads threads + | None -> joined_threads + in + let globals_to_side_effect = G.add threadid joined_threads_to_add current_globals in + ctx.sideg heap_var globals_to_side_effect in - let globals_to_side_effect = (threadid, joined_threads) in - D.iter (fun var -> side_effect_globals_to_heap_var var globals_to_side_effect) freed_heap_vars + D.iter side_effect_globals_to_heap_var freed_heap_vars + (* TRANSFER FUNCTIONS *) From 027b9cda653b95e63f904b417256da1b476546bb Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 2 Aug 2023 11:41:35 +0300 Subject: [PATCH 0369/1312] Improve solside tracing --- src/framework/analyses.ml | 1 + src/solvers/td3.ml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index dd57f40c70..df3346af93 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -75,6 +75,7 @@ end module GVarF (V: SpecSysVar) = struct include Printable.Either (V) (CilType.Fundec) + let name () = "FromSpec" let spec x = `Left x let contexts x = `Right x diff --git a/src/solvers/td3.ml b/src/solvers/td3.ml index f0a728f73b..9621f69db8 100644 --- a/src/solvers/td3.ml +++ b/src/solvers/td3.ml @@ -429,7 +429,7 @@ module Base = if tracing then trace "sol2" "stable add %a\n" S.Var.pretty_trace y; HM.replace stable y (); if not (S.Dom.leq tmp old) then ( - if tracing && not (S.Dom.is_bot old) then trace "solside" "side to %a (wpx: %b) from %a\n" S.Var.pretty_trace y (HM.mem wpoint y) (Pretty.docOpt (S.Var.pretty_trace ())) x; + if tracing && not (S.Dom.is_bot old) then trace "solside" "side to %a (wpx: %b) from %a: %a -> %a\n" S.Var.pretty_trace y (HM.mem wpoint y) (Pretty.docOpt (S.Var.pretty_trace ())) x S.Dom.pretty old S.Dom.pretty tmp; let sided = match x with | Some x -> let sided = VS.mem x old_sides in From 152f34eebcc29471f43f4f7f49597b4b64fa5354 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 2 Aug 2023 11:43:07 +0300 Subject: [PATCH 0370/1312] Handle all_index in evalbinop_base --- src/analyses/base.ml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 1808014654..5b7a16e002 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -397,6 +397,8 @@ struct Int (if AD.is_bot (AD.meet p1 p2) then ID.of_int ik BI.zero else match eq p1 p2 with Some x when x -> ID.of_int ik BI.one | _ -> bool_top ik) | Ne -> Int (if AD.is_bot (AD.meet p1 p2) then ID.of_int ik BI.one else match eq p1 p2 with Some x when x -> ID.of_int ik BI.zero | _ -> bool_top ik) + | IndexPI when AD.to_string p2 = ["all_index"] -> + addToAddrOp p1 (ID.top_of (Cilfacade.ptrdiff_ikind ())) | _ -> VD.top () end (* For other values, we just give up! *) From 6d0498326c395d35d51b23df9d754b866ff0fe7f Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 2 Aug 2023 11:43:35 +0300 Subject: [PATCH 0371/1312] Improve widen tracing --- src/cdomains/intDomain.ml | 2 +- src/solvers/td3.ml | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cdomains/intDomain.ml b/src/cdomains/intDomain.ml index b1db3796a8..5417598360 100644 --- a/src/cdomains/intDomain.ml +++ b/src/cdomains/intDomain.ml @@ -760,7 +760,7 @@ struct norm ik @@ Some (l2,u2) |> fst let widen ik x y = let r = widen ik x y in - if M.tracing then M.tracel "int" "interval widen %a %a -> %a\n" pretty x pretty y pretty r; + if M.tracing && not (equal x y) then M.tracel "int" "interval widen %a %a -> %a\n" pretty x pretty y pretty r; assert (leq x y); (* TODO: remove for performance reasons? *) r diff --git a/src/solvers/td3.ml b/src/solvers/td3.ml index 9621f69db8..26df6aac95 100644 --- a/src/solvers/td3.ml +++ b/src/solvers/td3.ml @@ -334,6 +334,8 @@ module Base = ); if not (Timing.wrap "S.Dom.equal" (fun () -> S.Dom.equal old wpd) ()) then ( (* value changed *) if tracing then trace "sol" "Changed\n"; + (* if tracing && not (S.Dom.is_bot old) && HM.mem wpoint x then trace "solchange" "%a (wpx: %b): %a -> %a\n" S.Var.pretty_trace x (HM.mem wpoint x) S.Dom.pretty old S.Dom.pretty wpd; *) + if tracing && not (S.Dom.is_bot old) && HM.mem wpoint x then trace "solchange" "%a (wpx: %b): %a\n" S.Var.pretty_trace x (HM.mem wpoint x) S.Dom.pretty_diff (wpd, old); update_var_event x old wpd; HM.replace rho x wpd; destabilize x; From f0a05668ca991baa67ad5eadf4b722357bca263b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 18 May 2023 17:18:38 +0300 Subject: [PATCH 0372/1312] Fix var_eq may_change for other argument being constant --- src/analyses/varEq.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/varEq.ml b/src/analyses/varEq.ml index 99307d5d37..3054f2e0da 100644 --- a/src/analyses/varEq.ml +++ b/src/analyses/varEq.ml @@ -291,7 +291,7 @@ struct | Question (b, t, f, _) -> lval_may_change_pt b bl || lval_may_change_pt t bl || lval_may_change_pt f bl in let r = - if Cil.isConstant b then false + if Cil.isConstant b || Cil.isConstant a then false else if Queries.LS.is_top bls || Queries.LS.mem (dummyFunDec.svar, `NoOffset) bls then ((*Messages.warn ~category:Analyzer "No PT-set: switching to types ";*) type_may_change_apt a ) else Queries.LS.exists (lval_may_change_pt a) bls From 5372a76b0b2dad8a89193cbcc14e0c2ff33e0945 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 19 May 2023 13:46:38 +0300 Subject: [PATCH 0373/1312] Disable CIL check in unassume because variables of typedef types fail --- src/analyses/unassumeAnalysis.ml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/analyses/unassumeAnalysis.ml b/src/analyses/unassumeAnalysis.ml index 6b719c57b9..43707acd1e 100644 --- a/src/analyses/unassumeAnalysis.ml +++ b/src/analyses/unassumeAnalysis.ml @@ -111,7 +111,7 @@ struct Locator.ES.iter (fun n -> let fundec = Node.find_fundec n in - match InvariantParser.parse_cil inv_parser ~fundec ~loc inv_cabs with + match InvariantParser.parse_cil inv_parser ~check:false ~fundec ~loc inv_cabs with | Ok inv_exp -> M.debug ~category:Witness ~loc:msgLoc "located invariant to %a: %a" Node.pretty n Cil.d_exp inv_exp; NH.add invs n {exp = inv_exp; uuid} @@ -157,12 +157,12 @@ struct Locator.ES.iter (fun n -> let fundec = Node.find_fundec n in - match InvariantParser.parse_cil inv_parser ~fundec ~loc pre_cabs with + match InvariantParser.parse_cil inv_parser ~check:false ~fundec ~loc pre_cabs with | Ok pre_exp -> M.debug ~category:Witness ~loc:msgLoc "located precondition to %a: %a" CilType.Fundec.pretty fundec Cil.d_exp pre_exp; FH.add fun_pres fundec pre_exp; - begin match InvariantParser.parse_cil inv_parser ~fundec ~loc inv_cabs with + begin match InvariantParser.parse_cil inv_parser ~check:false ~fundec ~loc inv_cabs with | Ok inv_exp -> M.debug ~category:Witness ~loc:msgLoc "located invariant to %a: %a" Node.pretty n Cil.d_exp inv_exp; if not (NH.mem pre_invs n) then From 10881f05c6318ed501249986858b80a5c49f0c70 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 19 May 2023 13:46:50 +0300 Subject: [PATCH 0374/1312] Add solchange tracing to TD3 side effects --- src/solvers/td3.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/solvers/td3.ml b/src/solvers/td3.ml index 26df6aac95..29ad301292 100644 --- a/src/solvers/td3.ml +++ b/src/solvers/td3.ml @@ -432,6 +432,7 @@ module Base = HM.replace stable y (); if not (S.Dom.leq tmp old) then ( if tracing && not (S.Dom.is_bot old) then trace "solside" "side to %a (wpx: %b) from %a: %a -> %a\n" S.Var.pretty_trace y (HM.mem wpoint y) (Pretty.docOpt (S.Var.pretty_trace ())) x S.Dom.pretty old S.Dom.pretty tmp; + if tracing && not (S.Dom.is_bot old) then trace "solchange" "side to %a (wpx: %b) from %a: %a\n" S.Var.pretty_trace y (HM.mem wpoint y) (Pretty.docOpt (S.Var.pretty_trace ())) x S.Dom.pretty_diff (tmp, old); let sided = match x with | Some x -> let sided = VS.mem x old_sides in From 90f155cb23bffb2ca55d1d26dc2df5dd1c8a4035 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Sat, 20 May 2023 11:04:56 +0300 Subject: [PATCH 0375/1312] Exclude more pthread types from base --- src/cdomains/valueDomain.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cdomains/valueDomain.ml b/src/cdomains/valueDomain.ml index 20c4f3bf21..9d8fcc5012 100644 --- a/src/cdomains/valueDomain.ml +++ b/src/cdomains/valueDomain.ml @@ -115,7 +115,7 @@ struct | _ -> false let is_mutex_type (t: typ): bool = match t with - | TNamed (info, attr) -> info.tname = "pthread_mutex_t" || info.tname = "spinlock_t" || info.tname = "pthread_spinlock_t" + | TNamed (info, attr) -> info.tname = "pthread_mutex_t" || info.tname = "spinlock_t" || info.tname = "pthread_spinlock_t" || info.tname = "pthread_cond_t" || info.tname = "pthread_mutexattr_t" | TInt (IInt, attr) -> hasAttribute "mutex" attr | _ -> false From 5e19b23189b80012c6571b669b4233945036ae0b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 1 Aug 2023 12:00:01 +0300 Subject: [PATCH 0376/1312] Fix relational witness literature examples to also validate --- tests/regression/56-witness/37-hh-ex3.c | 2 +- tests/regression/56-witness/37-hh-ex3.yml | 4 ++-- tests/regression/56-witness/40-bh-ex1-poly.yml | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/regression/56-witness/37-hh-ex3.c b/tests/regression/56-witness/37-hh-ex3.c index c3f26b5cf1..e59fd53108 100644 --- a/tests/regression/56-witness/37-hh-ex3.c +++ b/tests/regression/56-witness/37-hh-ex3.c @@ -1,4 +1,4 @@ -// SKIP PARAM: --set ana.activated[+] apron --disable solvers.td3.remove-wpoint --set ana.activated[+] unassume --set witness.yaml.unassume 37-hh-ex3.yml +// SKIP PARAM: --set ana.activated[+] apron --enable ana.apron.strengthening --disable solvers.td3.remove-wpoint --set ana.activated[+] unassume --set witness.yaml.unassume 37-hh-ex3.yml #include int main() { int i = 0; diff --git a/tests/regression/56-witness/37-hh-ex3.yml b/tests/regression/56-witness/37-hh-ex3.yml index 9a4562d6d2..d6cd5150a4 100644 --- a/tests/regression/56-witness/37-hh-ex3.yml +++ b/tests/regression/56-witness/37-hh-ex3.yml @@ -20,10 +20,10 @@ location: file_name: 37-hh-ex3.c file_hash: 9c984e89a790b595d2b37ca8a05e5967a15130592cb2567fac2fae4aff668a4f - line: 7 + line: 6 column: 4 function: main location_invariant: - string: 0 <= i && i <= 3 && j == 0 + string: 0 <= i && i <= 3 type: assertion format: C diff --git a/tests/regression/56-witness/40-bh-ex1-poly.yml b/tests/regression/56-witness/40-bh-ex1-poly.yml index e219e1f877..cdbd8d666b 100644 --- a/tests/regression/56-witness/40-bh-ex1-poly.yml +++ b/tests/regression/56-witness/40-bh-ex1-poly.yml @@ -20,10 +20,10 @@ location: file_name: 40-bh-ex1-poly.c file_hash: 34f781dcae089ecb6b7b2811027395fcb501b8477b7e5016f7b38081724bea28 - line: 8 + line: 7 column: 4 function: main location_invariant: - string: 0 <= i && i <= 3 && j == 0 + string: 0 <= i && i <= 3 type: assertion format: C From e018cb756ab36ac6f4a27d6056fd08305216435a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 1 Aug 2023 14:57:25 +0300 Subject: [PATCH 0377/1312] Add non-terminating hh-ex3 --- src/cdomains/apron/apronDomain.apron.ml | 6 ++-- src/goblint.ml | 2 +- tests/regression/56-witness/63-hh-ex3-term.c | 33 +++++++++++++++++++ .../regression/56-witness/63-hh-ex3-term.yml | 25 ++++++++++++++ 4 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 tests/regression/56-witness/63-hh-ex3-term.c create mode 100644 tests/regression/56-witness/63-hh-ex3-term.yml diff --git a/src/cdomains/apron/apronDomain.apron.ml b/src/cdomains/apron/apronDomain.apron.ml index d9928df597..7dffafe967 100644 --- a/src/cdomains/apron/apronDomain.apron.ml +++ b/src/cdomains/apron/apronDomain.apron.ml @@ -693,16 +693,16 @@ struct let join x y = (* just to optimize joining folds, which start with bot *) - if is_bot x then + if is_bot x then (* TODO: also for non-empty env *) y - else if is_bot y then + else if is_bot y then (* TODO: also for non-empty env *) x else ( if M.tracing then M.traceli "apron" "join %a %a\n" pretty x pretty y; let j = join x y in if M.tracing then M.trace "apron" "j = %a\n" pretty j; let j = - if strengthening_enabled then + if strengthening_enabled then (* TODO: skip if same envs? *) strengthening j x y else j diff --git a/src/goblint.ml b/src/goblint.ml index a73d0a9fad..4ea3a3d242 100644 --- a/src/goblint.ml +++ b/src/goblint.ml @@ -73,7 +73,7 @@ let main () = exit 1 | Sys.Break -> (* raised on Ctrl-C if `Sys.catch_break true` *) do_stats (); - (* Printexc.print_backtrace BatInnerIO.stderr *) + Printexc.print_backtrace stderr; eprintf "%s\n" (MessageUtil.colorize ~fd:Unix.stderr ("{RED}Analysis was aborted by SIGINT (Ctrl-C)!")); Goblint_timing.teardown_tef (); exit 131 (* same exit code as without `Sys.catch_break true`, otherwise 0 *) diff --git a/tests/regression/56-witness/63-hh-ex3-term.c b/tests/regression/56-witness/63-hh-ex3-term.c new file mode 100644 index 0000000000..0d90e8753f --- /dev/null +++ b/tests/regression/56-witness/63-hh-ex3-term.c @@ -0,0 +1,33 @@ +// SKIP PARAM: --enable ana.int.interval --set ana.activated[+] apron --set ana.apron.domain polyhedra --enable ana.apron.strengthening --set ana.activated[+] unassume --set witness.yaml.unassume 63-hh-ex3-term.yml --enable ana.widen.tokens --disable witness.invariant.other --enable exp.arg +extern void __assert_fail (const char *__assertion, const char *__file, + unsigned int __line, const char *__function) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__noreturn__)); +extern void __assert_perror_fail (int __errnum, const char *__file, + unsigned int __line, const char *__function) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__noreturn__)); +extern void __assert (const char *__assertion, const char *__file, int __line) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__noreturn__)); + +extern void abort(void); +void reach_error() { ((void) sizeof ((0) ? 1 : 0), __extension__ ({ if (0) ; else __assert_fail ("0", "hh-ex3.c", 3, __extension__ __PRETTY_FUNCTION__); })); } +void __VERIFIER_assert(int cond) { if(!(cond)) { ERROR: {reach_error();abort();} } } +int main() { + int i = 0; + while (i < 4) { + int j = 0; + while (j < 4) { + i++; + j++; + __VERIFIER_assert(0 <= j); + __VERIFIER_assert(j <= i); + __VERIFIER_assert(i <= j + 3); + __VERIFIER_assert(j <= 4); + } + __VERIFIER_assert(0 <= j); + __VERIFIER_assert(j <= i); + __VERIFIER_assert(i <= j + 3); + __VERIFIER_assert(j <= 4); + i = i - j + 1; + } + return 0; +} diff --git a/tests/regression/56-witness/63-hh-ex3-term.yml b/tests/regression/56-witness/63-hh-ex3-term.yml new file mode 100644 index 0000000000..e635e24014 --- /dev/null +++ b/tests/regression/56-witness/63-hh-ex3-term.yml @@ -0,0 +1,25 @@ +- entry_type: location_invariant + metadata: + format_version: "0.1" + uuid: d834761a-d0d7-4fea-bf42-2ff2b9a19143 + creation_time: 2022-10-12T10:59:25Z + producer: + name: Simmo Saan + version: n/a + task: + input_files: + - /home/vagrant/eval-prec/prec/hh-ex3.i + input_file_hashes: + /home/vagrant/eval-prec/prec/hh-ex3.i: 9c984e89a790b595d2b37ca8a05e5967a15130592cb2567fac2fae4aff668a4f + data_model: LP64 + language: C + location: + file_name: 63-hh-ex3-term.c + file_hash: 9c984e89a790b595d2b37ca8a05e5967a15130592cb2567fac2fae4aff668a4f + line: 17 + column: 4 + function: main + location_invariant: + string: 0 <= i && i <= 3 + type: assertion + format: C From 0715a0451a6069368370aac1a6deeb29f2b3da36 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 1 Aug 2023 15:45:12 +0300 Subject: [PATCH 0378/1312] Simplify hh-ex3-term --- tests/regression/56-witness/63-hh-ex3-term.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/regression/56-witness/63-hh-ex3-term.c b/tests/regression/56-witness/63-hh-ex3-term.c index 0d90e8753f..80913c3b9d 100644 --- a/tests/regression/56-witness/63-hh-ex3-term.c +++ b/tests/regression/56-witness/63-hh-ex3-term.c @@ -19,14 +19,8 @@ int main() { i++; j++; __VERIFIER_assert(0 <= j); - __VERIFIER_assert(j <= i); - __VERIFIER_assert(i <= j + 3); - __VERIFIER_assert(j <= 4); } __VERIFIER_assert(0 <= j); - __VERIFIER_assert(j <= i); - __VERIFIER_assert(i <= j + 3); - __VERIFIER_assert(j <= 4); i = i - j + 1; } return 0; From ac8baaa8b551c7e6324d7e37c3d94d6256c9f355 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 1 Aug 2023 16:26:01 +0300 Subject: [PATCH 0379/1312] Disable witness lifter if GraphML witness generation disabled --- src/framework/control.ml | 2 +- src/witness/witness.ml | 54 ++++++++++++++++++++++++++++++++-------- 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/src/framework/control.ml b/src/framework/control.ml index 5ceddf2870..cd3a8b5f74 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -14,7 +14,7 @@ module type S2S = functor (X : Spec) -> Spec (* spec is lazy, so HConsed table in Hashcons lifters is preserved between analyses in server mode *) let spec_module: (module Spec) Lazy.t = lazy ( GobConfig.building_spec := true; - let arg_enabled = get_bool "ana.sv-comp.enabled" || get_bool "exp.arg" in + let arg_enabled = get_bool "witness.enabled" || get_bool "exp.arg" in let open Batteries in (* apply functor F on module X if opt is true *) let lift opt (module F : S2S) (module X : Spec) = (module (val if opt then (module F (X)) else (module X) : Spec) : Spec) in diff --git a/src/witness/witness.ml b/src/witness/witness.ml index aff9a01383..f73a2755c8 100644 --- a/src/witness/witness.ml +++ b/src/witness/witness.ml @@ -297,12 +297,45 @@ struct module ArgTool = ArgTools.Make (R) module NHT = ArgTool.NHT + module type BiArgInvariant = + sig + include ArgTools.BiArg + val find_invariant: Node.t -> Invariant.t + end + let determine_result entrystates (module Task:Task): (module WitnessTaskResult) = - let module Arg = (val ArgTool.create entrystates) in + let module Arg: BiArgInvariant = + (val if GobConfig.get_bool "witness.enabled" then ( + let module Arg = (val ArgTool.create entrystates) in + let module Arg = + struct + include Arg - let find_invariant (n, c, i) = - let context = {Invariant.default_context with path = Some i} in - ask_local (n, c) (Invariant context) + let find_invariant (n, c, i) = + let context = {Invariant.default_context with path = Some i} in + ask_local (n, c) (Invariant context) + end + in + (module Arg: BiArgInvariant) + ) + else ( + let module Arg = + struct + module Node = ArgTool.Node + module Edge = MyARG.InlineEdge + let next _ = [] + let prev _ = [] + let find_invariant _ = Invariant.none + let main_entry = + let lvar = WitnessUtil.find_main_entry entrystates in + (fst lvar, snd lvar, -1) + let iter_nodes f = f main_entry + let query _ q = Queries.Result.top q + end + in + (module Arg: BiArgInvariant) + ) + ) in match Task.specification with @@ -324,7 +357,7 @@ struct struct module Arg = Arg let result = Result.True - let invariant = find_invariant + let invariant = Arg.find_invariant let is_violation _ = false let is_sink _ = false end @@ -332,13 +365,13 @@ struct (module TaskResult:WitnessTaskResult) ) else ( let is_violation = function - | FunctionEntry f, _, _ when Svcomp.is_error_function f.svar -> true - | _, _, _ -> false + | FunctionEntry f when Svcomp.is_error_function f.svar -> true + | _ -> false in (* redefine is_violation to shift violations back by one, so enterFunction __VERIFIER_error is never used *) let is_violation n = Arg.next n - |> List.exists (fun (_, to_n) -> is_violation to_n) + |> List.exists (fun (_, to_n) -> is_violation (Arg.Node.cfgnode to_n)) in let violations = (* TODO: fold_nodes?s *) @@ -363,7 +396,7 @@ struct struct module Arg = Arg let result = Result.Unknown - let invariant = find_invariant + let invariant = Arg.find_invariant let is_violation = is_violation let is_sink = is_sink end @@ -454,7 +487,7 @@ struct struct module Arg = Arg let result = Result.True - let invariant = find_invariant + let invariant = Arg.find_invariant let is_violation _ = false let is_sink _ = false end @@ -480,7 +513,6 @@ struct print_task_result (module TaskResult); - (* TODO: use witness.enabled elsewhere as well *) if get_bool "witness.enabled" && (TaskResult.result <> Result.Unknown || get_bool "witness.unknown") then ( let witness_path = get_string "witness.path" in Timing.wrap "write" (write_file witness_path (module Task)) (module TaskResult) From 33eb6748eaa0f0b36e8fbc24744caf9f4cb3be86 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 1 Aug 2023 17:01:46 +0300 Subject: [PATCH 0380/1312] Enable witness.invariant.other in svcomp-yaml-validate conf --- conf/svcomp-yaml-validate.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/svcomp-yaml-validate.json b/conf/svcomp-yaml-validate.json index 05bb1ebcc2..fc2ada7143 100644 --- a/conf/svcomp-yaml-validate.json +++ b/conf/svcomp-yaml-validate.json @@ -79,7 +79,7 @@ "invariant": { "loop-head": true, "after-lock": false, - "other": false + "other": true } }, "solver": "td3", From 9127dadece4657c60ed81e3e9b78cd6ba98c986f Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 2 Aug 2023 10:22:39 +0300 Subject: [PATCH 0381/1312] Fix ARG enabled without ana.sv-comp.enabled --- src/framework/control.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/control.ml b/src/framework/control.ml index cd3a8b5f74..5cefc1a7de 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -14,7 +14,7 @@ module type S2S = functor (X : Spec) -> Spec (* spec is lazy, so HConsed table in Hashcons lifters is preserved between analyses in server mode *) let spec_module: (module Spec) Lazy.t = lazy ( GobConfig.building_spec := true; - let arg_enabled = get_bool "witness.enabled" || get_bool "exp.arg" in + let arg_enabled = (get_bool "ana.sv-comp.enabled" && get_bool "witness.enabled") || get_bool "exp.arg" in let open Batteries in (* apply functor F on module X if opt is true *) let lift opt (module F : S2S) (module X : Spec) = (module (val if opt then (module F (X)) else (module X) : Spec) : Spec) in From d512cb5e46e7c6e19e6ff80c1097c5ec183af2fa Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 2 Aug 2023 10:25:48 +0300 Subject: [PATCH 0382/1312] Remove pthread_mutexattr_t from is_mutex_type --- src/cdomains/valueDomain.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cdomains/valueDomain.ml b/src/cdomains/valueDomain.ml index 9d8fcc5012..a239be7c83 100644 --- a/src/cdomains/valueDomain.ml +++ b/src/cdomains/valueDomain.ml @@ -115,7 +115,7 @@ struct | _ -> false let is_mutex_type (t: typ): bool = match t with - | TNamed (info, attr) -> info.tname = "pthread_mutex_t" || info.tname = "spinlock_t" || info.tname = "pthread_spinlock_t" || info.tname = "pthread_cond_t" || info.tname = "pthread_mutexattr_t" + | TNamed (info, attr) -> info.tname = "pthread_mutex_t" || info.tname = "spinlock_t" || info.tname = "pthread_spinlock_t" || info.tname = "pthread_cond_t" | TInt (IInt, attr) -> hasAttribute "mutex" attr | _ -> false From 66204e417e23b40281c0b9973a94b62e653f2baf Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 2 Feb 2023 14:05:38 +0200 Subject: [PATCH 0383/1312] Special case calloc with count 1 in base --- src/analyses/base.ml | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 5b7a16e002..86c7cc5c2c 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2277,10 +2277,18 @@ struct then AD.join addr AD.null_ptr (* calloc can fail and return NULL *) else addr in let ik = Cilfacade.ptrdiff_ikind () in - let blobsize = ID.mul (ID.cast_to ik @@ eval_int (Analyses.ask_of_ctx ctx) gs st size) (ID.cast_to ik @@ eval_int (Analyses.ask_of_ctx ctx) gs st n) in - (* the memory that was allocated by calloc is set to bottom, but we keep track that it originated from calloc, so when bottom is read from memory allocated by calloc it is turned to zero *) - set_many ~ctx (Analyses.ask_of_ctx ctx) gs st [(add_null (AD.of_var heap_var), TVoid [], Array (CArrays.make (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) BI.one) (Blob (VD.bot (), blobsize, false)))); - (eval_lv (Analyses.ask_of_ctx ctx) gs st lv, (Cilfacade.typeOfLval lv), Address (add_null (AD.of_mval (heap_var, `Index (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) BI.zero, `NoOffset)))))] + let sizeval = eval_int (Analyses.ask_of_ctx ctx) gs st size in + let countval = eval_int (Analyses.ask_of_ctx ctx) gs st n in + if ID.to_int countval = Some Z.one then ( + set_many ~ctx (Analyses.ask_of_ctx ctx) gs st [(add_null (AD.of_var heap_var), TVoid [], Blob (VD.bot (), sizeval, false)); + (eval_lv (Analyses.ask_of_ctx ctx) gs st lv, (Cilfacade.typeOfLval lv), Address (add_null (AD.of_var heap_var)))] + ) + else ( + let blobsize = ID.mul (ID.cast_to ik @@ sizeval) (ID.cast_to ik @@ countval) in + (* the memory that was allocated by calloc is set to bottom, but we keep track that it originated from calloc, so when bottom is read from memory allocated by calloc it is turned to zero *) + set_many ~ctx (Analyses.ask_of_ctx ctx) gs st [(add_null (AD.of_var heap_var), TVoid [], Array (CArrays.make (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) BI.one) (Blob (VD.bot (), blobsize, false)))); + (eval_lv (Analyses.ask_of_ctx ctx) gs st lv, (Cilfacade.typeOfLval lv), Address (add_null (AD.of_mval (heap_var, `Index (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) BI.zero, `NoOffset)))))] + ) | _ -> st end | Realloc { ptr = p; size }, _ -> From 22f6061df7d2c7bd3584c000389b9f0a7abf010e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 2 Aug 2023 12:10:29 +0300 Subject: [PATCH 0384/1312] Fix trace-not-in-tracing semgrep rule for conjunctions --- .semgrep/tracing.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.semgrep/tracing.yml b/.semgrep/tracing.yml index 4892066c76..061b3efa0d 100644 --- a/.semgrep/tracing.yml +++ b/.semgrep/tracing.yml @@ -9,6 +9,7 @@ rules: - pattern: Messages.traceu - pattern: Messages.traceli - pattern-not-inside: if Messages.tracing then ... + - pattern-not-inside: if Messages.tracing && ... then ... message: trace functions should only be called if tracing is enabled at compile time languages: [ocaml] severity: WARNING From 1f53318999d3d1d425ed68fdf680dd68151e2d9c Mon Sep 17 00:00:00 2001 From: Karoliine Holter <44437975+karoliineh@users.noreply.github.com> Date: Wed, 2 Aug 2023 15:03:26 +0300 Subject: [PATCH 0385/1312] Apply suggestions from code review Co-authored-by: Simmo Saan --- src/analyses/libraryFunctions.ml | 54 ++++++++++++++++---------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 8d809ba46a..242c3f37fd 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -40,22 +40,22 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("fflush", unknown [drop "stream" [r_deep; w_deep]]); ("fgetc", unknown [drop "stream" [r_deep; w_deep]]); ("getc", unknown [drop "stream" [r_deep; w_deep]]); - ("fgets", unknown [drop "str" [r; w]; drop "count" []; drop "stream" [r_deep; w_deep]]); + ("fgets", unknown [drop "str" [w]; drop "count" []; drop "stream" [r_deep; w_deep]]); ("fopen", unknown [drop "pathname" [r]; drop "mode" [r]]); ("fdopen", unknown [drop "fd" []; drop "mode" [r]]); ("printf", unknown (drop "format" [r] :: VarArgs (drop' []))); (* TODO: why not r for VarArgs?*) ("fprintf", unknown (drop "stream" [r_deep; w_deep] :: drop "format" [r] :: VarArgs (drop' []))); (* TODO: why not r for VarArgs?*) - ("sprintf", unknown (drop "buffer" [r_deep; w_deep] :: drop "format" [r] :: VarArgs (drop' []))); (* TODO: why not r for VarArgs?*) - ("snprintf", unknown (drop "buffer" [r_deep; w_deep] :: drop "bufsz" [] :: drop "format" [r] :: VarArgs (drop' []))); (* TODO: why not r for VarArgs?*) + ("sprintf", unknown (drop "buffer" [w] :: drop "format" [r] :: VarArgs (drop' []))); (* TODO: why not r for VarArgs?*) + ("snprintf", unknown (drop "buffer" [w] :: drop "bufsz" [] :: drop "format" [r] :: VarArgs (drop' []))); (* TODO: why not r for VarArgs?*) ("fputc", unknown [drop "ch" []; drop "stream" [r_deep; w_deep]]); ("putc", unknown [drop "ch" []; drop "stream" [r_deep; w_deep]]); ("fputs", unknown [drop "str" [r]; drop "stream" [r_deep; w_deep]]); - ("fread", unknown [drop "buffer" [w_deep]; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); + ("fread", unknown [drop "buffer" [w]; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); ("fseek", unknown [drop "stream" [r_deep; w_deep]; drop "offset" []; drop "origin" []]); ("ftell", unknown [drop "stream" [r_deep]]); - ("fwrite", unknown [drop "buffer" [r_deep]; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); + ("fwrite", unknown [drop "buffer" [r]; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); ("rewind", unknown [drop "stream" [r_deep; w_deep]]); - ("setvbuf", unknown [drop "stream" [r_deep; w_deep]; drop "buffer" [r_deep; w_deep]; drop "mode" []; drop "size" []]); + ("setvbuf", unknown [drop "stream" [r_deep; w_deep]; drop "buffer" [r; w]; drop "mode" []; drop "size" []]); (* TODO: if this is used to set an input buffer, the buffer (second argument) would need to remain TOP, *) (* as any future write (or flush) of the stream could result in a write to the buffer *) ("gmtime", unknown ~attrs:[ThreadUnsafe] [drop "timer" [r_deep]]); @@ -64,7 +64,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("strlen", special [__ "s" [r]] @@ fun s -> Strlen s); ("strstr", special [__ "haystack" [r]; __ "needle" [r]] @@ fun haystack needle -> Strstr { haystack; needle; }); ("strcmp", special [__ "s1" [r]; __ "s2" [r]] @@ fun s1 s2 -> Strcmp { s1; s2; n = None; }); - ("strtok", unknown ~attrs:[ThreadUnsafe] [drop "str" [r]; drop "delim" [r]]); + ("strtok", unknown ~attrs:[ThreadUnsafe] [drop "str" [r; w]; drop "delim" [r]]); ("__builtin_strcmp", special [__ "s1" [r]; __ "s2" [r]] @@ fun s1 s2 -> Strcmp { s1; s2; n = None; }); ("strncmp", special [__ "s1" [r]; __ "s2" [r]; __ "n" []] @@ fun s1 s2 n -> Strcmp { s1; s2; n = Some n; }); ("malloc", special [__ "size" []] @@ fun size -> Malloc size); @@ -73,7 +73,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("exit", special [drop "exit_code" []] Abort); ("ungetc", unknown [drop "c" []; drop "stream" [r; w]]); ("scanf", unknown ((drop "format" [r]) :: (VarArgs (drop' [w])))); - ("fscanf", unknown ((drop "stream" [r; w]) :: (drop "format" [r]) :: (VarArgs (drop' [w])))); (* TODO: why stream not r_deep; w_deep? *) + ("fscanf", unknown ((drop "stream" [r_deep; w_deep]) :: (drop "format" [r]) :: (VarArgs (drop' [w])))); ("sscanf", unknown ((drop "buffer" [r]) :: (drop "format" [r]) :: (VarArgs (drop' [w])))); ("__freading", unknown [drop "stream" [r]]); ("mbsinit", unknown [drop "ps" [r]]); @@ -99,7 +99,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("tolower", unknown [drop "ch" []]); ("toupper", unknown [drop "ch" []]); ("time", unknown [drop "arg" [w]]); - ("tmpnam", unknown ~attrs:[ThreadUnsafe] [drop "filename" [r]]); + ("tmpnam", unknown ~attrs:[ThreadUnsafe] [drop "filename" [w]]); ("vprintf", unknown [drop "format" [r]; drop "vlist" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) ("vfprintf", unknown [drop "stream" [r_deep; w_deep]; drop "format" [r]; drop "vlist" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) ("vsprintf", unknown [drop "buffer" [w]; drop "format" [r]; drop "vlist" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) @@ -112,7 +112,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("difftime", unknown [drop "time1" []; drop "time2" []]); ("system", unknown ~attrs:[ThreadUnsafe] [drop "command" [r]]); ("wcscat", unknown [drop "dest" [r; w]; drop "src" [r]]); - ("wcrtomb", unknown ~attrs:[ThreadUnsafe] [drop "s" [r]; drop "wc" []; drop "ps" [w_deep]]); + ("wcrtomb", unknown ~attrs:[ThreadUnsafe] [drop "s" [w]; drop "wc" []; drop "ps" [r_deep; w_deep]]); ("abs", unknown [drop "j" []]); ("localtime_r", unknown [drop "timep" [r]; drop "result" [w]]); ("strpbrk", unknown [drop "s" [r]; drop "accept" [r]]); @@ -128,9 +128,9 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__builtin_bzero", special [__ "dest" [w]; __ "count" []] @@ fun dest count -> Bzero { dest; count; }); ("explicit_bzero", special [__ "dest" [w]; __ "count" []] @@ fun dest count -> Bzero { dest; count; }); ("__explicit_bzero_chk", special [__ "dest" [w]; __ "count" []; drop "os" []] @@ fun dest count -> Bzero { dest; count; }); - ("catgets", unknown ~attrs:[ThreadUnsafe] [drop "catalog" [r_deep]; drop "set_number" []; drop "message_number" []; drop "message" [r_deep]]); + ("catgets", unknown ~attrs:[ThreadUnsafe] [drop "catalog" [r_deep]; drop "set_number" []; drop "message_number" []; drop "message" [r]]); ("crypt", unknown ~attrs:[ThreadUnsafe] [drop "key" [r]; drop "salt" [r]]); - ("ctermid", unknown ~attrs:[ThreadUnsafe] [drop "s" [r]]); + ("ctermid", unknown ~attrs:[ThreadUnsafe] [drop "s" [w]]); ("dbm_clearerr", unknown ~attrs:[ThreadUnsafe] [drop "db" [r_deep; w_deep]]); ("dbm_close", unknown ~attrs:[ThreadUnsafe] [drop "db" [r_deep; w_deep; f_deep]]); ("dbm_delete", unknown ~attrs:[ThreadUnsafe] [drop "db" [r_deep; w_deep]; drop "key" []]); @@ -138,7 +138,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("dbm_fetch", unknown ~attrs:[ThreadUnsafe] [drop "db" [r_deep]; drop "key" []]); ("dbm_firstkey", unknown ~attrs:[ThreadUnsafe] [drop "db" [r_deep]]); ("dbm_nextkey", unknown ~attrs:[ThreadUnsafe] [drop "db" [r_deep]]); - ("dbm_open", unknown ~attrs:[ThreadUnsafe] [drop "file" [r_deep; w_deep]; drop "open_flags" []; drop "file_mode" []]); + ("dbm_open", unknown ~attrs:[ThreadUnsafe] [drop "file" [r; w]; drop "open_flags" []; drop "file_mode" []]); ("dbm_store", unknown ~attrs:[ThreadUnsafe] [drop "db" [r_deep; w_deep]; drop "key" []; drop "content" []; drop "store_mode" []]); ("dlerror", unknown ~attrs:[ThreadUnsafe] []); ("drand48", unknown ~attrs:[ThreadUnsafe] []); @@ -180,7 +180,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("getc_unlocked", unknown ~attrs:[ThreadUnsafe] [drop "stream" [r_deep; w_deep]]); ("getchar_unlocked", unknown ~attrs:[ThreadUnsafe] []); ("ptsname", unknown ~attrs:[ThreadUnsafe] [drop "fd" []]); - ("putc_unlocked", unknown ~attrs:[ThreadUnsafe] [drop "c" []; drop "stream" [w]]); + ("putc_unlocked", unknown ~attrs:[ThreadUnsafe] [drop "c" []; drop "stream" [r_deep; w_deep]]); ("putchar_unlocked", unknown ~attrs:[ThreadUnsafe] [drop "c" []]); ("putenv", unknown ~attrs:[ThreadUnsafe] [drop "string" [r; w]]); ("readdir", unknown ~attrs:[ThreadUnsafe] [drop "dirp" [r_deep]]); @@ -195,7 +195,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("fcntl", unknown (drop "fd" [] :: drop "cmd" [] :: VarArgs (drop' [r; w]))); ("fseeko", unknown [drop "stream" [r_deep; w_deep]; drop "offset" []; drop "whence" []]); ("fileno", unknown [drop "stream" [r_deep; w_deep]]); - ("getopt", unknown ~attrs:[ThreadUnsafe] [drop "argc" []; drop "argv" [r]; drop "optstring" [r]]); + ("getopt", unknown ~attrs:[ThreadUnsafe] [drop "argc" []; drop "argv" [r_deep]; drop "optstring" [r]]); ("iconv_open", unknown [drop "tocode" [r]; drop "fromcode" [r]]); ("iconv", unknown [drop "cd" [r]; drop "inbuf" [r]; drop "inbytesleft" [r;w]; drop "outbuf" [w]; drop "outbytesleft" [r;w]]); ("iconv_close", unknown [drop "cd" [f]]); @@ -241,7 +241,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("lstat", unknown [drop "pathname" [r]; drop "statbuf" [w]]); ("getpwnam", unknown [drop "name" [r]]); ("chdir", unknown [drop "path" [r]]); - ("closedir", unknown [drop "dirp" [w]]); + ("closedir", unknown [drop "dirp" [r]]); ("mkdir", unknown [drop "pathname" [r]; drop "mode" []]); ("opendir", unknown [drop "name" [r]]); ("rmdir", unknown [drop "path" [r]]); @@ -286,8 +286,8 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ let pthread_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("pthread_create", special [__ "thread" [w]; drop "attr" [r]; __ "start_routine" [s]; __ "arg" []] @@ fun thread start_routine arg -> ThreadCreate { thread; start_routine; arg }); (* For precision purposes arg is not considered accessed here. Instead all accesses (if any) come from actually analyzing start_routine. *) ("pthread_exit", special [__ "retval" []] @@ fun retval -> ThreadExit { ret_val = retval }); (* Doesn't dereference the void* itself, but just passes to pthread_join. *) - ("pthread_cond_init", unknown [drop "cond" [w]; drop "attr" [w]]); - ("__pthread_cond_init", unknown [drop "cond" [w]; drop "attr" [w]]); + ("pthread_cond_init", unknown [drop "cond" [w]; drop "attr" [r]]); + ("__pthread_cond_init", unknown [drop "cond" [w]; drop "attr" [r]]); ("pthread_cond_signal", special [__ "cond" []] @@ fun cond -> Signal cond); ("__pthread_cond_signal", special [__ "cond" []] @@ fun cond -> Signal cond); ("pthread_cond_broadcast", special [__ "cond" []] @@ fun cond -> Broadcast cond); @@ -308,7 +308,7 @@ let pthread_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__pthread_mutex_unlock", special [__ "mutex" []] @@ fun mutex -> Unlock mutex); ("pthread_mutexattr_init", unknown [drop "attr" [w]]); ("pthread_mutexattr_destroy", unknown [drop "attr" [f]]); - ("pthread_rwlock_init", unknown [drop "rwlock" [w]; drop "attr" [w]]); + ("pthread_rwlock_init", unknown [drop "rwlock" [w]; drop "attr" [r]]); ("pthread_rwlock_destroy", unknown [drop "rwlock" [f]]); ("pthread_rwlock_rdlock", special [__ "rwlock" []] @@ fun rwlock -> Lock {lock = rwlock; try_ = get_bool "sem.lock.fail"; write = false; return_on_success = true}); ("pthread_rwlock_tryrdlock", special [__ "rwlock" []] @@ fun rwlock -> Lock {lock = rwlock; try_ = get_bool "sem.lock.fail"; write = false; return_on_success = true}); @@ -317,18 +317,18 @@ let pthread_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("pthread_rwlock_unlock", special [__ "rwlock" []] @@ fun rwlock -> Unlock rwlock); ("pthread_rwlockattr_init", unknown [drop "attr" [w]]); ("pthread_rwlockattr_destroy", unknown [drop "attr" [f]]); - ("pthread_spin_init", unknown [drop "lock" []; drop "pshared" []]); + ("pthread_spin_init", unknown [drop "lock" [w]; drop "pshared" []]); ("pthread_spin_destroy", unknown [drop "lock" [f]]); ("pthread_spin_lock", special [__ "lock" []] @@ fun lock -> Lock {lock = lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true}); ("pthread_spin_trylock", special [__ "lock" []] @@ fun lock -> Lock {lock = lock; try_ = true; write = true; return_on_success = false}); ("pthread_spin_unlock", special [__ "lock" []] @@ fun lock -> Unlock lock); ("pthread_attr_init", unknown [drop "attr" [w]]); ("pthread_attr_destroy", unknown [drop "attr" [f]]); - ("pthread_attr_getdetachstate", unknown [drop "attr" [r]; drop "detachstate" [r]]); + ("pthread_attr_getdetachstate", unknown [drop "attr" [r]; drop "detachstate" [w]]); ("pthread_attr_setdetachstate", unknown [drop "attr" [w]; drop "detachstate" []]); - ("pthread_attr_getstacksize", unknown [drop "attr" [r]; drop "stacksize" [r]]); + ("pthread_attr_getstacksize", unknown [drop "attr" [r]; drop "stacksize" [w]]); ("pthread_attr_setstacksize", unknown [drop "attr" [w]; drop "stacksize" []]); - ("pthread_attr_getscope", unknown [drop "attr" [r]; drop "scope" [r]]); + ("pthread_attr_getscope", unknown [drop "attr" [r]; drop "scope" [w]]); ("pthread_attr_setscope", unknown [drop "attr" [w]; drop "scope" []]); ("pthread_self", unknown []); ("pthread_sigmask", unknown [drop "how" []; drop "set" [r]; drop "oldset" [w]]); @@ -408,10 +408,10 @@ let glibc_desc_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("euidaccess", unknown [drop "pathname" [r]; drop "mode" []]); ("rpmatch", unknown [drop "response" [r]]); ("getpagesize", unknown []); - ("__fgets_alias", unknown [drop "__s" [r; w]; drop "__n" []; drop "__stream" [r_deep; w_deep]]); - ("__fgets_chk", unknown [drop "__s" [r; w]; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); - ("__fread_alias", unknown [drop "__ptr" [w_deep]; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); - ("__fread_chk", unknown [drop "__ptr" [w_deep]; drop "__ptrlen" []; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); + ("__fgets_alias", unknown [drop "__s" [w]; drop "__n" []; drop "__stream" [r_deep; w_deep]]); + ("__fgets_chk", unknown [drop "__s" [w]; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); + ("__fread_alias", unknown [drop "__ptr" [w]; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); + ("__fread_chk", unknown [drop "__ptr" [w]; drop "__ptrlen" []; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); ("__read_chk", unknown [drop "__fd" []; drop "__buf" [w]; drop "__nbytes" []; drop "__buflen" []]); ("__read_alias", unknown [drop "__fd" []; drop "__buf" [w]; drop "__nbytes" []]); ("__readlink_chk", unknown [drop "path" [r]; drop "buf" [w]; drop "len" []; drop "buflen" []]); From 5c8bcfbf2b270acca278db4cd8b2094b6a3693d3 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Wed, 2 Aug 2023 15:20:46 +0300 Subject: [PATCH 0386/1312] Implement comments from code review --- src/analyses/libraryFunctions.ml | 39 ++++++++++++++++---------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 242c3f37fd..2de54f9660 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -15,11 +15,9 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("memcpy", special [__ "dest" [w]; __ "src" [r]; drop "n" []] @@ fun dest src -> Memcpy { dest; src }); ("__builtin_memcpy", special [__ "dest" [w]; __ "src" [r]; drop "n" []] @@ fun dest src -> Memcpy { dest; src }); ("__builtin___memcpy_chk", special [__ "dest" [w]; __ "src" [r]; drop "n" []; drop "os" []] @@ fun dest src -> Memcpy { dest; src }); - ("mempcpy", unknown [drop "dest" [w]; drop "src" [r]; drop "n" []]); - ("__builtin___mempcpy_chk", unknown [drop "dest" [w]; drop "src" [r]; drop "n" []; drop "os" []]); - ("memmove", unknown [drop "dest" [w]; drop "src" [r]; drop "count" []]); - ("__builtin_memmove", unknown [drop "dest" [w]; drop "src" [r]; drop "count" []]); - ("__builtin___memmove_chk", unknown [drop "dest" [w]; drop "src" [r]; drop "count" []; drop "os" []]); + ("memmove", special [__ "dest" [w]; __ "src" [r]; drop "count" []] @@ fun dest src -> Memcpy { dest; src }); + ("__builtin_memmove", special [__ "dest" [w]; __ "src" [r]; drop "count" []] @@ fun dest src -> Memcpy { dest; src }); + ("__builtin___memmove_chk", special [__ "dest" [w]; __ "src" [r]; drop "count" []; drop "os" []] @@ fun dest src -> Memcpy { dest; src }); ("strcpy", special [__ "dest" [w]; __ "src" [r]] @@ fun dest src -> Strcpy { dest; src; n = None; }); ("__builtin_strcpy", special [__ "dest" [w]; __ "src" [r]] @@ fun dest src -> Strcpy { dest; src; n = None; }); ("__builtin___strcpy_chk", special [__ "dest" [w]; __ "src" [r]; drop "os" []] @@ fun dest src -> Strcpy { dest; src; n = None; }); @@ -33,20 +31,15 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__builtin_strncat", special [__ "dest" [r; w]; __ "src" [r]; __ "n" []] @@ fun dest src n -> Strcat { dest; src; n = Some n; }); ("__builtin___strncat_chk", special [__ "dest" [r; w]; __ "src" [r]; __ "n" []; drop "os" []] @@ fun dest src n -> Strcat { dest; src; n = Some n; }); ("asctime", unknown ~attrs:[ThreadUnsafe] [drop "time_ptr" [r_deep]]); - ("free", unknown [drop "ptr" [f]]); ("fclose", unknown [drop "stream" [r_deep; w_deep; f_deep]]); ("feof", unknown [drop "stream" [r_deep; w_deep]]); ("ferror", unknown [drop "stream" [r_deep; w_deep]]); ("fflush", unknown [drop "stream" [r_deep; w_deep]]); ("fgetc", unknown [drop "stream" [r_deep; w_deep]]); - ("getc", unknown [drop "stream" [r_deep; w_deep]]); - ("fgets", unknown [drop "str" [w]; drop "count" []; drop "stream" [r_deep; w_deep]]); - ("fopen", unknown [drop "pathname" [r]; drop "mode" [r]]); - ("fdopen", unknown [drop "fd" []; drop "mode" [r]]); - ("printf", unknown (drop "format" [r] :: VarArgs (drop' []))); (* TODO: why not r for VarArgs?*) - ("fprintf", unknown (drop "stream" [r_deep; w_deep] :: drop "format" [r] :: VarArgs (drop' []))); (* TODO: why not r for VarArgs?*) - ("sprintf", unknown (drop "buffer" [w] :: drop "format" [r] :: VarArgs (drop' []))); (* TODO: why not r for VarArgs?*) - ("snprintf", unknown (drop "buffer" [w] :: drop "bufsz" [] :: drop "format" [r] :: VarArgs (drop' []))); (* TODO: why not r for VarArgs?*) + ("printf", unknown (drop "format" [r] :: VarArgs (drop' [r]))); + ("fprintf", unknown (drop "stream" [r_deep; w_deep] :: drop "format" [r] :: VarArgs (drop' [r]))); + ("sprintf", unknown (drop "buffer" [w] :: drop "format" [r] :: VarArgs (drop' [r]))); + ("snprintf", unknown (drop "buffer" [w] :: drop "bufsz" [] :: drop "format" [r] :: VarArgs (drop' [r]))); ("fputc", unknown [drop "ch" []; drop "stream" [r_deep; w_deep]]); ("putc", unknown [drop "ch" []; drop "stream" [r_deep; w_deep]]); ("fputs", unknown [drop "str" [r]; drop "stream" [r_deep; w_deep]]); @@ -87,6 +80,10 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("putchar", unknown [drop "ch" []]); ("puts", unknown [drop "s" [r]]); ("rand", unknown ~attrs:[ThreadUnsafe] []); + ("setgrent", unknown ~attrs:[ThreadUnsafe] []); + ("setpwent", unknown ~attrs:[ThreadUnsafe] []); + ("setutxent", unknown ~attrs:[ThreadUnsafe] []); + ("strerror", unknown ~attrs:[ThreadUnsafe] [drop "errnum" []]); ("strspn", unknown [drop "s" [r]; drop "accept" [r]]); ("strcspn", unknown [drop "s" [r]; drop "accept" [r]]); ("strftime", unknown [drop "str" [w]; drop "count" []; drop "format" [r]; drop "tp" [r]]); @@ -107,7 +104,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("ctime", unknown ~attrs:[ThreadUnsafe] [drop "rm" [r]]); ("clearerr", unknown [drop "stream" [w]]); ("setbuf", unknown [drop "stream" [w]; drop "buf" [w]]); - ("swprintf", unknown (drop "wcs" [w] :: drop "maxlen" [] :: drop "fmt" [r] :: VarArgs (drop' []))); + ("swprintf", unknown (drop "wcs" [w] :: drop "maxlen" [] :: drop "fmt" [r] :: VarArgs (drop' [r]))); ("assert", special [__ "exp" []] @@ fun exp -> Assert { exp; check = true; refine = get_bool "sem.assert.refine" }); (* only used if assert is used without include, e.g. in transformed files *) ("difftime", unknown [drop "time1" []; drop "time2" []]); ("system", unknown ~attrs:[ThreadUnsafe] [drop "command" [r]]); @@ -185,16 +182,16 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("putenv", unknown ~attrs:[ThreadUnsafe] [drop "string" [r; w]]); ("readdir", unknown ~attrs:[ThreadUnsafe] [drop "dirp" [r_deep]]); ("setenv", unknown ~attrs:[ThreadUnsafe] [drop "name" [r]; drop "name" [r]; drop "overwrite" []]); - ("setgrent", unknown ~attrs:[ThreadUnsafe] []); - ("setpwent", unknown ~attrs:[ThreadUnsafe] []); - ("setutxent", unknown ~attrs:[ThreadUnsafe] []); - ("strerror", unknown ~attrs:[ThreadUnsafe] [drop "errnum" []]); ("strsignal", unknown ~attrs:[ThreadUnsafe] [drop "sig" []]); ("unsetenv", unknown ~attrs:[ThreadUnsafe] [drop "name" [r]]); ("lseek", unknown [drop "fd" []; drop "offset" []; drop "whence" []]); ("fcntl", unknown (drop "fd" [] :: drop "cmd" [] :: VarArgs (drop' [r; w]))); ("fseeko", unknown [drop "stream" [r_deep; w_deep]; drop "offset" []; drop "whence" []]); ("fileno", unknown [drop "stream" [r_deep; w_deep]]); + ("fgets", unknown [drop "str" [w]; drop "count" []; drop "stream" [r_deep; w_deep]]); + ("fopen", unknown [drop "pathname" [r]; drop "mode" [r]]); + ("fdopen", unknown [drop "fd" []; drop "mode" [r]]); + ("getc", unknown [drop "stream" [r_deep; w_deep]]); ("getopt", unknown ~attrs:[ThreadUnsafe] [drop "argc" []; drop "argv" [r_deep]; drop "optstring" [r]]); ("iconv_open", unknown [drop "tocode" [r]; drop "fromcode" [r]]); ("iconv", unknown [drop "cd" [r]; drop "inbuf" [r]; drop "inbytesleft" [r;w]; drop "outbuf" [w]; drop "outbytesleft" [r;w]]); @@ -438,6 +435,8 @@ let glibc_desc_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("strcasestr", unknown [drop "haystack" [r]; drop "needle" [r]]); ("inet_aton", unknown [drop "cp" [r]; drop "inp" [w]]); ("fopencookie", unknown [drop "cookie" []; drop "mode" [r]; drop "io_funcs" [s_deep]]); (* doesn't access cookie but passes it to io_funcs *) + ("mempcpy", special [__ "dest" [w]; __ "src" [r]; drop "n" []] @@ fun dest src -> Memcpy { dest; src }); + ("__builtin___mempcpy_chk", special [__ "dest" [w]; __ "src" [r]; drop "n" []; drop "os" []] @@ fun dest src -> Memcpy { dest; src }); ] let linux_userspace_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ @@ -449,7 +448,7 @@ let linux_userspace_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("epoll_create", unknown [drop "size" []]); ("epoll_ctl", unknown [drop "epfd" []; drop "op" []; drop "fd" []; drop "event" [w]]); ("epoll_wait", unknown [drop "epfd" []; drop "events" [w]; drop "maxevents" []; drop "timeout" []]); - ("__fprintf_chk", unknown [drop "stream" [r_deep; w_deep]; drop "flag" []; drop "format" [r]]); + ("__fprintf_chk", unknown (drop "stream" [r_deep; w_deep] :: drop "flag" [] :: drop "format" [r] :: VarArgs (drop' [r]))); ("sysinfo", unknown [drop "info" [w_deep]]); ("__xpg_basename", unknown [drop "path" [r]]); ("ptrace", unknown (drop "request" [] :: VarArgs (drop' [r_deep; w_deep]))); (* man page has 4 arguments, but header has varargs and real-world programs may call with <4 *) From c90fb0f11a3070c9c92534232a06008b0c2beb32 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 2 Aug 2023 14:27:40 +0200 Subject: [PATCH 0387/1312] Add global vars for all three memory safety SVComp properties --- src/framework/analysisState.ml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/framework/analysisState.ml b/src/framework/analysisState.ml index 385fa26aef..e987c414f2 100644 --- a/src/framework/analysisState.ml +++ b/src/framework/analysisState.ml @@ -10,6 +10,12 @@ let svcomp_may_overflow = ref false (** Whether a Use-After-Free (UAF) happened *) let svcomp_may_use_after_free = ref false +(** Whether an invalid pointer dereference happened *) +let svcomp_may_invalid_deref = ref false + +(** Whether an invalid memtrack happened *) +let svcomp_may_invalid_memtrack = ref false + (** A hack to see if we are currently doing global inits *) let global_initialization = ref false From e28f72b22f55fa0412d14b5877c8c90577c1c3eb Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 2 Aug 2023 14:29:13 +0200 Subject: [PATCH 0388/1312] Improve SVComp result generation and support all 3 memory-safety props --- src/autoTune.ml | 11 ++++--- src/witness/svcomp.ml | 4 ++- src/witness/svcompSpec.ml | 41 +++++++++++++++++++------- src/witness/witness.ml | 62 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 101 insertions(+), 17 deletions(-) diff --git a/src/autoTune.ml b/src/autoTune.ml index bce12302cd..d532081799 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -185,7 +185,6 @@ let enableAnalyses anas = (*does not consider dynamic calls!*) let notNeccessaryThreadAnalyses = ["race"; "deadlock"; "maylocks"; "symb_locks"; "thread"; "threadid"; "threadJoins"; "threadreturn"] -let memorySafetyAnalyses = ["useAfterFree"] let reduceThreadAnalyses () = let isThreadCreate = function | LibraryDesc.ThreadCreate _ -> true @@ -220,9 +219,13 @@ let focusOnSpecification () = | NoOverflow -> (*We focus on integer analysis*) set_bool "ana.int.def_exc" true; set_bool "ana.int.interval" true - | MemorySafety -> (* Enable the memory safety analyses *) - print_endline @@ "Specification: MemorySafety -> enabling memory safety analyses \"" ^ (String.concat ", " memorySafetyAnalyses) ^ "\""; - enableAnalyses memorySafetyAnalyses + | ValidFree -> (* Enable the useAfterFree analysis *) + let uafAna = ["useAfterFree"] in + print_endline @@ "Specification: ValidFree -> enabling useAfterFree analysis \"" ^ (String.concat ", " uafAna) ^ "\""; + enableAnalyses uafAna + (* TODO: Finish these two below later *) + | ValidDeref + | ValidMemtrack -> () (*Detect enumerations and enable the "ana.int.enums" option*) exception EnumFound diff --git a/src/witness/svcomp.ml b/src/witness/svcomp.ml index b762d2eb5d..f1ee18ed72 100644 --- a/src/witness/svcomp.ml +++ b/src/witness/svcomp.ml @@ -52,7 +52,9 @@ struct | UnreachCall _ -> "unreach-call" | NoOverflow -> "no-overflow" | NoDataRace -> "no-data-race" (* not yet in SV-COMP/Benchexec *) - | MemorySafety -> "memory-safety" + | ValidFree -> "valid-free" + | ValidDeref -> "valid-deref" + | ValidMemtrack -> "valid-memtrack" in "false(" ^ result_spec ^ ")" | Unknown -> "unknown" diff --git a/src/witness/svcompSpec.ml b/src/witness/svcompSpec.ml index 784155ab9b..8dafb8873c 100644 --- a/src/witness/svcompSpec.ml +++ b/src/witness/svcompSpec.ml @@ -6,19 +6,20 @@ type t = | UnreachCall of string | NoDataRace | NoOverflow - | MemorySafety + | ValidFree + | ValidDeref + | ValidMemtrack let of_string s = let s = String.strip s in - let regexp = Str.regexp "CHECK( init(main()), LTL(G ! \\(.*\\)) )" in - if Str.string_match regexp s 0 then + let regexp = Str.regexp "CHECK( init(main()), LTL(G \\(.*\\)) )" in + let regexp_negated = Str.regexp "CHECK( init(main()), LTL(G ! \\(.*\\)) )" in + if Str.string_match regexp_negated s 0 then let global_not = Str.matched_group 1 s in if global_not = "data-race" then NoDataRace else if global_not = "overflow" then NoOverflow - else if global_not = "memory-safety" then - MemorySafety else let call_regex = Str.regexp "call(\\(.*\\)())" in if Str.string_match call_regex global_not 0 then @@ -26,6 +27,16 @@ let of_string s = UnreachCall f else failwith "Svcomp.Specification.of_string: unknown global not expression" + else if Str.string_match regexp s 0 then + let global = Str.matched_group 1 s in + if global = "valid-free" then + ValidFree + else if global = "valid-deref" then + ValidDeref + else if global = "valid-memtrack" then + ValidMemtrack + else + failwith "Svcomp.Specification.of_string: unknown global expression" else failwith "Svcomp.Specification.of_string: unknown expression" @@ -41,10 +52,18 @@ let of_option () = of_string s let to_string spec = - let global_not = match spec with - | UnreachCall f -> "call(" ^ f ^ "())" - | NoDataRace -> "data-race" - | NoOverflow -> "overflow" - | MemorySafety -> "memory-safety" + let print_output spec_str is_neg = + if is_neg then + Printf.sprintf "CHECK( init(main()), LTL(G ! %s) )" spec_str + else + Printf.sprintf "CHECK( init(main()), LTL(G %s) )" spec_str + in + let spec_str, is_neg = match spec with + | UnreachCall f -> "call(" ^ f ^ "())", true + | NoDataRace -> "data-race", true + | NoOverflow -> "overflow", true + | ValidFree -> "valid-free", false + | ValidDeref -> "valid-deref", false + | ValidMemtrack -> "valid-memtrack", false in - "CHECK( init(main()), LTL(G ! " ^ global_not ^ ") )" + print_output spec_str is_neg diff --git a/src/witness/witness.ml b/src/witness/witness.ml index 6dc0d04034..797541c606 100644 --- a/src/witness/witness.ml +++ b/src/witness/witness.ml @@ -472,7 +472,7 @@ struct in (module TaskResult:WitnessTaskResult) ) - | MemorySafety -> + | ValidFree -> let module TrivialArg = struct include Arg @@ -502,6 +502,66 @@ struct in (module TaskResult:WitnessTaskResult) ) + | ValidDeref -> + let module TrivialArg = + struct + include Arg + let next _ = [] + end + in + if not !AnalysisState.svcomp_may_invalid_deref then + let module TaskResult = + struct + module Arg = Arg + let result = Result.True + let invariant _ = Invariant.none + let is_violation _ = false + let is_sink _ = false + end + in + (module TaskResult:WitnessTaskResult) + else ( + let module TaskResult = + struct + module Arg = TrivialArg + let result = Result.Unknown + let invariant _ = Invariant.none + let is_violation _ = false + let is_sink _ = false + end + in + (module TaskResult:WitnessTaskResult) + ) + | ValidMemtrack -> + let module TrivialArg = + struct + include Arg + let next _ = [] + end + in + if not !AnalysisState.svcomp_may_invalid_memtrack then + let module TaskResult = + struct + module Arg = Arg + let result = Result.True + let invariant _ = Invariant.none + let is_violation _ = false + let is_sink _ = false + end + in + (module TaskResult:WitnessTaskResult) + else ( + let module TaskResult = + struct + module Arg = TrivialArg + let result = Result.Unknown + let invariant _ = Invariant.none + let is_violation _ = false + let is_sink _ = false + end + in + (module TaskResult:WitnessTaskResult) + ) let write entrystates = From f2fd9b5e5bdebc92631088c22e6b43401903b85f Mon Sep 17 00:00:00 2001 From: Karoliine Holter <44437975+karoliineh@users.noreply.github.com> Date: Wed, 2 Aug 2023 22:32:05 +0300 Subject: [PATCH 0389/1312] Update src/analyses/libraryFunctions.ml Co-authored-by: Simmo Saan --- src/analyses/libraryFunctions.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 3accc6d2e8..d49800f33a 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -141,7 +141,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("dbm_store", unknown ~attrs:[ThreadUnsafe] [drop "db" [r_deep; w_deep]; drop "key" []; drop "content" []; drop "store_mode" []]); ("dlerror", unknown ~attrs:[ThreadUnsafe] []); ("drand48", unknown ~attrs:[ThreadUnsafe] []); - ("encrypt", unknown ~attrs:[ThreadUnsafe] [drop "block" []; drop "edflag" []]); + ("encrypt", unknown ~attrs:[ThreadUnsafe] [drop "block" [r; w]; drop "edflag" []]); ("endgrent", unknown ~attrs:[ThreadUnsafe] []); ("endpwent", unknown ~attrs:[ThreadUnsafe] []); ("fcvt", unknown ~attrs:[ThreadUnsafe] [drop "number" []; drop "ndigits" []; drop "decpt" [w]; drop "sign" [w]]); From 870a9b847fc4d786752a49353eeb322d7babd319 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 3 Aug 2023 11:20:37 +0300 Subject: [PATCH 0390/1312] Fix PartitionDomain.SetSet.pretty_diff crash --- src/domains/partitionDomain.ml | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/domains/partitionDomain.ml b/src/domains/partitionDomain.ml index eab15e1b05..9675e9bfce 100644 --- a/src/domains/partitionDomain.ml +++ b/src/domains/partitionDomain.ml @@ -115,18 +115,23 @@ struct for_all (fun p -> exists (B.leq p) y) x let pretty_diff () (y, x) = - (* based on DisjointDomain.PairwiseSet *) - let x_not_leq = filter (fun p -> - not (exists (fun q -> B.leq p q) y) - ) x - in - let p_not_leq = choose x_not_leq in - GoblintCil.Pretty.( - dprintf "%a:\n" B.pretty p_not_leq - ++ - fold (fun q acc -> - dprintf "not leq %a because %a\n" B.pretty q B.pretty_diff (p_not_leq, q) ++ acc - ) y nil + if E.is_top x then ( + GoblintCil.Pretty.(dprintf "%a not leq bot" pretty y) + ) + else ( + (* based on DisjointDomain.PairwiseSet *) + let x_not_leq = filter (fun p -> + not (exists (fun q -> B.leq p q) y) + ) x + in + let p_not_leq = choose x_not_leq in + GoblintCil.Pretty.( + dprintf "%a:\n" B.pretty p_not_leq + ++ + fold (fun q acc -> + dprintf "not leq %a because %a\n" B.pretty q B.pretty_diff (p_not_leq, q) ++ acc + ) y nil + ) ) let meet xs ys = if is_bot xs || is_bot ys then bot () else From ab339c5e5fdaf68cd4e7a836adfc1ff720e79bd6 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 3 Aug 2023 11:26:40 +0300 Subject: [PATCH 0391/1312] Move back accidentally moved library functions --- src/analyses/libraryFunctions.ml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index d49800f33a..bb3517a755 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -36,6 +36,9 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("ferror", unknown [drop "stream" [r_deep; w_deep]]); ("fflush", unknown [drop "stream" [r_deep; w_deep]]); ("fgetc", unknown [drop "stream" [r_deep; w_deep]]); + ("getc", unknown [drop "stream" [r_deep; w_deep]]); + ("fgets", unknown [drop "str" [w]; drop "count" []; drop "stream" [r_deep; w_deep]]); + ("fopen", unknown [drop "pathname" [r]; drop "mode" [r]]); ("printf", unknown (drop "format" [r] :: VarArgs (drop' [r]))); ("fprintf", unknown (drop "stream" [r_deep; w_deep] :: drop "format" [r] :: VarArgs (drop' [r]))); ("sprintf", unknown (drop "buffer" [w] :: drop "format" [r] :: VarArgs (drop' [r]))); @@ -81,9 +84,6 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("putchar", unknown [drop "ch" []]); ("puts", unknown [drop "s" [r]]); ("rand", unknown ~attrs:[ThreadUnsafe] []); - ("setgrent", unknown ~attrs:[ThreadUnsafe] []); - ("setpwent", unknown ~attrs:[ThreadUnsafe] []); - ("setutxent", unknown ~attrs:[ThreadUnsafe] []); ("strerror", unknown ~attrs:[ThreadUnsafe] [drop "errnum" []]); ("strspn", unknown [drop "s" [r]; drop "accept" [r]]); ("strcspn", unknown [drop "s" [r]; drop "accept" [r]]); @@ -184,16 +184,16 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("putenv", unknown ~attrs:[ThreadUnsafe] [drop "string" [r; w]]); ("readdir", unknown ~attrs:[ThreadUnsafe] [drop "dirp" [r_deep]]); ("setenv", unknown ~attrs:[ThreadUnsafe] [drop "name" [r]; drop "name" [r]; drop "overwrite" []]); + ("setgrent", unknown ~attrs:[ThreadUnsafe] []); + ("setpwent", unknown ~attrs:[ThreadUnsafe] []); + ("setutxent", unknown ~attrs:[ThreadUnsafe] []); ("strsignal", unknown ~attrs:[ThreadUnsafe] [drop "sig" []]); ("unsetenv", unknown ~attrs:[ThreadUnsafe] [drop "name" [r]]); ("lseek", unknown [drop "fd" []; drop "offset" []; drop "whence" []]); ("fcntl", unknown (drop "fd" [] :: drop "cmd" [] :: VarArgs (drop' [r; w]))); ("fseeko", unknown [drop "stream" [r_deep; w_deep]; drop "offset" []; drop "whence" []]); ("fileno", unknown [drop "stream" [r_deep; w_deep]]); - ("fgets", unknown [drop "str" [w]; drop "count" []; drop "stream" [r_deep; w_deep]]); - ("fopen", unknown [drop "pathname" [r]; drop "mode" [r]]); ("fdopen", unknown [drop "fd" []; drop "mode" [r]]); - ("getc", unknown [drop "stream" [r_deep; w_deep]]); ("getopt", unknown ~attrs:[ThreadUnsafe] [drop "argc" []; drop "argv" [r_deep]; drop "optstring" [r]]); ("iconv_open", unknown [drop "tocode" [r]; drop "fromcode" [r]]); ("iconv", unknown [drop "cd" [r]; drop "inbuf" [r]; drop "inbytesleft" [r;w]; drop "outbuf" [w]; drop "outbytesleft" [r;w]]); From 2267095562e397f4ee9400e5f45dd639cfae8933 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 3 Aug 2023 11:27:12 +0300 Subject: [PATCH 0392/1312] Remove LibraryFunctions trailing whitespace, fix one VarArgs --- src/analyses/libraryFunctions.ml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index bb3517a755..447263b3ef 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -51,7 +51,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("ftell", unknown [drop "stream" [r_deep]]); ("fwrite", unknown [drop "buffer" [r]; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); ("rewind", unknown [drop "stream" [r_deep; w_deep]]); - ("setvbuf", unknown [drop "stream" [r_deep; w_deep]; drop "buffer" [r; w]; drop "mode" []; drop "size" []]); + ("setvbuf", unknown [drop "stream" [r_deep; w_deep]; drop "buffer" [r; w]; drop "mode" []; drop "size" []]); (* TODO: if this is used to set an input buffer, the buffer (second argument) would need to remain TOP, *) (* as any future write (or flush) of the stream could result in a write to the buffer *) ("gmtime", unknown ~attrs:[ThreadUnsafe] [drop "timer" [r_deep]]); @@ -364,7 +364,7 @@ let gcc_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__assert_rtn", special [drop "func" [r]; drop "file" [r]; drop "line" []; drop "exp" [r]] @@ Abort); (* MacOS's built-in assert *) ("__assert_fail", special [drop "assertion" [r]; drop "file" [r]; drop "line" []; drop "function" [r]] @@ Abort); (* gcc's built-in assert *) ("__builtin_return_address", unknown [drop "level" []]); - ("__builtin___sprintf_chk", unknown (drop "s" [w] :: drop "flag" [] :: drop "os" [] :: drop "fmt" [r] :: VarArgs (drop' []))); + ("__builtin___sprintf_chk", unknown (drop "s" [w] :: drop "flag" [] :: drop "os" [] :: drop "fmt" [r] :: VarArgs (drop' [r]))); ("__builtin_add_overflow", unknown [drop "a" []; drop "b" []; drop "c" [w]]); ("__builtin_sadd_overflow", unknown [drop "a" []; drop "b" []; drop "c" [w]]); ("__builtin_saddl_overflow", unknown [drop "a" []; drop "b" []; drop "c" [w]]); @@ -1119,7 +1119,7 @@ let invalidate_actions = [ ] let () = List.iter (fun (x, _) -> - if Hashtbl.exists (fun _ b -> List.mem_assoc x b) libraries then + if Hashtbl.exists (fun _ b -> List.mem_assoc x b) libraries then failwith ("You have added a function to invalidate_actions that already exists in libraries. Please undo this for function: " ^ x); ) invalidate_actions From ed231fa346bb8c4abbfd17c4c31cdbb77b4e316a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 3 Aug 2023 11:46:31 +0300 Subject: [PATCH 0393/1312] Fix access analysis to not dereference non-pointer types --- src/analyses/accessAnalysis.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/accessAnalysis.ml b/src/analyses/accessAnalysis.ml index 5245e4adfe..bc5330726c 100644 --- a/src/analyses/accessAnalysis.ml +++ b/src/analyses/accessAnalysis.ml @@ -44,7 +44,7 @@ struct let access_one_top ?(force=false) ?(deref=false) ctx (kind: AccessKind.t) reach exp = if M.tracing then M.traceli "access" "access_one_top %a (kind = %a, reach = %B, deref = %B)\n" CilType.Exp.pretty exp AccessKind.pretty kind reach deref; if force || !collect_local || !emit_single_threaded || ThreadFlag.has_ever_been_multi (Analyses.ask_of_ctx ctx) then ( - if deref then + if deref && Cil.isPointerType (Cilfacade.typeOf exp) then (* avoid dereferencing integers to unknown pointers, which cause many spurious type-based accesses *) do_access ctx kind reach exp; if M.tracing then M.tracei "access" "distribute_access_exp\n"; Access.distribute_access_exp (do_access ctx Read false) exp; From 346246954086c6be78c716873bb316c0b240ecbb Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 3 Aug 2023 10:53:00 +0200 Subject: [PATCH 0394/1312] Modify get in base in attempt to fix BlobSize --- src/analyses/base.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 7bc589df20..6ec15f0b31 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -462,7 +462,7 @@ struct let var = get_var a gs st x in let v = VD.eval_offset (Queries.to_value_domain_ask a) (fun x -> get a gs st x exp) var offs exp (Some (Var x, Offs.to_cil_offset offs)) x.vtype in if M.tracing then M.tracec "get" "var = %a, %a = %a\n" VD.pretty var AD.pretty (AD.of_mval (x, offs)) VD.pretty v; - if full then v else match v with + if full then var else match v with | Blob (c,s,_) -> c | x -> x in From 4fcfc715135d4721a156b0f9432ac0c1105142ae Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 3 Aug 2023 14:56:51 +0300 Subject: [PATCH 0395/1312] Add failing fixpoint test case for #1126 --- .../73-strings/04-smtprc_strlen_fp.c | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 tests/regression/73-strings/04-smtprc_strlen_fp.c diff --git a/tests/regression/73-strings/04-smtprc_strlen_fp.c b/tests/regression/73-strings/04-smtprc_strlen_fp.c new file mode 100644 index 0000000000..a046eac238 --- /dev/null +++ b/tests/regression/73-strings/04-smtprc_strlen_fp.c @@ -0,0 +1,21 @@ +// FIXPOINT extracted from smtprc_comb +#include // for optarg + +typedef unsigned int size_t; // size_t from 32bit cilly +extern size_t strlen(char const *__s ); + +void *s_malloc(unsigned long size) +{ + void *mymem; + mymem = malloc((unsigned int) size); + return mymem; +} + +int main() { + char const *p; + size_t s; + p = optarg; + s = strlen(optarg); + s_malloc((unsigned long) ((s + 1U) * sizeof(char))); + return 0; +} From 409cbd1f622a387f558b95f54db6cc57909f9f42 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 3 Aug 2023 14:57:43 +0300 Subject: [PATCH 0396/1312] Fix fixpoint issue from #1126 --- src/analyses/base.ml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 86c7cc5c2c..0c5805c72c 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1055,7 +1055,6 @@ struct else if AD.may_be_null adr then M.warn ~category:M.Category.Behavior.Undefined.nullpointer_dereference ~tags:[CWE 476] "May dereference NULL pointer"); AD.map (add_offset_varinfo (convert_offset a gs st ofs)) adr - | Bot -> AD.bot () | _ -> M.debug ~category:Analyzer "Failed evaluating %a to lvalue" d_lval lval; AD.unknown_ptr From ddecc8524347a0769ae425bb8738518f5b76025b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 3 Aug 2023 15:49:17 +0300 Subject: [PATCH 0397/1312] Move options from unassume bench to yaml confs --- conf/bench-yaml-validate.json | 8 -------- conf/bench-yaml.json | 14 -------------- conf/svcomp-yaml-validate.json | 13 +++++-------- conf/svcomp-yaml.json | 10 +++++++++- 4 files changed, 14 insertions(+), 31 deletions(-) diff --git a/conf/bench-yaml-validate.json b/conf/bench-yaml-validate.json index ca830be08a..7b18371bd1 100644 --- a/conf/bench-yaml-validate.json +++ b/conf/bench-yaml-validate.json @@ -52,14 +52,6 @@ "tokens": true } }, - "witness": { - "enabled": false, - "invariant": { - "loop-head": true, - "after-lock": true, - "other": false - } - }, "sem": { "unknown_function": { "invalidate": { diff --git a/conf/bench-yaml.json b/conf/bench-yaml.json index a24035fc9b..fd97b2c08c 100644 --- a/conf/bench-yaml.json +++ b/conf/bench-yaml.json @@ -48,20 +48,6 @@ ] } }, - "witness": { - "enabled": false, - "yaml": { - "enabled": true - }, - "invariant": { - "exact": false, - "exclude-vars": [ - "tmp\\(___[0-9]+\\)?", - "cond", - "RETURN" - ] - } - }, "sem": { "unknown_function": { "invalidate": { diff --git a/conf/svcomp-yaml-validate.json b/conf/svcomp-yaml-validate.json index fc2ada7143..1934a56932 100644 --- a/conf/svcomp-yaml-validate.json +++ b/conf/svcomp-yaml-validate.json @@ -12,6 +12,10 @@ "float": { "interval": true }, + "apron": { + "domain": "polyhedra", + "strengthening": true + }, "activated": [ "base", "threadid", @@ -31,6 +35,7 @@ "region", "thread", "threadJoins", + "apron", "unassume" ], "context": { @@ -74,14 +79,6 @@ "exp": { "region-offsets": true }, - "witness": { - "enabled": false, - "invariant": { - "loop-head": true, - "after-lock": false, - "other": true - } - }, "solver": "td3", "sem": { "unknown_function": { diff --git a/conf/svcomp-yaml.json b/conf/svcomp-yaml.json index 6e3d0e4767..e09d1c80d7 100644 --- a/conf/svcomp-yaml.json +++ b/conf/svcomp-yaml.json @@ -12,6 +12,10 @@ "float": { "interval": true }, + "apron": { + "domain": "polyhedra", + "strengthening": true + }, "activated": [ "base", "threadid", @@ -30,7 +34,8 @@ "symb_locks", "region", "thread", - "threadJoins" + "threadJoins", + "apron" ], "context": { "widen": false @@ -76,6 +81,9 @@ "enabled": true }, "invariant": { + "loop-head": true, + "other": false, + "accessed": false, "exact": false, "exclude-vars": [ "tmp\\(___[0-9]+\\)?", From c72fdedf8bc2281375c6c3c287e7423ef7699b1f Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 3 Aug 2023 16:12:39 +0200 Subject: [PATCH 0398/1312] Add MemoryLeak undefined behavior category --- src/util/messageCategory.ml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/util/messageCategory.ml b/src/util/messageCategory.ml index 5452225c26..d1c8f0db3c 100644 --- a/src/util/messageCategory.ml +++ b/src/util/messageCategory.ml @@ -13,6 +13,7 @@ type undefined_behavior = | UseAfterFree | DoubleFree | InvalidMemoryDeallocation + | MemoryLeak | Uninitialized | DoubleLocking | Other @@ -67,6 +68,7 @@ struct let use_after_free: category = create @@ UseAfterFree let double_free: category = create @@ DoubleFree let invalid_memory_deallocation: category = create @@ InvalidMemoryDeallocation + let memory_leak: category = create @@ MemoryLeak let uninitialized: category = create @@ Uninitialized let double_locking: category = create @@ DoubleLocking let other: category = create @@ Other @@ -117,6 +119,7 @@ struct | UseAfterFree -> ["UseAfterFree"] | DoubleFree -> ["DoubleFree"] | InvalidMemoryDeallocation -> ["InvalidMemoryDeallocation"] + | MemoryLeak -> ["MemoryLeak"] | Uninitialized -> ["Uninitialized"] | DoubleLocking -> ["DoubleLocking"] | Other -> ["Other"] @@ -228,6 +231,7 @@ let behaviorName = function |UseAfterFree -> "UseAfterFree" |DoubleFree -> "DoubleFree" |InvalidMemoryDeallocation -> "InvalidMemoryDeallocation" + |MemoryLeak -> "MemoryLeak" |Uninitialized -> "Uninitialized" |DoubleLocking -> "DoubleLocking" |Other -> "Other" From 9cea9fb688b9b7aad13c68f455d6e0167689e93c Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 3 Aug 2023 16:15:25 +0200 Subject: [PATCH 0399/1312] Add first version of a simple memory leak analysis --- src/analyses/memLeak.ml | 87 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 src/analyses/memLeak.ml diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml new file mode 100644 index 0000000000..d4a1bc967d --- /dev/null +++ b/src/analyses/memLeak.ml @@ -0,0 +1,87 @@ +(** An analysis for the detection of memory leaks ([memLeak]). *) + +open GoblintCil +open Analyses +open MessageCategory + +module ToppedVarInfoSet = SetDomain.ToppedSet(CilType.Varinfo)(struct let topname = "All Heap Variables" end) + +module Spec : Analyses.MCPSpec = +struct + include Analyses.DefaultSpec + + let name () = "memLeak" + + module D = ToppedVarInfoSet + module C = Lattice.Unit + + let context _ _ = () + + (* HELPER FUNCTIONS *) + let warn_for_multi_threaded ctx = + if not (ctx.ask (Queries.MustBeSingleThreaded { since_start = true })) then + M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Program isn't running in single-threaded mode. A memory leak might occur due to multi-threading" + + (* TRANSFER FUNCTIONS *) + let assign ctx (lval:lval) (rval:exp) : D.t = + ctx.local + + let branch ctx (exp:exp) (tv:bool) : D.t = + ctx.local + + let body ctx (f:fundec) : D.t = + ctx.local + + let return ctx (exp:exp option) (f:fundec) : D.t = + let state = ctx.local in + (* TODO: Is this too hacky of a solution? *) + if f.svar.vname = "main" && not @@ D.is_empty state then + M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory leak from function \"%s\": %a\n" f.svar.vname D.pretty state; + state + + let enter ctx (lval:lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = + [ctx.local, ctx.local] + + let combine_env ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (callee_local:D.t) (f_ask:Queries.ask) : D.t = + callee_local + + let combine_assign ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (callee_local:D.t) (f_ask: Queries.ask): D.t = + ctx.local + + let special ctx (lval:lval option) (f:varinfo) (arglist:exp list) : D.t = + let state = ctx.local in + let desc = LibraryFunctions.find f in + match desc.special arglist with + | Malloc _ + | Calloc _ + | Realloc _ -> + (* Warn about multi-threaded programs as soon as we encounter a dynamic memory allocation function *) + warn_for_multi_threaded ctx; + begin match ctx.ask Queries.HeapVar with + | `Lifted var -> D.add var state + | _ -> state + end + | Free ptr -> + begin match ctx.ask (Queries.MayPointTo ptr) with + | a when not (Queries.LS.is_top a) && not (Queries.LS.mem (dummyFunDec.svar, `NoOffset) a) -> + (* TODO: Need to always set "ana.malloc.unique_address_count" to smth > 0 *) + let unique_pointed_to_heap_vars = + Queries.LS.filter (fun (v, _) -> ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsMultiple v)) a + |> Queries.LS.elements + |> List.map fst + |> D.of_list + in + D.diff state unique_pointed_to_heap_vars + | _ -> state + end + | _ -> state + + let threadenter ctx lval f args = [ctx.local] + let threadspawn ctx lval f args fctx = ctx.local + + let startstate v = D.bot () + let exitstate v = D.top () +end + +let _ = + MCP.register_analysis (module Spec : MCPSpec) \ No newline at end of file From 933b4e3482175aab6336b0308dab2ccb1a9f7960 Mon Sep 17 00:00:00 2001 From: FelixKrayer Date: Thu, 3 Aug 2023 17:27:16 +0200 Subject: [PATCH 0400/1312] indentation and removing unnecessary implementations --- src/analyses/libraryDesc.ml | 18 +++++++++--------- src/analyses/tmpSpecial.ml | 13 ++----------- 2 files changed, 11 insertions(+), 20 deletions(-) diff --git a/src/analyses/libraryDesc.ml b/src/analyses/libraryDesc.ml index c009c3c2fb..94de4fbf82 100644 --- a/src/analyses/libraryDesc.ml +++ b/src/analyses/libraryDesc.ml @@ -186,15 +186,15 @@ module MathPrintable = struct | Sin (fk, exp) -> Pretty.dprintf "(%a )sin(%a)" d_fkind fk d_exp exp | Tan (fk, exp) -> Pretty.dprintf "(%a )tan(%a)" d_fkind fk d_exp exp - include Printable.SimplePretty ( - struct - type nonrec t = t - let pretty = pretty - end - ) + include Printable.SimplePretty ( + struct + type nonrec t = t + let pretty = pretty + end + ) end module MathLifted = Lattice.Flat (MathPrintable) (struct - let top_name = "Unknown or no math desc" - let bot_name = "Nonexistent math desc" -end) + let top_name = "Unknown or no math desc" + let bot_name = "Nonexistent math desc" + end) diff --git a/src/analyses/tmpSpecial.ml b/src/analyses/tmpSpecial.ml index 5d43f69add..c4af80906e 100644 --- a/src/analyses/tmpSpecial.ml +++ b/src/analyses/tmpSpecial.ml @@ -28,21 +28,12 @@ struct (* Invalidate all entrys from the map that are possibly written by the assignment *) invalidate (Analyses.ask_of_ctx ctx) (mkAddrOf lval) ctx.local - let branch ctx (exp:exp) (tv:bool) : D.t = - ctx.local - - let body ctx (f:fundec) : D.t = - ctx.local - - let return ctx (exp:exp option) (f:fundec) : D.t = - ctx.local - let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = - (* For now we only track relationships intraprocedurally. TODO: handle interprocedural tracking *) + (* For now we only track relationships intraprocedurally. *) [ctx.local, D.bot ()] let combine ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) f_ask : D.t = - (* For now we only track relationships intraprocedurally. TODO: handle interprocedural tracking *) + (* For now we only track relationships intraprocedurally. *) D.bot () let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = From 3573182fa4e22ce8f448f6337b007f279c892e18 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 4 Aug 2023 10:24:36 +0200 Subject: [PATCH 0401/1312] Add two simple regression tests --- tests/regression/76-memleak/01-simple-no-mem-leak.c | 9 +++++++++ tests/regression/76-memleak/02-simple-mem-leak.c | 8 ++++++++ 2 files changed, 17 insertions(+) create mode 100644 tests/regression/76-memleak/01-simple-no-mem-leak.c create mode 100644 tests/regression/76-memleak/02-simple-mem-leak.c diff --git a/tests/regression/76-memleak/01-simple-no-mem-leak.c b/tests/regression/76-memleak/01-simple-no-mem-leak.c new file mode 100644 index 0000000000..da6cdacddb --- /dev/null +++ b/tests/regression/76-memleak/01-simple-no-mem-leak.c @@ -0,0 +1,9 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak +#include + +int main(int argc, char const *argv[]) { + int *p = malloc(sizeof(int)); + free(p); + + return 0; //NOWARN +} diff --git a/tests/regression/76-memleak/02-simple-mem-leak.c b/tests/regression/76-memleak/02-simple-mem-leak.c new file mode 100644 index 0000000000..c3086433dd --- /dev/null +++ b/tests/regression/76-memleak/02-simple-mem-leak.c @@ -0,0 +1,8 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak +#include + +int main(int argc, char const *argv[]) { + int *p = malloc(sizeof(int)); + // No free => memory is leaked + return 0; //TODO: `make test` detects OTHER and not WARN here +} From f2b7d1c0f3ac1dcec8107a67eb6bb19869943e9a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 4 Aug 2023 17:24:29 +0300 Subject: [PATCH 0402/1312] Fix FloatDomain whitespace (PR #1041) --- src/cdomains/floatDomain.ml | 40 ++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/cdomains/floatDomain.ml b/src/cdomains/floatDomain.ml index baae801585..f52c849111 100644 --- a/src/cdomains/floatDomain.ml +++ b/src/cdomains/floatDomain.ml @@ -671,38 +671,42 @@ module FloatIntervalImpl(Float_t : CFloatType) = struct | _ -> top () (**could be exact for intervals where l=h, or even for some intervals *) let eval_inv_ceil ?(asPreciseAsConcrete=false) = function - | (l, h) -> - if (Float_t.sub Up (Float_t.ceil l) (Float_t.sub Down (Float_t.ceil l) (Float_t.of_float Nearest 1.0)) = (Float_t.of_float Nearest 1.0)) then + | (l, h) -> + if (Float_t.sub Up (Float_t.ceil l) (Float_t.sub Down (Float_t.ceil l) (Float_t.of_float Nearest 1.0)) = (Float_t.of_float Nearest 1.0)) then ( (* if [ceil(l) - (ceil(l) - 1.0) = 1.0], then we are in a range, where each int is expressable as float. - With that we can say, that [(ceil(x) >= l) => (x > (ceil(l) - 1.0)] *) + With that we can say, that [(ceil(x) >= l) => (x > (ceil(l) - 1.0)] *) if asPreciseAsConcrete then (* in case abstract and concrete precision are the same, [succ(l - 1.0), h] is more precise *) Interval (Float_t.succ (Float_t.sub Down (Float_t.ceil l) (Float_t.of_float Nearest 1.0)), h) else Interval (Float_t.sub Down (Float_t.ceil l) (Float_t.of_float Nearest 1.0), h) - else + ) + else ( (* if we know the abstract and concrete precision are the same, we return [l, h] as an interval, since no x in [l - 1.0, l] could exist such that ceil(x) = l appart from l itself *) if asPreciseAsConcrete then Interval (l, h) else Interval (Float_t.pred l, h) + ) let eval_inv_floor ?(asPreciseAsConcrete=false) = function - | (l, h) -> - if (Float_t.sub Up (Float_t.add Up (Float_t.floor h) (Float_t.of_float Nearest 1.0)) (Float_t.floor h) = (Float_t.of_float Nearest 1.0)) then + | (l, h) -> + if (Float_t.sub Up (Float_t.add Up (Float_t.floor h) (Float_t.of_float Nearest 1.0)) (Float_t.floor h) = (Float_t.of_float Nearest 1.0)) then ( (* if [(floor(h) + 1.0) - floor(h) = 1.0], then we are in a range, where each int is expressable as float. - With that we can say, that [(floor(x) <= h) => (x < (floor(h) + 1.0)] *) + With that we can say, that [(floor(x) <= h) => (x < (floor(h) + 1.0)] *) if asPreciseAsConcrete then (* in case abstract and concrete precision are the same, [l, pred(floor(h) + 1.0)] is more precise than [l, floor(h) + 1.0] *) Interval (l, Float_t.pred (Float_t.add Up (Float_t.floor h) (Float_t.of_float Nearest 1.0))) else Interval (l, Float_t.add Up (Float_t.floor h) (Float_t.of_float Nearest 1.0)) - else + ) + else ( (* if we know the abstract and concrete precision are the same, we return [l, h] as an interval, since no x in [h, h + 1.0] could exist such that floor(x) = h appart from h itself *) if asPreciseAsConcrete then Interval (l, h) else Interval (l, Float_t.succ h) + ) let eval_inv_fabs = function | (_, h) when h < Float_t.zero -> Bot (* Result of fabs cannot be negative *) @@ -897,16 +901,16 @@ module FloatIntervalImplLifted = struct let tan = lift (F1.tan, F2.tan) let inv_ceil ?(asPreciseAsConcrete=BoolDomain.MustBool.top ()) = function - | F32 a -> F32 (F1.inv_ceil ~asPreciseAsConcrete:true a) - | F64 a -> F64 (F2.inv_ceil ~asPreciseAsConcrete:true a) - | FLong a -> FLong (F2.inv_ceil a) - | FFloat128 a -> FFloat128 (F2.inv_ceil a) + | F32 a -> F32 (F1.inv_ceil ~asPreciseAsConcrete:true a) + | F64 a -> F64 (F2.inv_ceil ~asPreciseAsConcrete:true a) + | FLong a -> FLong (F2.inv_ceil a) + | FFloat128 a -> FFloat128 (F2.inv_ceil a) let inv_floor ?(asPreciseAsConcrete=BoolDomain.MustBool.top ()) = function - | F32 a -> F32 (F1.inv_floor ~asPreciseAsConcrete:true a) - | F64 a -> F64 (F2.inv_floor ~asPreciseAsConcrete:true a) - | FLong a -> FLong (F2.inv_floor a) - | FFloat128 a -> FFloat128 (F2.inv_floor a) + | F32 a -> F32 (F1.inv_floor ~asPreciseAsConcrete:true a) + | F64 a -> F64 (F2.inv_floor ~asPreciseAsConcrete:true a) + | FLong a -> FLong (F2.inv_floor a) + | FFloat128 a -> FFloat128 (F2.inv_floor a) let inv_fabs = lift (F1.inv_fabs, F2.inv_fabs) let add = lift2 (F1.add, F2.add) @@ -1078,7 +1082,7 @@ module FloatDomTupleImpl = struct let starting_after fkind = create { fi= (fun (type a) (module F : FloatDomain with type t = a) -> F.starting_after fkind); } let finite = - create { fi= (fun (type a) (module F : FloatDomain with type t = a) -> F.finite); } + create { fi= (fun (type a) (module F : FloatDomain with type t = a) -> F.finite); } let of_string fkind = create { fi= (fun (type a) (module F : FloatDomain with type t = a) -> F.of_string fkind); } @@ -1164,7 +1168,7 @@ module FloatDomTupleImpl = struct map { f1= (fun (type a) (module F : FloatDomain with type t = a) -> F.inv_floor ~asPreciseAsConcrete:(BoolDomain.MustBool.top ())); } let inv_fabs = map { f1= (fun (type a) (module F : FloatDomain with type t = a) -> F.inv_fabs); } - + (* f2: binary ops *) let join = map2 { f2= (fun (type a) (module F : FloatDomain with type t = a) -> F.join); } From 15a5b1a1470014017df01ba51e1ddf41c35a157f Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 6 Aug 2023 14:34:29 +0200 Subject: [PATCH 0403/1312] Return bot in BlobSize queries if there's a non-heap var or some offset --- src/analyses/base.ml | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 6ec15f0b31..a213170ba2 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1261,11 +1261,22 @@ struct (* ignore @@ printf "BlobSize %a MayPointTo %a\n" d_plainexp e VD.pretty p; *) match p with | Address a -> - let r = get ~full:true (Analyses.ask_of_ctx ctx) ctx.global ctx.local a None in - (* ignore @@ printf "BlobSize %a = %a\n" d_plainexp e VD.pretty r; *) - (match r with - | Blob (_,s,_) -> `Lifted s - | _ -> Queries.Result.top q) + let s = addrToLvalSet a in + let has_offset = function + | `NoOffset -> false + | `Field _ + | `Index _ -> true + in + (* If there's a non-heap var or an offset in the lval set, we answer with bottom *) + if ValueDomainQueries.LS.exists (fun (v, o) -> (not @@ ctx.ask (Queries.IsHeapVar v)) || has_offset o) s then + Queries.Result.bot q + else ( + let r = get ~full:true (Analyses.ask_of_ctx ctx) ctx.global ctx.local a None in + (* ignore @@ printf "BlobSize %a = %a\n" d_plainexp e VD.pretty r; *) + (match r with + | Blob (_,s,_) -> `Lifted s + | _ -> Queries.Result.top q) + ) | _ -> Queries.Result.top q end | Q.MayPointTo e -> begin From ad19f893aad67e9909d1de9969ea79177f087e2e Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 8 Aug 2023 16:11:15 +0200 Subject: [PATCH 0404/1312] Don't warn with a newline at the end --- src/analyses/memLeak.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index d4a1bc967d..1fc25c71ef 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -36,7 +36,7 @@ struct let state = ctx.local in (* TODO: Is this too hacky of a solution? *) if f.svar.vname = "main" && not @@ D.is_empty state then - M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory leak from function \"%s\": %a\n" f.svar.vname D.pretty state; + M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory leak from function \"%s\": %a" f.svar.vname D.pretty state; state let enter ctx (lval:lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = From 3711fc4e63ca2676404c2b821eda444f6120e0f1 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 8 Aug 2023 16:12:33 +0200 Subject: [PATCH 0405/1312] Clean up comments of 2nd memleak test case --- tests/regression/76-memleak/02-simple-mem-leak.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression/76-memleak/02-simple-mem-leak.c b/tests/regression/76-memleak/02-simple-mem-leak.c index c3086433dd..3673addfdf 100644 --- a/tests/regression/76-memleak/02-simple-mem-leak.c +++ b/tests/regression/76-memleak/02-simple-mem-leak.c @@ -4,5 +4,5 @@ int main(int argc, char const *argv[]) { int *p = malloc(sizeof(int)); // No free => memory is leaked - return 0; //TODO: `make test` detects OTHER and not WARN here + return 0; //WARN } From 90d0a64f0627e0b1419d73a7e0aeff1fd9eba43c Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 8 Aug 2023 16:16:23 +0200 Subject: [PATCH 0406/1312] Remove only definitely freed heap vars from state upon free() --- src/analyses/memLeak.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index 1fc25c71ef..15a8947f15 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -63,7 +63,7 @@ struct end | Free ptr -> begin match ctx.ask (Queries.MayPointTo ptr) with - | a when not (Queries.LS.is_top a) && not (Queries.LS.mem (dummyFunDec.svar, `NoOffset) a) -> + | a when not (Queries.LS.is_top a) && not (Queries.LS.mem (dummyFunDec.svar, `NoOffset) a) && Queries.LS.cardinal a = 1 -> (* TODO: Need to always set "ana.malloc.unique_address_count" to smth > 0 *) let unique_pointed_to_heap_vars = Queries.LS.filter (fun (v, _) -> ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsMultiple v)) a From 83d2578ded2cc8f228d6bd4d8d6d79cff7063430 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 8 Aug 2023 18:41:08 +0200 Subject: [PATCH 0407/1312] Handle abort and assert special functions Make more elaborate warning for mem leaks --- src/analyses/memLeak.ml | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index 15a8947f15..c7a7b97843 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -22,6 +22,14 @@ struct if not (ctx.ask (Queries.MustBeSingleThreaded { since_start = true })) then M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Program isn't running in single-threaded mode. A memory leak might occur due to multi-threading" + let check_for_mem_leak ?(assert_exp_imprecise = false) ?(exp = None) ctx = + let state = ctx.local in + if not @@ D.is_empty state then + if assert_exp_imprecise && Option.is_some exp then + M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "assert expression %a is unknown. Memory leak might possibly occur for heap variables: %a" d_exp (Option.get exp) D.pretty state + else + M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory leak detected for heap variables: %a" D.pretty state + (* TRANSFER FUNCTIONS *) let assign ctx (lval:lval) (rval:exp) : D.t = ctx.local @@ -33,11 +41,9 @@ struct ctx.local let return ctx (exp:exp option) (f:fundec) : D.t = - let state = ctx.local in - (* TODO: Is this too hacky of a solution? *) - if f.svar.vname = "main" && not @@ D.is_empty state then - M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory leak from function \"%s\": %a" f.svar.vname D.pretty state; - state + (* Returning from "main" is one possible program exit => need to check for memory leaks *) + if f.svar.vname = "main" then check_for_mem_leak ctx; + ctx.local let enter ctx (lval:lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = [ctx.local, ctx.local] @@ -74,6 +80,26 @@ struct D.diff state unique_pointed_to_heap_vars | _ -> state end + | Abort -> + (* An "Abort" special function indicates program exit => need to check for memory leaks *) + check_for_mem_leak ctx; + state + | Assert { exp; _ } -> + let warn_for_assert_exp = + match ctx.ask (Queries.EvalInt exp) with + | a when Queries.ID.is_bot a -> M.warn ~category:Assert "assert expression %a is bottom" d_exp exp + | a -> + begin match Queries.ID.to_bool a with + | Some b -> + (* If we know for sure that the expression in "assert" is false => need to check for memory leaks *) + if b = false then + check_for_mem_leak ctx + else () + | None -> check_for_mem_leak ctx ~assert_exp_imprecise:true ~exp:(Some exp) + end + in + warn_for_assert_exp; + state | _ -> state let threadenter ctx lval f args = [ctx.local] From 79d13cd3e1f5a24026f3ee9ac45239c493f330d6 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 8 Aug 2023 18:41:41 +0200 Subject: [PATCH 0408/1312] Add further regression test cases --- tests/regression/76-memleak/03-simple-exit-mem-leak.c | 7 +++++++ tests/regression/76-memleak/04-simple-abort-mem-leak.c | 7 +++++++ .../76-memleak/05-simple-assert-no-mem-leak.c | 10 ++++++++++ .../regression/76-memleak/06-simple-assert-mem-leak.c | 8 ++++++++ 4 files changed, 32 insertions(+) create mode 100644 tests/regression/76-memleak/03-simple-exit-mem-leak.c create mode 100644 tests/regression/76-memleak/04-simple-abort-mem-leak.c create mode 100644 tests/regression/76-memleak/05-simple-assert-no-mem-leak.c create mode 100644 tests/regression/76-memleak/06-simple-assert-mem-leak.c diff --git a/tests/regression/76-memleak/03-simple-exit-mem-leak.c b/tests/regression/76-memleak/03-simple-exit-mem-leak.c new file mode 100644 index 0000000000..451dafa471 --- /dev/null +++ b/tests/regression/76-memleak/03-simple-exit-mem-leak.c @@ -0,0 +1,7 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak +#include + +int main(int argc, char const *argv[]) { + int *p = malloc(sizeof(int)); + exit(0); //WARN +} diff --git a/tests/regression/76-memleak/04-simple-abort-mem-leak.c b/tests/regression/76-memleak/04-simple-abort-mem-leak.c new file mode 100644 index 0000000000..d4001410de --- /dev/null +++ b/tests/regression/76-memleak/04-simple-abort-mem-leak.c @@ -0,0 +1,7 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak +#include + +int main(int argc, char const *argv[]) { + int *p = malloc(sizeof(int)); + abort(); //WARN +} diff --git a/tests/regression/76-memleak/05-simple-assert-no-mem-leak.c b/tests/regression/76-memleak/05-simple-assert-no-mem-leak.c new file mode 100644 index 0000000000..bdaf448ee8 --- /dev/null +++ b/tests/regression/76-memleak/05-simple-assert-no-mem-leak.c @@ -0,0 +1,10 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak +#include +#include + +int main(int argc, char const *argv[]) { + int *p = malloc(sizeof(int)); + assert(1); //NOWARN + free(p); + return 0; //NOWARN +} diff --git a/tests/regression/76-memleak/06-simple-assert-mem-leak.c b/tests/regression/76-memleak/06-simple-assert-mem-leak.c new file mode 100644 index 0000000000..aff78ddd59 --- /dev/null +++ b/tests/regression/76-memleak/06-simple-assert-mem-leak.c @@ -0,0 +1,8 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak +#include +#include + +int main(int argc, char const *argv[]) { + int *p = malloc(sizeof(int)); + assert(0); //WARN +} From 3bf7d4a426a10300af6983496c20d31317325d6b Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 8 Aug 2023 18:47:26 +0200 Subject: [PATCH 0409/1312] Fix some regression test annotations --- tests/regression/76-memleak/05-simple-assert-no-mem-leak.c | 2 +- tests/regression/76-memleak/06-simple-assert-mem-leak.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/regression/76-memleak/05-simple-assert-no-mem-leak.c b/tests/regression/76-memleak/05-simple-assert-no-mem-leak.c index bdaf448ee8..8dbf20c433 100644 --- a/tests/regression/76-memleak/05-simple-assert-no-mem-leak.c +++ b/tests/regression/76-memleak/05-simple-assert-no-mem-leak.c @@ -4,7 +4,7 @@ int main(int argc, char const *argv[]) { int *p = malloc(sizeof(int)); - assert(1); //NOWARN + assert(1); free(p); return 0; //NOWARN } diff --git a/tests/regression/76-memleak/06-simple-assert-mem-leak.c b/tests/regression/76-memleak/06-simple-assert-mem-leak.c index aff78ddd59..b2f78388dc 100644 --- a/tests/regression/76-memleak/06-simple-assert-mem-leak.c +++ b/tests/regression/76-memleak/06-simple-assert-mem-leak.c @@ -1,4 +1,4 @@ -//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak +//PARAM: --set warn.assert false --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak #include #include From 908aa591cd85ae2feb8b6d041bd6da30c3b0a073 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 8 Aug 2023 18:52:55 +0200 Subject: [PATCH 0410/1312] Fix the mem leak check function --- src/analyses/memLeak.ml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index c7a7b97843..559f2badb7 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -25,10 +25,9 @@ struct let check_for_mem_leak ?(assert_exp_imprecise = false) ?(exp = None) ctx = let state = ctx.local in if not @@ D.is_empty state then - if assert_exp_imprecise && Option.is_some exp then - M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "assert expression %a is unknown. Memory leak might possibly occur for heap variables: %a" d_exp (Option.get exp) D.pretty state - else - M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory leak detected for heap variables: %a" D.pretty state + match assert_exp_imprecise, exp with + | true, Some exp -> M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "assert expression %a is unknown. Memory leak might possibly occur for heap variables: %a" d_exp exp D.pretty state + | _ -> M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory leak detected for heap variables: %a" D.pretty state (* TRANSFER FUNCTIONS *) let assign ctx (lval:lval) (rval:exp) : D.t = From c3c4bb1f96077c17276a4bd1ba5aec52ef7c42dd Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 9 Aug 2023 15:12:56 +0200 Subject: [PATCH 0411/1312] Clean up the code a bit --- src/analyses/memLeak.ml | 25 ++----------------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index 559f2badb7..99df5695a7 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -8,7 +8,7 @@ module ToppedVarInfoSet = SetDomain.ToppedSet(CilType.Varinfo)(struct let topnam module Spec : Analyses.MCPSpec = struct - include Analyses.DefaultSpec + include Analyses.IdentitySpec let name () = "memLeak" @@ -30,29 +30,11 @@ struct | _ -> M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory leak detected for heap variables: %a" D.pretty state (* TRANSFER FUNCTIONS *) - let assign ctx (lval:lval) (rval:exp) : D.t = - ctx.local - - let branch ctx (exp:exp) (tv:bool) : D.t = - ctx.local - - let body ctx (f:fundec) : D.t = - ctx.local - let return ctx (exp:exp option) (f:fundec) : D.t = (* Returning from "main" is one possible program exit => need to check for memory leaks *) if f.svar.vname = "main" then check_for_mem_leak ctx; ctx.local - let enter ctx (lval:lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = - [ctx.local, ctx.local] - - let combine_env ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (callee_local:D.t) (f_ask:Queries.ask) : D.t = - callee_local - - let combine_assign ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (callee_local:D.t) (f_ask: Queries.ask): D.t = - ctx.local - let special ctx (lval:lval option) (f:varinfo) (arglist:exp list) : D.t = let state = ctx.local in let desc = LibraryFunctions.find f in @@ -69,7 +51,7 @@ struct | Free ptr -> begin match ctx.ask (Queries.MayPointTo ptr) with | a when not (Queries.LS.is_top a) && not (Queries.LS.mem (dummyFunDec.svar, `NoOffset) a) && Queries.LS.cardinal a = 1 -> - (* TODO: Need to always set "ana.malloc.unique_address_count" to smth > 0 *) + (* Note: Need to always set "ana.malloc.unique_address_count" to a value > 0 *) let unique_pointed_to_heap_vars = Queries.LS.filter (fun (v, _) -> ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsMultiple v)) a |> Queries.LS.elements @@ -101,9 +83,6 @@ struct state | _ -> state - let threadenter ctx lval f args = [ctx.local] - let threadspawn ctx lval f args fctx = ctx.local - let startstate v = D.bot () let exitstate v = D.top () end From 0652ea09f4892c4f39350b50bdb596ecdac1d048 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 9 Aug 2023 16:01:11 +0200 Subject: [PATCH 0412/1312] Add support for quick_exit --- src/analyses/libraryFunctions.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 447263b3ef..f4a9ac803d 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -68,6 +68,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("free", special [__ "ptr" [f]] @@ fun ptr -> Free ptr); ("abort", special [] Abort); ("exit", special [drop "exit_code" []] Abort); + ("quick_exit", special [drop "exit_code" []] Abort); ("ungetc", unknown [drop "c" []; drop "stream" [r; w]]); ("scanf", unknown ((drop "format" [r]) :: (VarArgs (drop' [w])))); ("fscanf", unknown ((drop "stream" [r_deep; w_deep]) :: (drop "format" [r]) :: (VarArgs (drop' [w])))); From 545714e6f552495652829fe9a110f414842d0606 Mon Sep 17 00:00:00 2001 From: Nathan Schmidt Date: Wed, 9 Aug 2023 17:33:54 +0200 Subject: [PATCH 0413/1312] Add tests from Juliet --- src/cdomains/arrayDomain.ml | 9 +++------ src/cdomains/valueDomain.ml | 2 +- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 7892826e57..68e64f125b 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1529,15 +1529,12 @@ struct let string_concat (must_nulls_set1, may_nulls_set1, size1) (must_nulls_set2, may_nulls_set2, size2) n = let update_sets min_size1 max_size1 max_size1_exists minlen1 maxlen1 maxlen1_exists minlen2 maxlen2 maxlen2_exists must_nulls_set2' may_nulls_set2' = (* track any potential buffer overflow and issue warning if needed *) - (if max_size1_exists && Z.lt max_size1 (Z.add minlen1 minlen2) then + (if max_size1_exists && Z.leq max_size1 (Z.add minlen1 minlen2) then M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of the concatenation of the strings in src and dest is greater than the allocated size for dest" - else if (maxlen1_exists && maxlen2_exists && Z.lt min_size1 (Z.add maxlen1 maxlen2)) - || (maxlen1_exists && Z.lt min_size1 (Z.add maxlen1 minlen2)) - || (maxlen2_exists && Z.lt min_size1 (Z.add minlen1 maxlen2)) - || Z.lt min_size1 (Z.add minlen1 minlen2) then + else if (maxlen1_exists && maxlen2_exists && Z.leq min_size1 (Z.add maxlen1 maxlen2)) || not maxlen1_exists || not maxlen2_exists then M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end - "The length of the conctenation of the strings in src and dest may be greater than the allocated size for dest"); + "The length of the concatenation of the strings in src and dest may be greater than the allocated size for dest"); (* if any must_nulls_set empty, result must_nulls_set also empty; * for all i1, i2 in may_nulls_set1, may_nulls_set2: add i1 + i2 if it is <= strlen(dest) + strlen(src) to new may_nulls_set * and keep indexes > minimal strlen(dest) + strlen(src) of may_nulls_set *) diff --git a/src/cdomains/valueDomain.ml b/src/cdomains/valueDomain.ml index 7480ca12a6..5dcebf71ce 100644 --- a/src/cdomains/valueDomain.ml +++ b/src/cdomains/valueDomain.ml @@ -300,7 +300,7 @@ struct | _ -> false end | Address a when AD.may_be_null a -> false - | _ -> true + | _ -> false (* we don't know anything *) let is_int_ikind = function | Int n -> Some (ID.ikind n) From 0acbf242523dfad622fabb252d9c5dbe31575ac1 Mon Sep 17 00:00:00 2001 From: Nathan Schmidt Date: Wed, 9 Aug 2023 17:34:48 +0200 Subject: [PATCH 0414/1312] Add tests from Juliet --- tests/regression/73-strings/06-juliet.c | 145 ++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 tests/regression/73-strings/06-juliet.c diff --git a/tests/regression/73-strings/06-juliet.c b/tests/regression/73-strings/06-juliet.c new file mode 100644 index 0000000000..53bc2ba4e9 --- /dev/null +++ b/tests/regression/73-strings/06-juliet.c @@ -0,0 +1,145 @@ +// PARAM: --disable ana.base.limit-string-addresses --enable ana.int.interval --enable ana.base.arrays.nullbytes + +#include +#include +#include + +// TODO: tackle memset -> map it to for loop with set for each cell + +int main() { + CWE121_Stack_Based_Buffer_Overflow__src_char_declare_cpy_01_bad(); + CWE126_Buffer_Overread__CWE170_char_loop_01_bad(); + CWE126_Buffer_Overread__CWE170_char_strncpy_01_bad(); + CWE126_Buffer_Overread__char_declare_loop_01_bad(); + CWE571_Expression_Always_True__string_equals_01_bad(); + CWE665_Improper_Initialization__char_cat_01_bad(); + CWE665_Improper_Initialization__char_ncat_11_bad(); + + return 0; +} + +void CWE121_Stack_Based_Buffer_Overflow__src_char_declare_cpy_01_bad() +{ + char * data; + char dataBuffer[100]; + data = dataBuffer; + /* FLAW: Initialize data as a large buffer that is larger than the small buffer used in the sink */ + memset(data, 'A', 100-1); /* fill with 'A's */ + data[100-1] = '\0'; /* null terminate */ + { + char dest[50] = ""; + /* POTENTIAL FLAW: Possible buffer overflow if data is larger than dest */ + strcpy(dest, data); // WARN + } +} + +void CWE126_Buffer_Overread__CWE170_char_loop_01_bad() +{ + { + char src[150], dest[100]; + int i; + /* Initialize src */ + memset(src, 'A', 149); + src[149] = '\0'; + for(i=0; i < 99; i++) + { + dest[i] = src[i]; + } + /* FLAW: do not explicitly null terminate dest after the loop */ + __goblint_check(dest[42] != '\0'); + __goblint_check(dest[99] != '\0'); // UNKNOWN + } +} + +void CWE126_Buffer_Overread__CWE170_char_strncpy_01_bad() +{ + { + char data[150], dest[100]; + /* Initialize data */ + memset(data, 'A', 149); + data[149] = '\0'; + /* strncpy() does not null terminate if the string in the src buffer is larger than + * the number of characters being copied to the dest buffer */ + strncpy(dest, data, 99); // WARN + /* FLAW: do not explicitly null terminate dest after the use of strncpy() */ + } +} + +void CWE126_Buffer_Overread__char_declare_loop_01_bad() +{ + char * data; + char dataBadBuffer[50]; + char dataGoodBuffer[100]; + memset(dataBadBuffer, 'A', 50-1); /* fill with 'A's */ + dataBadBuffer[50-1] = '\0'; /* null terminate */ + memset(dataGoodBuffer, 'A', 100-1); /* fill with 'A's */ + dataGoodBuffer[100-1] = '\0'; /* null terminate */ + /* FLAW: Set data pointer to a small buffer */ + data = dataBadBuffer; + { + size_t i, destLen; + char dest[100]; + memset(dest, 'C', 100-1); + dest[100-1] = '\0'; /* null terminate */ + destLen = strlen(dest); + __goblint_check(destLen == 99); + /* POTENTIAL FLAW: using length of the dest where data + * could be smaller than dest causing buffer overread */ + for (i = 0; i < destLen; i++) + { + dest[i] = data[i]; + } + dest[100-1] = '\0'; + } +} + +void CWE665_Improper_Initialization__char_cat_01_bad() +{ + char * data; + char dataBuffer[100]; + data = dataBuffer; + /* FLAW: Do not initialize data */ + ; /* empty statement needed for some flow variants */ + { + char source[100]; + memset(source, 'C', 100-1); /* fill with 'C's */ + source[100-1] = '\0'; /* null terminate */ + /* POTENTIAL FLAW: If data is not initialized properly, strcat() may not function correctly */ + strcat(data, source); // WARN + } +} + +void CWE571_Expression_Always_True__string_equals_01_bad() +{ + char charString[10] = "true"; + int cmp = strcmp(charString, "true"); + __goblint_check(cmp == 0); // UNKNOWN + + /* FLAW: This expression is always true */ + if (cmp == 0) + { + printf("always prints"); + } +} + +void CWE665_Improper_Initialization__char_ncat_11_bad() +{ + char * data; + char dataBuffer[100]; + data = dataBuffer; + if(rand()) + { + /* FLAW: Do not initialize data */ + ; /* empty statement needed for some flow variants */ + } + { + size_t sourceLen; + char source[100]; + memset(source, 'C', 100-1); /* fill with 'C's */ + source[100-1] = '\0'; /* null terminate */ + sourceLen = strlen(source); + __goblint_check(sourceLen == 99); + /* POTENTIAL FLAW: If data is not initialized properly, strncat() may not function correctly */ + strncat(data, source, sourceLen); // WARN --> why not?? spurious + } +} From f4d74e2129a7d4e1854d49d7d258a66c04aac472 Mon Sep 17 00:00:00 2001 From: Nathan Schmidt Date: Wed, 9 Aug 2023 19:14:21 +0200 Subject: [PATCH 0415/1312] Added larger example --- .../regression/73-strings/07-larger_example.c | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 tests/regression/73-strings/07-larger_example.c diff --git a/tests/regression/73-strings/07-larger_example.c b/tests/regression/73-strings/07-larger_example.c new file mode 100644 index 0000000000..08676661e6 --- /dev/null +++ b/tests/regression/73-strings/07-larger_example.c @@ -0,0 +1,36 @@ +// PARAM: --disable ana.base.limit-string-addresses --enable ana.int.interval --enable ana.base.arrays.nullbytes + +#include +#include +#include + +int main() { + char* user; + if (rand()) + user = "Alice"; + else + user = "Bob"; + + if (strcmp(user, "Alice") == 0) + strcpy(user, "++++++++"); // WARN + + char pwd_gen[20]; + + char* p1 = "hello"; + char* p2 = "12345"; + strcat(pwd_gen, p1); // WARN + strncpy(pwd_gen, p2, 6); + __goblint_check(pwd_gen[5] == '\0'); // TODO: fix get in attributeconfiguredarraydomain + strncat(pwd_gen, p1, 4); + __goblint_check(pwd_gen[5] != '\0'); // TODO: fix get in attributeconfiguredarraydomain + + pwd_gen[10] = '\0'; + int cmp = strcmp(pwd_gen, "12345hello"); + __goblint_check(cmp != 0); + + char* pwd = strstr(pwd_gen, p2); + size_t pwd_len = strlen(pwd_gen); + __goblint_check(pwd_len == 9); + + return 0; +} \ No newline at end of file From a24546f55d3f9b0b9c70c836956bfa98c90fcb06 Mon Sep 17 00:00:00 2001 From: Nathan Schmidt <73504207+nathanschmidt@users.noreply.github.com> Date: Wed, 9 Aug 2023 20:35:31 +0200 Subject: [PATCH 0416/1312] Update 07-larger_example.c --- tests/regression/73-strings/07-larger_example.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/regression/73-strings/07-larger_example.c b/tests/regression/73-strings/07-larger_example.c index 08676661e6..950011244b 100644 --- a/tests/regression/73-strings/07-larger_example.c +++ b/tests/regression/73-strings/07-larger_example.c @@ -15,6 +15,8 @@ int main() { strcpy(user, "++++++++"); // WARN char pwd_gen[20]; + for (size_t i = 12; i < 20; i++) + pwd_gen[i] = (char) (rand() % 123); char* p1 = "hello"; char* p2 = "12345"; @@ -33,4 +35,4 @@ int main() { __goblint_check(pwd_len == 9); return 0; -} \ No newline at end of file +} From cc826231df404cad3d77a182477a89e543f39a37 Mon Sep 17 00:00:00 2001 From: Nathan Schmidt Date: Thu, 10 Aug 2023 20:00:30 +0200 Subject: [PATCH 0417/1312] Modification to larger example --- tests/regression/73-strings/07-larger_example.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/regression/73-strings/07-larger_example.c b/tests/regression/73-strings/07-larger_example.c index 950011244b..5dce3b0cfe 100644 --- a/tests/regression/73-strings/07-larger_example.c +++ b/tests/regression/73-strings/07-larger_example.c @@ -14,6 +14,10 @@ int main() { if (strcmp(user, "Alice") == 0) strcpy(user, "++++++++"); // WARN + __goblint_check(strcmp(user, "Alice") == 0); // UNKNOWN + __goblint_check(strcmp(user, "Bob") == 0); // UNKNOWN + __goblint_check(strcmp(user, "Eve") != 0); // TODO: check implementation, maybe returning top wrong and we should return bot in string literals domain + char pwd_gen[20]; for (size_t i = 12; i < 20; i++) pwd_gen[i] = (char) (rand() % 123); @@ -26,7 +30,6 @@ int main() { strncat(pwd_gen, p1, 4); __goblint_check(pwd_gen[5] != '\0'); // TODO: fix get in attributeconfiguredarraydomain - pwd_gen[10] = '\0'; int cmp = strcmp(pwd_gen, "12345hello"); __goblint_check(cmp != 0); From 755e4eef2b74107d05e1c7e5498bdc1123348ce6 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 11 Aug 2023 14:02:40 +0200 Subject: [PATCH 0418/1312] Add regression test for a memory leak with quick_exit() --- .../regression/76-memleak/07-simple-quick-exit-mem-leak.c | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 tests/regression/76-memleak/07-simple-quick-exit-mem-leak.c diff --git a/tests/regression/76-memleak/07-simple-quick-exit-mem-leak.c b/tests/regression/76-memleak/07-simple-quick-exit-mem-leak.c new file mode 100644 index 0000000000..eba23385b8 --- /dev/null +++ b/tests/regression/76-memleak/07-simple-quick-exit-mem-leak.c @@ -0,0 +1,7 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak +#include + +int main(int argc, char const *argv[]) { + int *p = malloc(sizeof(int)); + quick_exit(0); //WARN +} From 503fb2416b4669aedbf03f531e0eb405e85c2cd3 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 12 Aug 2023 08:11:06 +0200 Subject: [PATCH 0419/1312] Remove wrongly overtaken goblintutil.ml --- src/util/goblintutil.ml | 166 ---------------------------------------- 1 file changed, 166 deletions(-) delete mode 100644 src/util/goblintutil.ml diff --git a/src/util/goblintutil.ml b/src/util/goblintutil.ml deleted file mode 100644 index ecaa38f338..0000000000 --- a/src/util/goblintutil.ml +++ /dev/null @@ -1,166 +0,0 @@ -(** Globally accessible flags and utility functions. *) - -open GoblintCil -open GobConfig - - -(** Outputs information about what the goblin is doing *) -(* let verbose = ref false *) - -(** If this is true we output messages and collect accesses. - This is set to true in control.ml before we verify the result (or already before solving if warn = 'early') *) -let should_warn = ref false - -(** Whether signed overflow or underflow happened *) -let svcomp_may_overflow = ref false - -(** Whether an invalid pointer dereference happened *) -let may_invalid_deref = ref false - -(** The file where everything is output *) -let out = ref stdout - -(** Command for assigning an id to a varinfo. All varinfos directly created by Goblint should be modified by this method *) -let create_var (var: varinfo) = - (* TODO Hack: this offset should preempt conflicts with ids generated by CIL *) - let start_id = 10_000_000_000 in - let hash = Hashtbl.hash { var with vid = 0 } in - let hash = if hash < start_id then hash + start_id else hash in - { var with vid = hash } - -(* Type invariant variables. *) -let type_inv_tbl = Hashtbl.create 13 -let type_inv (c:compinfo) : varinfo = - try Hashtbl.find type_inv_tbl c.ckey - with Not_found -> - let i = create_var (makeGlobalVar ("{struct "^c.cname^"}") (TComp (c,[]))) in - Hashtbl.add type_inv_tbl c.ckey i; - i - -let is_blessed (t:typ): varinfo option = - let me_gusta x = List.mem x (get_string_list "exp.unique") in - match unrollType t with - | TComp (ci,_) when me_gusta ci.cname -> Some (type_inv ci) - | _ -> (None : varinfo option) - - -(** A hack to see if we are currently doing global inits *) -let global_initialization = ref false - -(** Another hack to see if earlyglobs is enabled *) -let earlyglobs = ref false - -(** Whether currently in postsolver evaluations (e.g. verify, warn) *) -let postsolving = ref false - -(* None if verification is disabled, Some true if verification succeeded, Some false if verification failed *) -let verified : bool option ref = ref None - -let escape = XmlUtil.escape (* TODO: inline everywhere *) - - -(** Creates a directory and returns the absolute path **) -let create_dir name = - let dirName = GobFpath.cwd_append name in - GobSys.mkdir_or_exists dirName; - dirName - -(** Remove directory and its content, as "rm -rf" would do. *) -let rm_rf path = - let rec f path = - let path_str = Fpath.to_string path in - if Sys.is_directory path_str then begin - let files = Array.map (Fpath.add_seg path) (Sys.readdir path_str) in - Array.iter f files; - Unix.rmdir path_str - end else - Sys.remove path_str - in - f path - - -exception Timeout - -let timeout = Timeout.timeout - -let seconds_of_duration_string = - let unit = function - | "" | "s" -> 1 - | "m" -> 60 - | "h" -> 60 * 60 - | s -> failwith ("Unkown duration unit " ^ s ^ ". Supported units are h, m, s.") - in - let int_rest f s = Scanf.sscanf s "%u%s" f in - let split s = BatString.(head s 1, tail s 1) in - let rec f i s = - let u, r = split s in (* unit, rest *) - i * (unit u) + if r = "" then 0 else int_rest f r - in - int_rest f - -let vars = ref 0 -let evals = ref 0 -let narrow_reuses = ref 0 - -(* print GC statistics; taken from Cil.Stats.print which also includes timing; there's also Gc.print_stat, but it's in words instead of MB and more info than we want (also slower than quick_stat since it goes through the heap) *) -let print_gc_quick_stat chn = - let gc = Gc.quick_stat () in - let printM (w: float) : string = - let coeff = float_of_int (Sys.word_size / 8) in - Printf.sprintf "%.2fMB" (w *. coeff /. 1000000.0) - in - Printf.fprintf chn - "Memory statistics: total=%s, max=%s, minor=%s, major=%s, promoted=%s\n minor collections=%d major collections=%d compactions=%d\n" - (printM (gc.Gc.minor_words +. gc.Gc.major_words - -. gc.Gc.promoted_words)) - (printM (float_of_int gc.Gc.top_heap_words)) - (printM gc.Gc.minor_words) - (printM gc.Gc.major_words) - (printM gc.Gc.promoted_words) - gc.Gc.minor_collections - gc.Gc.major_collections - gc.Gc.compactions; - gc - -let exe_dir = Fpath.(parent (v Sys.executable_name)) -let command_line = match Array.to_list Sys.argv with - | command :: arguments -> Filename.quote_command command arguments - | [] -> assert false - -(* https://ocaml.org/api/Sys.html#2_SignalnumbersforthestandardPOSIXsignals *) -(* https://ocaml.github.io/ocamlunix/signals.html *) -let signal_of_string = let open Sys in function - | "sigint" -> sigint (* Ctrl+C Interactive interrupt *) - | "sigtstp" -> sigtstp (* Ctrl+Z Interactive stop *) - | "sigquit" -> sigquit (* Ctrl+\ Interactive termination *) - | "sigalrm" -> sigalrm (* Timeout *) - | "sigkill" -> sigkill (* Termination (cannot be ignored) *) - | "sigsegv" -> sigsegv (* Invalid memory reference, https://github.com/goblint/analyzer/issues/206 *) - | "sigterm" -> sigterm (* Termination *) - | "sigusr1" -> sigusr1 (* Application-defined signal 1 *) - | "sigusr2" -> sigusr2 (* Application-defined signal 2 *) - | "sigstop" -> sigstop (* Stop *) - | "sigprof" -> sigprof (* Profiling interrupt *) - | "sigxcpu" -> sigxcpu (* Timeout in cpu time *) - | s -> failwith ("Unhandled signal " ^ s) - -let self_signal signal = Unix.kill (Unix.getpid ()) signal - -let rec for_all_in_range (a, b) f = - let module BI = IntOps.BigIntOps in - if BI.compare a b > 0 - then true - else f a && (for_all_in_range (BI.add a (BI.one), b) f) - -let dummy_obj = Obj.repr () - -let jobs () = - match get_int "jobs" with - | 0 -> Cpu.numcores () - | n -> n - -(** call [f], with [r] temporarily set to [x] *) -let with_ref r x = - let x0 = !r in - r := x; - Fun.protect ~finally:(fun () -> r := x0) From 0c53230cd3e0404f86fd2c3de0bc91863eeb7af5 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 12 Aug 2023 08:18:01 +0200 Subject: [PATCH 0420/1312] Move may_invalid_deref to analysisState --- src/framework/analysisState.ml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/framework/analysisState.ml b/src/framework/analysisState.ml index 0f3a9f55bc..32b4e4d608 100644 --- a/src/framework/analysisState.ml +++ b/src/framework/analysisState.ml @@ -7,6 +7,9 @@ let should_warn = ref false (** Whether signed overflow or underflow happened *) let svcomp_may_overflow = ref false +(** Whether an invalid pointer dereference happened *) +let svcomp_may_invalid_deref = ref false + (** A hack to see if we are currently doing global inits *) let global_initialization = ref false From c07d867dd75287f00afdbf1c1be1b8a02d463542 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 12 Aug 2023 08:19:23 +0200 Subject: [PATCH 0421/1312] Use AnalysisState in place of Goblintutil --- src/analyses/memOutOfBounds.ml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 415934b52d..85e8530de7 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -2,7 +2,7 @@ open GoblintCil open Analyses open MessageCategory -module GU = Goblintutil +module AS = AnalysisState module Spec = struct @@ -146,7 +146,7 @@ struct let (host, offset) = lval in match host, get_offset_size offset with | _, None -> - GU.may_invalid_deref := true; + AS.svcomp_may_invalid_deref := true; M.warn ~category:(Behavior undefined_behavior) "Offset size for lval %a not known. A memory out-of-bounds access may occur" CilType.Lval.pretty lval | Var v, Some oi -> begin match sizeOf v.vtype with @@ -154,14 +154,14 @@ struct begin match cilint_to_int_wrapper i with | Some i -> if i < oi then - GU.may_invalid_deref := true; + AS.svcomp_may_invalid_deref := true; M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Offset bigger than var type's size for lval %a. A memory out-of-bounds access must occur" CilType.Lval.pretty lval | _ -> - GU.may_invalid_deref := true; + AS.svcomp_may_invalid_deref := true; M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Unknown size of var %a for lval %a. A memory out-of-bounds access might occur" CilType.Varinfo.pretty v CilType.Lval.pretty lval end | _ -> - GU.may_invalid_deref := true; + AS.svcomp_may_invalid_deref := true; M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Unknown size of var %a for lval %a. A memory out-of-bounds access might occur" CilType.Varinfo.pretty v CilType.Lval.pretty lval end | Mem e, Some oi -> @@ -217,13 +217,13 @@ struct begin match ptr_size, offset_size with | Some pi, Some oi -> if pi < oi then - GU.may_invalid_deref := true; + AS.svcomp_may_invalid_deref := true; M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Pointer size in expression %a %a %a is smaller than offset for pointer arithmetic. Memory out-of-bounds access must occur" d_exp e1 CilType.Binop.pretty binop d_exp e2 | None, _ -> - GU.may_invalid_deref := true; + AS.svcomp_may_invalid_deref := true; M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Pointer (%a) size in expression %a %a %a not known. Memory out-of-bounds access might occur" d_exp e1 d_exp e1 CilType.Binop.pretty binop d_exp e2 | _, None -> - GU.may_invalid_deref := true; + AS.svcomp_may_invalid_deref := true; M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Operand value for pointer arithmetic in expression %a %a %a not known. Memory out-of-bounds access might occur" d_exp e1 CilType.Binop.pretty binop d_exp e2 end | _ -> () From b1b710ef597878488789a726c409c99107885b38 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 14 Aug 2023 10:57:10 +0300 Subject: [PATCH 0422/1312] =?UTF-8?q?Add=20local=20widen/narrow=20example?= =?UTF-8?q?=20from=20A=C2=B2I=20paper?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sarah Tilscher <66023521+stilscher@users.noreply.github.com> --- .../regression/34-localwn_restart/06-td-a2i.c | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 tests/regression/34-localwn_restart/06-td-a2i.c diff --git a/tests/regression/34-localwn_restart/06-td-a2i.c b/tests/regression/34-localwn_restart/06-td-a2i.c new file mode 100644 index 0000000000..e1fe6b05d0 --- /dev/null +++ b/tests/regression/34-localwn_restart/06-td-a2i.c @@ -0,0 +1,22 @@ +// PARAM: --enable ana.int.interval --set solver td3 --enable solvers.td3.remove-wpoint +// Example from "The Top-Down Solver — An Exercise in A²I", Section 6. +#include + +int main() { + int i, j, x; + i = 0; + while (i < 42) { + j = 0; + while (j < 17) { + x = i + j; + j++; + } + __goblint_check(j == 17); + __goblint_check(i >= 0); + __goblint_check(i <= 41); + i++; + } + __goblint_check(i == 42); + __goblint_check(j == 17); // TODO + return 0; +} From e11c982aac5925edc348389305ce1a48efb73aa0 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Mon, 14 Aug 2023 13:53:11 +0300 Subject: [PATCH 0423/1312] Make sem.unknown_function.call option --- src/analyses/libraryFunctions.ml | 3 ++- src/util/options.schema.json | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 6495341c7a..7c31d69fb8 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -1165,7 +1165,8 @@ let unknown_desc ~f name = (* TODO: remove name argument, unknown function shoul | Read when GobConfig.get_bool "sem.unknown_function.read.args" -> args | Read -> [] | Free -> [] - | Call -> [] (* TODO: option *) + | Call when get_bool "sem.unknown_function.call.args" -> args + | Call -> [] | Spawn when get_bool "sem.unknown_function.spawn" -> args | Spawn -> [] in diff --git a/src/util/options.schema.json b/src/util/options.schema.json index efa1dfabb8..0e89ede0b5 100644 --- a/src/util/options.schema.json +++ b/src/util/options.schema.json @@ -1317,6 +1317,13 @@ "type": "boolean", "default": true }, + "call": { + "title": "sem.unknown_function.call", + "description": + "Unknown function call calls reachable functions", + "type": "boolean", + "default": true + }, "invalidate": { "title": "sem.unknown_function.invalidate", "type": "object", From f150fdca489dfe76e1510c6a78ececddc0a0c60e Mon Sep 17 00:00:00 2001 From: karoliineh Date: Mon, 14 Aug 2023 14:37:09 +0300 Subject: [PATCH 0424/1312] Fix rand in libraryFunctions --- src/analyses/libraryFunctions.ml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 7c31d69fb8..5dc311a587 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -84,7 +84,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("getchar", unknown []); ("putchar", unknown [drop "ch" []]); ("puts", unknown [drop "s" [r]]); - ("rand", unknown ~attrs:[ThreadUnsafe] []); + ("rand", special ~attrs:[ThreadUnsafe] [] Rand); ("strerror", unknown ~attrs:[ThreadUnsafe] [drop "errnum" []]); ("strspn", unknown [drop "s" [r]; drop "accept" [r]]); ("strcspn", unknown [drop "s" [r]; drop "accept" [r]]); @@ -118,7 +118,6 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("_setjmp", special [__ "env" [w]] @@ fun env -> Setjmp { env }); (* only has one underscore *) ("setjmp", special [__ "env" [w]] @@ fun env -> Setjmp { env }); ("longjmp", special [__ "env" [r]; __ "value" []] @@ fun env value -> Longjmp { env; value }); - ("rand", special [] Rand); ] (** C POSIX library functions. From 379a7ff53d0f330e8fe92042c6cec9b37b369ccc Mon Sep 17 00:00:00 2001 From: karoliineh Date: Mon, 14 Aug 2023 14:37:36 +0300 Subject: [PATCH 0425/1312] Fix side_access call --- src/analyses/raceAnalysis.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index c09396bdd3..970895e971 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -105,7 +105,7 @@ struct let g: V.t = Obj.obj g in begin match g with | `Left g' -> (* accesses *) - (* ignore (Pretty.printf "WarnGlobal %a\n" CilType.Varinfo.pretty g); *) + (* ignore (Pretty.printf "WarnGlobal %a\n" Access.MemoRoot.pretty g'); *) let trie = G.access (ctx.global g) in (** Distribute access to contained fields. *) let rec distribute_inner offset (accs, children) ancestor_accs = @@ -202,7 +202,7 @@ struct let loc = Option.get !Node.current_node in let vo = Some f in let a = Obj.obj (ctx.ask (PartAccess (Memory {exp=e; var_opt=vo; kind=Call}))) in - side_access ctx (conf, Call, loc, e, a) ((`Type f.vtype), `NoOffset) ; + side_access ctx (conf, Call, loc, e, a) ((`Var f), `NoOffset) ; ); ctx.local From 8465e0b2eb3a72230ec3c327faa47fc969aad211 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Mon, 14 Aug 2023 14:38:05 +0300 Subject: [PATCH 0426/1312] Rename tests to have unique numbers --- .../{77-thread-unsafe_fun_rc.c => 90-thread-unsafe_fun_rc.c} | 0 .../{78-thread-unsafe_fun_nr.c => 91-thread-unsafe_fun_nr.c} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename tests/regression/04-mutex/{77-thread-unsafe_fun_rc.c => 90-thread-unsafe_fun_rc.c} (100%) rename tests/regression/04-mutex/{78-thread-unsafe_fun_nr.c => 91-thread-unsafe_fun_nr.c} (100%) diff --git a/tests/regression/04-mutex/77-thread-unsafe_fun_rc.c b/tests/regression/04-mutex/90-thread-unsafe_fun_rc.c similarity index 100% rename from tests/regression/04-mutex/77-thread-unsafe_fun_rc.c rename to tests/regression/04-mutex/90-thread-unsafe_fun_rc.c diff --git a/tests/regression/04-mutex/78-thread-unsafe_fun_nr.c b/tests/regression/04-mutex/91-thread-unsafe_fun_nr.c similarity index 100% rename from tests/regression/04-mutex/78-thread-unsafe_fun_nr.c rename to tests/regression/04-mutex/91-thread-unsafe_fun_nr.c From 59feaf8beebfb16ec3e69bebe300ce781f4631bb Mon Sep 17 00:00:00 2001 From: karoliineh Date: Mon, 14 Aug 2023 18:05:18 +0300 Subject: [PATCH 0427/1312] Create warn_accs record and refactor Co-authored-by: Simmo Saan --- src/analyses/raceAnalysis.ml | 2 +- src/domains/access.ml | 83 +++++++++++++++++++----------------- 2 files changed, 45 insertions(+), 40 deletions(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index c004800db6..bc0709541d 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -155,7 +155,7 @@ struct if not (Access.AS.is_empty accs) || (not (Access.AS.is_empty ancestor_accs) && not (Access.AS.is_empty outer_accs)) then ( let memo = (g', offset) in let mem_loc_str = GobPretty.sprint Access.Memo.pretty memo in - Timing.wrap ~args:[("memory location", `String mem_loc_str)] "race" (Access.warn_global ~safe ~vulnerable ~unsafe ~ancestor_accs ~ancestor_outer_accs ~outer_accs memo) accs + Timing.wrap ~args:[("memory location", `String mem_loc_str)] "race" (Access.warn_global ~safe ~vulnerable ~unsafe {prefix=ancestor_accs; type_suffix_prefix=ancestor_outer_accs; type_suffix=outer_accs; node=accs}) memo ); let ancestor_outer_accs' = Access.AS.union ancestor_outer_accs outer_accs in let ancestor_accs' = Access.AS.union ancestor_accs accs in diff --git a/src/domains/access.ml b/src/domains/access.ml index b31d69baf6..918573835f 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -378,11 +378,18 @@ let may_race (conf,(kind: AccessKind.t),loc,e,a) (conf2,(kind2: AccessKind.t),lo else true -let group_may_race ~ancestor_accs ~ancestor_outer_accs ~outer_accs accs = - if M.tracing then M.tracei "access" "group_may_race\n\tancestors_accs: %a\n\touter_accs: %a\n" AS.pretty ancestor_accs AS.pretty outer_accs; +type warn_accs = { + node: AS.t; + prefix: AS.t; + type_suffix: AS.t; + type_suffix_prefix: AS.t; +} + +let group_may_race warn_accs = + if M.tracing then M.tracei "access" "group_may_race\n\tprefix: %a\n\ttype_suffix: %a\n" AS.pretty warn_accs.prefix AS.pretty warn_accs.type_suffix; (* BFS to traverse one component with may_race edges *) - let rec bfs' ~ancestor_accs ~ancestor_outer_accs ~outer_accs ~accs ~todo ~visited = - let may_race_accs ~accs ~todo = + let rec bfs' {prefix; type_suffix_prefix; type_suffix; node} ~todo ~visited = + let may_race_accs ~accs ~todo = (* TODO: rename to from-to *) AS.fold (fun acc todo' -> AS.fold (fun acc' todo' -> if may_race acc acc' then @@ -392,56 +399,54 @@ let group_may_race ~ancestor_accs ~ancestor_outer_accs ~outer_accs accs = ) accs todo' ) todo (AS.empty ()) in - let accs' = AS.diff accs todo in - let ancestor_accs' = AS.diff ancestor_accs todo in - let ancestor_outer_accs' = AS.diff ancestor_outer_accs todo in - let outer_accs' = AS.diff outer_accs todo in - let todo_accs = may_race_accs ~accs:accs' ~todo in - let accs_todo = AS.inter todo accs in - let todo_ancestor_accs = may_race_accs ~accs:ancestor_accs' ~todo:accs_todo in - let todo_ancestor_outer_accs = may_race_accs ~accs:ancestor_outer_accs' ~todo:accs_todo in - let todo_outer_accs = may_race_accs ~accs:outer_accs' ~todo:accs_todo in - let todo_ancestor_accs_cross = may_race_accs ~accs:ancestor_accs' ~todo:(AS.inter todo outer_accs) in - let todo_outer_accs_cross = may_race_accs ~accs:outer_accs' ~todo:(AS.inter todo ancestor_accs) in - let todos = [todo_accs; todo_ancestor_accs; todo_ancestor_outer_accs; todo_outer_accs; todo_ancestor_accs_cross; todo_outer_accs_cross] in - let todo' = List.reduce AS.union todos in - let visited' = AS.union visited todo in + let node' = AS.diff node todo in + let prefix' = AS.diff prefix todo in + let type_suffix' = AS.diff type_suffix todo in + let type_suffix_prefix' = AS.diff type_suffix_prefix todo in + let todo_node = AS.inter todo node in + let todo_node' = may_race_accs ~accs:node' ~todo in + let todo_prefix' = may_race_accs ~accs:prefix' ~todo:(AS.union todo_node (AS.inter todo type_suffix)) in + let todo_type_suffix' = may_race_accs ~accs:type_suffix' ~todo:(AS.union todo_node (AS.inter todo prefix)) in + let todo_type_suffix_prefix' = may_race_accs ~accs:type_suffix_prefix' ~todo:todo_node in + let todo' = List.reduce AS.union [todo_node'; todo_prefix'; todo_type_suffix_prefix'; todo_type_suffix'] in + let visited' = AS.union visited todo in (* TODO: use warn_accs record for todo *) + let warn_accs' = {prefix=prefix'; type_suffix_prefix=type_suffix_prefix'; type_suffix=type_suffix'; node=node'} in if AS.is_empty todo' then - (accs', ancestor_accs', ancestor_outer_accs', outer_accs', visited') + (warn_accs', visited') else - (bfs' [@tailcall]) ~ancestor_accs:ancestor_accs' ~ancestor_outer_accs:ancestor_outer_accs' ~outer_accs:outer_accs' ~accs:accs' ~todo:todo' ~visited:visited' + (bfs' [@tailcall]) warn_accs' ~todo:todo' ~visited:visited' in - let bfs ~ancestor_accs ~ancestor_outer_accs ~outer_accs ~accs acc = bfs' ~ancestor_accs ~ancestor_outer_accs ~outer_accs ~accs ~todo:(AS.singleton acc) ~visited:(AS.empty ()) in + let bfs warn_accs acc = bfs' warn_accs ~todo:(AS.singleton acc) ~visited:(AS.empty ()) in (* repeat BFS to find all components *) - let rec components comps ~ancestor_accs ~ancestor_outer_accs ~outer_accs ~accs = - if AS.is_empty accs then - (comps, ancestor_accs, outer_accs) + let rec components comps warn_accs = + if AS.is_empty warn_accs.node then + (comps, warn_accs) else ( - let acc = AS.choose accs in - let (accs', ancestor_accs', ancestor_outer_accs', outer_accs', comp) = bfs ~ancestor_accs ~ancestor_outer_accs ~outer_accs ~accs acc in + let acc = AS.choose warn_accs.node in + let (warn_accs', comp) = bfs warn_accs acc in let comps' = comp :: comps in - components comps' ~ancestor_accs:ancestor_accs' ~ancestor_outer_accs:ancestor_outer_accs' ~outer_accs:outer_accs' ~accs:accs' + components comps' warn_accs' ) in - let (comps, ancestor_accs, outer_accs) = components [] ~ancestor_accs ~ancestor_outer_accs ~outer_accs ~accs in - if M.tracing then M.trace "access" "components\n\tancestors_accs: %a\n\touter_accs: %a\n" AS.pretty ancestor_accs AS.pretty outer_accs; - let rec components_cross comps ~ancestor_accs ~outer_accs = - if AS.is_empty ancestor_accs then + let (comps, warn_accs) = components [] warn_accs in + if M.tracing then M.trace "access" "components\n\tprefix: %a\n\ttype_suffix: %a\n" AS.pretty warn_accs.prefix AS.pretty warn_accs.type_suffix; + let rec components_cross comps ~prefix ~type_suffix = + if AS.is_empty prefix then comps else ( - let ancestor_acc = AS.choose ancestor_accs in - let (_, ancestor_accs', _, outer_accs', comp) = bfs ~ancestor_accs ~ancestor_outer_accs:(AS.empty ()) ~outer_accs ~accs:(AS.empty ()) ancestor_acc in - if M.tracing then M.trace "access" "components_cross\n\tancestors_accs: %a\n\touter_accs: %a\n" AS.pretty ancestor_accs' AS.pretty outer_accs'; + let prefix_acc = AS.choose prefix in + let (warn_accs', comp) = bfs {prefix; type_suffix_prefix=(AS.empty ()); type_suffix; node=(AS.empty ())} prefix_acc in + if M.tracing then M.trace "access" "components_cross\n\tprefix: %a\n\ttype_suffix: %a\n" AS.pretty warn_accs'.prefix AS.pretty warn_accs'.type_suffix; let comps' = if AS.cardinal comp > 1 then comp :: comps else - comps (* ignore self-race ancestor_acc component, self-race checked at ancestor's level *) + comps (* ignore self-race prefix_acc component, self-race checked at prefix's level *) in - components_cross comps' ~ancestor_accs:ancestor_accs' ~outer_accs:outer_accs' + components_cross comps' ~prefix:warn_accs'.prefix ~type_suffix:warn_accs'.type_suffix ) in - let components_cross = components_cross comps ~ancestor_accs ~outer_accs in + let components_cross = components_cross comps ~prefix:warn_accs.prefix ~type_suffix:warn_accs.type_suffix in if M.tracing then M.traceu "access" "group_may_race\n"; components_cross @@ -507,7 +512,7 @@ let print_accesses memo grouped_accs = M.msg_group Success ~category:Race "Memory location %a (safe)" Memo.pretty memo (msgs safe_accs) ) -let warn_global ~safe ~vulnerable ~unsafe ~ancestor_accs ~ancestor_outer_accs ~outer_accs memo accs = - let grouped_accs = group_may_race ~ancestor_accs ~ancestor_outer_accs ~outer_accs accs in (* do expensive component finding only once *) +let warn_global ~safe ~vulnerable ~unsafe warn_accs memo = + let grouped_accs = group_may_race warn_accs in (* do expensive component finding only once *) incr_summary ~safe ~vulnerable ~unsafe memo grouped_accs; print_accesses memo grouped_accs From 49e529f627e9fc1ae8fe9f4fbd6b31a065fc6cbf Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 15 Aug 2023 10:55:09 +0300 Subject: [PATCH 0428/1312] Refactor distribute_inner in raceAnalysis --- src/analyses/raceAnalysis.ml | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index bc0709541d..2cf97afffd 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -111,7 +111,7 @@ struct ctx.sideg (V.access memoroot) (G.create_access (OffsetTrie.singleton offset (`Lifted (Access.AS.empty ())))); side_vars ctx memo - let outer_memo ((root, offset) : Access.Memo.t) : Access.Memo.t option = + let type_suffix_memo ((root, offset) : Access.Memo.t) : Access.Memo.t option = match root, offset with | `Var v, _ -> Some (`Type v.vtype, offset) (* TODO: Alloc variables void type *) | _, `NoOffset -> None @@ -125,12 +125,12 @@ struct | `Lifted accs -> accs | `Bot -> Access.AS.empty () in - let outer_accs = - match outer_memo (root, offset) with - | Some outer_memo -> distribute_outer ctx outer_memo + let type_suffix = + match type_suffix_memo (root, offset) with + | Some type_suffix_memo -> distribute_outer ctx type_suffix_memo | None -> Access.AS.empty () in - Access.AS.union accs outer_accs + Access.AS.union accs type_suffix let query ctx (type a) (q: a Queries.t): a Queries.result = match q with @@ -141,29 +141,29 @@ struct (* ignore (Pretty.printf "WarnGlobal %a\n" CilType.Varinfo.pretty g); *) let trie = G.access (ctx.global g) in (** Distribute access to contained fields. *) - let rec distribute_inner offset (accs, children) ~ancestor_accs ~ancestor_outer_accs = + let rec distribute_inner offset (accs, children) ~prefix ~type_suffix_prefix = let accs = match accs with | `Lifted accs -> accs | `Bot -> Access.AS.empty () in - let outer_accs = - match outer_memo (g', offset) with - | Some outer_memo -> distribute_outer ctx outer_memo + let type_suffix = + match type_suffix_memo (g', offset) with + | Some type_suffix_memo -> distribute_outer ctx type_suffix_memo | None -> Access.AS.empty () in - if not (Access.AS.is_empty accs) || (not (Access.AS.is_empty ancestor_accs) && not (Access.AS.is_empty outer_accs)) then ( + if not (Access.AS.is_empty accs) || (not (Access.AS.is_empty prefix) && not (Access.AS.is_empty type_suffix)) then ( let memo = (g', offset) in let mem_loc_str = GobPretty.sprint Access.Memo.pretty memo in - Timing.wrap ~args:[("memory location", `String mem_loc_str)] "race" (Access.warn_global ~safe ~vulnerable ~unsafe {prefix=ancestor_accs; type_suffix_prefix=ancestor_outer_accs; type_suffix=outer_accs; node=accs}) memo + Timing.wrap ~args:[("memory location", `String mem_loc_str)] "race" (Access.warn_global ~safe ~vulnerable ~unsafe {prefix; type_suffix_prefix; type_suffix; node=accs}) memo ); - let ancestor_outer_accs' = Access.AS.union ancestor_outer_accs outer_accs in - let ancestor_accs' = Access.AS.union ancestor_accs accs in + let prefix' = Access.AS.union prefix accs in + let type_suffix_prefix' = Access.AS.union type_suffix_prefix type_suffix in OffsetTrie.ChildMap.iter (fun child_key child_trie -> - distribute_inner (Offset.Unit.add_offset offset (OneOffset.to_offset child_key)) child_trie ~ancestor_accs:ancestor_accs' ~ancestor_outer_accs:ancestor_outer_accs' + distribute_inner (Offset.Unit.add_offset offset (OneOffset.to_offset child_key)) child_trie ~prefix:prefix' ~type_suffix_prefix:type_suffix_prefix' ) children; in - distribute_inner `NoOffset trie ~ancestor_accs:(Access.AS.empty ()) ~ancestor_outer_accs:(Access.AS.empty ()) + distribute_inner `NoOffset trie ~prefix:(Access.AS.empty ()) ~type_suffix_prefix:(Access.AS.empty ()) | `Right _ -> (* vars *) () end From 64c761bee01954873ce85148d9cb1b818f0d238f Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 15 Aug 2023 11:29:24 +0300 Subject: [PATCH 0429/1312] Use warn_accs for todo Co-authored-by: Simmo Saan --- src/domains/access.ml | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/domains/access.ml b/src/domains/access.ml index 918573835f..85626c4463 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -399,31 +399,31 @@ let group_may_race warn_accs = ) accs todo' ) todo (AS.empty ()) in - let node' = AS.diff node todo in - let prefix' = AS.diff prefix todo in - let type_suffix' = AS.diff type_suffix todo in - let type_suffix_prefix' = AS.diff type_suffix_prefix todo in - let todo_node = AS.inter todo node in - let todo_node' = may_race_accs ~accs:node' ~todo in - let todo_prefix' = may_race_accs ~accs:prefix' ~todo:(AS.union todo_node (AS.inter todo type_suffix)) in - let todo_type_suffix' = may_race_accs ~accs:type_suffix' ~todo:(AS.union todo_node (AS.inter todo prefix)) in - let todo_type_suffix_prefix' = may_race_accs ~accs:type_suffix_prefix' ~todo:todo_node in - let todo' = List.reduce AS.union [todo_node'; todo_prefix'; todo_type_suffix_prefix'; todo_type_suffix'] in - let visited' = AS.union visited todo in (* TODO: use warn_accs record for todo *) + let node' = AS.diff node todo.node in + let prefix' = AS.diff prefix todo.prefix in + let type_suffix' = AS.diff type_suffix todo.type_suffix in + let type_suffix_prefix' = AS.diff type_suffix_prefix todo.type_suffix_prefix in + let todo_all = AS.union todo.node (AS.union (AS.union todo.prefix todo.type_suffix) todo.type_suffix_prefix) in + let todo_node' = may_race_accs ~accs:node' ~todo:todo_all in + let todo_prefix' = may_race_accs ~accs:prefix' ~todo:(AS.union todo.node todo.type_suffix) in + let todo_type_suffix' = may_race_accs ~accs:type_suffix' ~todo:(AS.union todo.node todo.prefix) in + let todo_type_suffix_prefix' = may_race_accs ~accs:type_suffix_prefix' ~todo:todo.node in + let todo' = {prefix=todo_prefix'; type_suffix=todo_type_suffix'; type_suffix_prefix=todo_type_suffix_prefix'; node=todo_node'} in + let visited' = AS.union visited todo_all in let warn_accs' = {prefix=prefix'; type_suffix_prefix=type_suffix_prefix'; type_suffix=type_suffix'; node=node'} in - if AS.is_empty todo' then + if AS.is_empty todo'.node && AS.is_empty todo'.prefix && AS.is_empty todo'.type_suffix && AS.is_empty todo'.type_suffix_prefix then (warn_accs', visited') else (bfs' [@tailcall]) warn_accs' ~todo:todo' ~visited:visited' in - let bfs warn_accs acc = bfs' warn_accs ~todo:(AS.singleton acc) ~visited:(AS.empty ()) in + let bfs warn_accs todo = bfs' warn_accs ~todo ~visited:(AS.empty ()) in (* repeat BFS to find all components *) let rec components comps warn_accs = if AS.is_empty warn_accs.node then (comps, warn_accs) else ( let acc = AS.choose warn_accs.node in - let (warn_accs', comp) = bfs warn_accs acc in + let (warn_accs', comp) = bfs warn_accs {node=AS.singleton acc; prefix=AS.empty (); type_suffix=AS.empty (); type_suffix_prefix=AS.empty ()} in let comps' = comp :: comps in components comps' warn_accs' ) @@ -435,7 +435,7 @@ let group_may_race warn_accs = comps else ( let prefix_acc = AS.choose prefix in - let (warn_accs', comp) = bfs {prefix; type_suffix_prefix=(AS.empty ()); type_suffix; node=(AS.empty ())} prefix_acc in + let (warn_accs', comp) = bfs {prefix; type_suffix_prefix=(AS.empty ()); type_suffix; node=(AS.empty ())} {node=AS.empty (); prefix=AS.singleton prefix_acc; type_suffix=AS.empty (); type_suffix_prefix=AS.empty ()} in if M.tracing then M.trace "access" "components_cross\n\tprefix: %a\n\ttype_suffix: %a\n" AS.pretty warn_accs'.prefix AS.pretty warn_accs'.type_suffix; let comps' = if AS.cardinal comp > 1 then From b06b3e342981f8dfda512b58c1e1049053332321 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 15 Aug 2023 11:48:23 +0300 Subject: [PATCH 0430/1312] Make warn_accs record to a module Co-authored-by: Simmo Saan --- src/domains/access.ml | 77 ++++++++++++++++++++++++++++--------------- 1 file changed, 50 insertions(+), 27 deletions(-) diff --git a/src/domains/access.ml b/src/domains/access.ml index 85626c4463..4c752096ac 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -378,17 +378,42 @@ let may_race (conf,(kind: AccessKind.t),loc,e,a) (conf2,(kind2: AccessKind.t),lo else true -type warn_accs = { - node: AS.t; - prefix: AS.t; - type_suffix: AS.t; - type_suffix_prefix: AS.t; -} - -let group_may_race warn_accs = - if M.tracing then M.tracei "access" "group_may_race\n\tprefix: %a\n\ttype_suffix: %a\n" AS.pretty warn_accs.prefix AS.pretty warn_accs.type_suffix; +module WarnAccs = +struct + type t = { + node: AS.t; + prefix: AS.t; + type_suffix: AS.t; + type_suffix_prefix: AS.t; + } + + let diff w1 w2 = { + node = AS.diff w1.node w2.node; + prefix = AS.diff w1.prefix w2.prefix; + type_suffix = AS.diff w1.type_suffix w2.type_suffix; + type_suffix_prefix = AS.diff w1.type_suffix_prefix w2.type_suffix_prefix; + } + + let union_all w = + AS.union + (AS.union w.node w.prefix) + (AS.union w.type_suffix w.type_suffix_prefix) + + let is_empty w = + AS.is_empty w.node && AS.is_empty w.prefix && AS.is_empty w.type_suffix && AS.is_empty w.type_suffix_prefix + + let empty () = + {node=AS.empty (); prefix=AS.empty (); type_suffix=AS.empty (); type_suffix_prefix=AS.empty ()} + + let pretty () w = + Pretty.dprintf "{node = %a; prefix = %a; type_suffix = %a; type_suffix_prefix = %a}" + AS.pretty w.node AS.pretty w.prefix AS.pretty w.type_suffix AS.pretty w.type_suffix_prefix +end + +let group_may_race (warn_accs:WarnAccs.t) = + if M.tracing then M.tracei "access" "group_may_race %a\n" WarnAccs.pretty warn_accs; (* BFS to traverse one component with may_race edges *) - let rec bfs' {prefix; type_suffix_prefix; type_suffix; node} ~todo ~visited = + let rec bfs' warn_accs ~todo ~visited = let may_race_accs ~accs ~todo = (* TODO: rename to from-to *) AS.fold (fun acc todo' -> AS.fold (fun acc' todo' -> @@ -399,44 +424,42 @@ let group_may_race warn_accs = ) accs todo' ) todo (AS.empty ()) in - let node' = AS.diff node todo.node in - let prefix' = AS.diff prefix todo.prefix in - let type_suffix' = AS.diff type_suffix todo.type_suffix in - let type_suffix_prefix' = AS.diff type_suffix_prefix todo.type_suffix_prefix in - let todo_all = AS.union todo.node (AS.union (AS.union todo.prefix todo.type_suffix) todo.type_suffix_prefix) in - let todo_node' = may_race_accs ~accs:node' ~todo:todo_all in - let todo_prefix' = may_race_accs ~accs:prefix' ~todo:(AS.union todo.node todo.type_suffix) in - let todo_type_suffix' = may_race_accs ~accs:type_suffix' ~todo:(AS.union todo.node todo.prefix) in - let todo_type_suffix_prefix' = may_race_accs ~accs:type_suffix_prefix' ~todo:todo.node in - let todo' = {prefix=todo_prefix'; type_suffix=todo_type_suffix'; type_suffix_prefix=todo_type_suffix_prefix'; node=todo_node'} in + let warn_accs' = WarnAccs.diff warn_accs todo in + let todo_all = WarnAccs.union_all todo in let visited' = AS.union visited todo_all in - let warn_accs' = {prefix=prefix'; type_suffix_prefix=type_suffix_prefix'; type_suffix=type_suffix'; node=node'} in - if AS.is_empty todo'.node && AS.is_empty todo'.prefix && AS.is_empty todo'.type_suffix && AS.is_empty todo'.type_suffix_prefix then + let todo' : WarnAccs.t = { + node = may_race_accs ~accs:warn_accs'.node ~todo:todo_all; + prefix = may_race_accs ~accs:warn_accs'.prefix ~todo:(AS.union todo.node todo.type_suffix); + type_suffix = may_race_accs ~accs:warn_accs'.type_suffix ~todo:(AS.union todo.node todo.prefix); + type_suffix_prefix = may_race_accs ~accs:warn_accs'.type_suffix_prefix ~todo:todo.node + } + in + if WarnAccs.is_empty todo' then (warn_accs', visited') else (bfs' [@tailcall]) warn_accs' ~todo:todo' ~visited:visited' in let bfs warn_accs todo = bfs' warn_accs ~todo ~visited:(AS.empty ()) in (* repeat BFS to find all components *) - let rec components comps warn_accs = + let rec components comps (warn_accs:WarnAccs.t) = if AS.is_empty warn_accs.node then (comps, warn_accs) else ( let acc = AS.choose warn_accs.node in - let (warn_accs', comp) = bfs warn_accs {node=AS.singleton acc; prefix=AS.empty (); type_suffix=AS.empty (); type_suffix_prefix=AS.empty ()} in + let (warn_accs', comp) = bfs warn_accs {(WarnAccs.empty ()) with node=AS.singleton acc} in let comps' = comp :: comps in components comps' warn_accs' ) in let (comps, warn_accs) = components [] warn_accs in - if M.tracing then M.trace "access" "components\n\tprefix: %a\n\ttype_suffix: %a\n" AS.pretty warn_accs.prefix AS.pretty warn_accs.type_suffix; + if M.tracing then M.trace "access" "components %a\n" WarnAccs.pretty warn_accs; let rec components_cross comps ~prefix ~type_suffix = if AS.is_empty prefix then comps else ( let prefix_acc = AS.choose prefix in - let (warn_accs', comp) = bfs {prefix; type_suffix_prefix=(AS.empty ()); type_suffix; node=(AS.empty ())} {node=AS.empty (); prefix=AS.singleton prefix_acc; type_suffix=AS.empty (); type_suffix_prefix=AS.empty ()} in - if M.tracing then M.trace "access" "components_cross\n\tprefix: %a\n\ttype_suffix: %a\n" AS.pretty warn_accs'.prefix AS.pretty warn_accs'.type_suffix; + let (warn_accs', comp) = bfs {(WarnAccs.empty ()) with prefix; type_suffix} {(WarnAccs.empty ()) with prefix=AS.singleton prefix_acc} in + if M.tracing then M.trace "access" "components_cross %a\n" WarnAccs.pretty warn_accs'; let comps' = if AS.cardinal comp > 1 then comp :: comps From 69ddfdffab00cf5c3699a9490d86d64800b990c3 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 15 Aug 2023 12:13:20 +0300 Subject: [PATCH 0431/1312] Refactor and document todo' computation Co-authored-by: Simmo Saan --- src/domains/access.ml | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/src/domains/access.ml b/src/domains/access.ml index 4c752096ac..6a802e5c14 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -414,7 +414,11 @@ let group_may_race (warn_accs:WarnAccs.t) = if M.tracing then M.tracei "access" "group_may_race %a\n" WarnAccs.pretty warn_accs; (* BFS to traverse one component with may_race edges *) let rec bfs' warn_accs ~todo ~visited = - let may_race_accs ~accs ~todo = (* TODO: rename to from-to *) + let warn_accs' = WarnAccs.diff warn_accs todo in + let todo_all = WarnAccs.union_all todo in + let visited' = AS.union visited todo_all in + + let step_may_race ~todo ~accs = (* step from todo to accs if may_race *) AS.fold (fun acc todo' -> AS.fold (fun acc' todo' -> if may_race acc acc' then @@ -424,16 +428,28 @@ let group_may_race (warn_accs:WarnAccs.t) = ) accs todo' ) todo (AS.empty ()) in - let warn_accs' = WarnAccs.diff warn_accs todo in - let todo_all = WarnAccs.union_all todo in - let visited' = AS.union visited todo_all in + (* Undirected graph of may_race checks: + + type_suffix_prefix + | + | + type_suffix --+-- prefix + \ | / + \ | / + node + / \ + \_/ + + Each undirected edge is handled by two opposite step_may_race-s. + All missing edges are checked at other nodes by other group_may_race calls. *) let todo' : WarnAccs.t = { - node = may_race_accs ~accs:warn_accs'.node ~todo:todo_all; - prefix = may_race_accs ~accs:warn_accs'.prefix ~todo:(AS.union todo.node todo.type_suffix); - type_suffix = may_race_accs ~accs:warn_accs'.type_suffix ~todo:(AS.union todo.node todo.prefix); - type_suffix_prefix = may_race_accs ~accs:warn_accs'.type_suffix_prefix ~todo:todo.node + node = step_may_race ~todo:todo_all ~accs:warn_accs'.node; + prefix = step_may_race ~todo:(AS.union todo.node todo.type_suffix) ~accs:warn_accs'.prefix; + type_suffix = step_may_race ~todo:(AS.union todo.node todo.prefix) ~accs:warn_accs'.type_suffix; + type_suffix_prefix = step_may_race ~todo:todo.node ~accs:warn_accs'.type_suffix_prefix } in + if WarnAccs.is_empty todo' then (warn_accs', visited') else From 857d1cbaa31673ced488acbf42b97f9e3c3413aa Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 15 Aug 2023 12:36:18 +0300 Subject: [PATCH 0432/1312] Use Typsig for Access MemoRoot Co-authored-by: Simmo Saan --- src/analyses/raceAnalysis.ml | 6 +++--- src/domains/access.ml | 29 +++++++++-------------------- 2 files changed, 12 insertions(+), 23 deletions(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index 2cf97afffd..72d73f9de1 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -113,9 +113,9 @@ struct let type_suffix_memo ((root, offset) : Access.Memo.t) : Access.Memo.t option = match root, offset with - | `Var v, _ -> Some (`Type v.vtype, offset) (* TODO: Alloc variables void type *) + | `Var v, _ -> Some (`Type (Cil.typeSig v.vtype), offset) (* TODO: Alloc variables void type *) | _, `NoOffset -> None - | _, `Field (f, offset') -> Some (`Type f.ftype, offset') + | _, `Field (f, offset') -> Some (`Type (Cil.typeSig f.ftype), offset') | _, `Index ((), offset') -> None (* TODO *) let rec distribute_outer ctx ((root, offset) : Access.Memo.t) : Access.AS.t = @@ -190,7 +190,7 @@ struct in let add_access_struct conf ci = let a = part_access None in - Access.add_one (side_access octx (conf, kind, loc, e, a)) (`Type (TComp (ci, [])), `NoOffset) + Access.add_one (side_access octx (conf, kind, loc, e, a)) (`Type (TSComp (ci.cstruct, ci.cname, [])), `NoOffset) in let has_escaped g = octx.ask (Queries.MayEscape g) in (* The following function adds accesses to the lval-set ls diff --git a/src/domains/access.ml b/src/domains/access.ml index 6a802e5c14..ce71fcc98c 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -83,8 +83,7 @@ type acc_typ = [ `Type of CilType.Typ.t | `Struct of CilType.Compinfo.t * Offset module MemoRoot = struct include Printable.StdLeaf - type t = [`Var of CilType.Varinfo.t | `Type of CilType.Typ.t] [@@deriving eq, ord, hash] - (* Can't use typsig for `Type because there's no function to follow offsets on typsig. *) + type t = [`Var of CilType.Varinfo.t | `Type of CilType.Typsig.t] [@@deriving eq, ord, hash] let name () = "memoroot" @@ -92,8 +91,8 @@ struct (* Imitate old printing for now *) match vt with | `Var v -> Pretty.dprintf "%a@@%a" CilType.Varinfo.pretty v CilType.Location.pretty v.vdecl - | `Type (TComp (c, _)) -> Pretty.dprintf "(struct %s)" c.cname - | `Type t -> Pretty.dprintf "(%a)" CilType.Typ.pretty t + | `Type (TSComp (_, name, _)) -> Pretty.dprintf "(struct %s)" name + | `Type t -> Pretty.dprintf "(%a)" CilType.Typsig.pretty t include Printable.SimplePretty ( struct @@ -108,7 +107,6 @@ module Memo = struct include Printable.StdLeaf type t = MemoRoot.t * Offset.Unit.t [@@deriving eq, ord, hash] - (* Can't use typsig for `Type because there's no function to follow offsets on typsig. *) let name () = "memo" @@ -116,8 +114,8 @@ struct (* Imitate old printing for now *) match vt with | `Var v -> Pretty.dprintf "%a%a@@%a" CilType.Varinfo.pretty v Offset.Unit.pretty o CilType.Location.pretty v.vdecl - | `Type (TComp (c, _)) -> Pretty.dprintf "(struct %s)%a" c.cname Offset.Unit.pretty o - | `Type t -> Pretty.dprintf "(%a)%a" CilType.Typ.pretty t Offset.Unit.pretty o + | `Type (TSComp (_, name, _)) -> Pretty.dprintf "(struct %s)%a" name Offset.Unit.pretty o + | `Type t -> Pretty.dprintf "(%a)%a" CilType.Typsig.pretty t Offset.Unit.pretty o include Printable.SimplePretty ( struct @@ -128,23 +126,14 @@ struct let of_ty (ty: acc_typ): t = match ty with - | `Struct (c, o) -> (`Type (TComp (c, [])), o) - | `Type t -> (`Type t, `NoOffset) + | `Struct (c, o) -> (`Type (TSComp (c.cstruct, c.cname, [])), o) + | `Type t -> (`Type (Cil.typeSig t), `NoOffset) let to_mval: t -> Mval.Unit.t option = function | (`Var v, o) -> Some (v, o) | (`Type _, _) -> None let add_offset ((vt, o): t) o2: t = (vt, Offset.Unit.add_offset o o2) - - let type_of_base ((vt, _): t): typ = - match vt with - | `Var v -> v.vtype - | `Type t -> t - - (** @raise Offset.Type_of_error *) - let type_of ((vt, o) as memo: t): typ = - Offset.Unit.type_of ~base:(type_of_base memo) o end (* TODO: What is the logic for get_type? *) @@ -213,12 +202,12 @@ let add_one side memo: unit = (** Distribute type-based access to variables and containing fields. *) let rec add_distribute_outer side side0 (t: typ) (o: Offset.Unit.t) = - let memo = (`Type t, o) in + let ts = typeSig t in + let memo = (`Type ts, o) in if M.tracing then M.tracei "access" "add_distribute_outer %a\n" Memo.pretty memo; add_one side memo; (* distribute to variables of the type *) - let ts = typeSig t in let vars = TSH.find_all typeVar ts in List.iter (fun v -> add_one side0 (`Var v, o) (* same offset, but on variable *) From b28cbba1abb527b6d1768bbd1bd584348d49d2e3 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 15 Aug 2023 13:32:38 +0300 Subject: [PATCH 0433/1312] Fix index in type_suffix_memo Co-authored-by: Simmo Saan --- src/analyses/raceAnalysis.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index 72d73f9de1..f8705613c8 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -116,7 +116,8 @@ struct | `Var v, _ -> Some (`Type (Cil.typeSig v.vtype), offset) (* TODO: Alloc variables void type *) | _, `NoOffset -> None | _, `Field (f, offset') -> Some (`Type (Cil.typeSig f.ftype), offset') - | _, `Index ((), offset') -> None (* TODO *) + | `Type (TSArray (ts, _, _)), `Index ((), offset') -> Some (`Type ts, offset') + | _, `Index ((), offset') -> None (* TODO: why indexing on non-array? *) let rec distribute_outer ctx ((root, offset) : Access.Memo.t) : Access.AS.t = let trie = G.access (ctx.global (V.access root)) in From d6c5bbac05531ec6f52b2f45d419f841a61793cb Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 15 Aug 2023 13:58:54 +0300 Subject: [PATCH 0434/1312] Make new cram tests deterministic Co-authored-by: Simmo Saan --- .../04-mutex/77-type-nested-fields.t | 38 ++++++++--------- .../04-mutex/79-type-nested-fields-deep1.t | 38 ++++++++--------- .../04-mutex/80-type-nested-fields-deep2.t | 38 ++++++++--------- .../04-mutex/90-distribute-fields-type-1.t | 42 +++++++++---------- .../04-mutex/91-distribute-fields-type-2.t | 42 +++++++++---------- .../04-mutex/92-distribute-fields-type-deep.t | 42 +++++++++---------- .../93-distribute-fields-type-global.t | 32 +++++++------- 7 files changed, 136 insertions(+), 136 deletions(-) diff --git a/tests/regression/04-mutex/77-type-nested-fields.t b/tests/regression/04-mutex/77-type-nested-fields.t index 2cbd339dfa..dc87d0a85e 100644 --- a/tests/regression/04-mutex/77-type-nested-fields.t +++ b/tests/regression/04-mutex/77-type-nested-fields.t @@ -1,29 +1,29 @@ - $ goblint --enable allglobs 77-type-nested-fields.c - [Error][Imprecise][Unsound] Function definition missing for getS (77-type-nested-fields.c:32:3-32:20) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (77-type-nested-fields.c:32:3-32:20) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (77-type-nested-fields.c:32:3-32:20) - [Info][Unsound] Unknown address in {&tmp} has escaped. (77-type-nested-fields.c:32:3-32:20) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (77-type-nested-fields.c:32:3-32:20) + $ goblint --enable warn.deterministic --enable allglobs 77-type-nested-fields.c [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (77-type-nested-fields.c:32:3-32:20) - [Info][Unsound] Write to unknown address: privatization is unsound. (77-type-nested-fields.c:32:3-32:20) - [Error][Imprecise][Unsound] Function definition missing for getT (77-type-nested-fields.c:39:3-39:22) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (77-type-nested-fields.c:39:3-39:22) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (77-type-nested-fields.c:39:3-39:22) - [Info][Unsound] Unknown address in {&tmp} has escaped. (77-type-nested-fields.c:39:3-39:22) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (77-type-nested-fields.c:39:3-39:22) [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (77-type-nested-fields.c:39:3-39:22) - [Info][Unsound] Write to unknown address: privatization is unsound. (77-type-nested-fields.c:39:3-39:22) - [Info][Deadcode] Logical lines of code (LLoC) summary: - live: 7 - dead: 0 - total lines: 7 [Warning][Race] Memory location (struct T).s.field (race with conf. 100): write with [mhp:{tid=[main, t_fun@77-type-nested-fields.c:38:3-38:40#top]}, thread:[main, t_fun@77-type-nested-fields.c:38:3-38:40#top]] (conf. 100) (exp: & tmp->field) (77-type-nested-fields.c:32:3-32:20) write with [mhp:{tid=[main]; created={[main, t_fun@77-type-nested-fields.c:38:3-38:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->s.field) (77-type-nested-fields.c:39:3-39:22) - [Success][Race] Memory location (struct S).field (safe): - write with [mhp:{tid=[main, t_fun@77-type-nested-fields.c:38:3-38:40#top]}, thread:[main, t_fun@77-type-nested-fields.c:38:3-38:40#top]] (conf. 100) (exp: & tmp->field) (77-type-nested-fields.c:32:3-32:20) [Info][Race] Memory locations race summary: safe: 1 vulnerable: 0 unsafe: 1 total memory locations: 2 + [Success][Race] Memory location (struct S).field (safe): + write with [mhp:{tid=[main, t_fun@77-type-nested-fields.c:38:3-38:40#top]}, thread:[main, t_fun@77-type-nested-fields.c:38:3-38:40#top]] (conf. 100) (exp: & tmp->field) (77-type-nested-fields.c:32:3-32:20) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 0 + total lines: 7 + [Info][Unsound] Unknown address in {&tmp} has escaped. (77-type-nested-fields.c:32:3-32:20) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (77-type-nested-fields.c:32:3-32:20) + [Info][Unsound] Write to unknown address: privatization is unsound. (77-type-nested-fields.c:32:3-32:20) + [Info][Unsound] Unknown address in {&tmp} has escaped. (77-type-nested-fields.c:39:3-39:22) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (77-type-nested-fields.c:39:3-39:22) + [Info][Unsound] Write to unknown address: privatization is unsound. (77-type-nested-fields.c:39:3-39:22) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (77-type-nested-fields.c:32:3-32:20) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (77-type-nested-fields.c:32:3-32:20) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (77-type-nested-fields.c:39:3-39:22) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (77-type-nested-fields.c:39:3-39:22) + [Error][Imprecise][Unsound] Function definition missing for getS (77-type-nested-fields.c:32:3-32:20) + [Error][Imprecise][Unsound] Function definition missing for getT (77-type-nested-fields.c:39:3-39:22) diff --git a/tests/regression/04-mutex/79-type-nested-fields-deep1.t b/tests/regression/04-mutex/79-type-nested-fields-deep1.t index 4075dab33b..5fc60223b9 100644 --- a/tests/regression/04-mutex/79-type-nested-fields-deep1.t +++ b/tests/regression/04-mutex/79-type-nested-fields-deep1.t @@ -1,24 +1,6 @@ - $ goblint --enable allglobs 79-type-nested-fields-deep1.c - [Error][Imprecise][Unsound] Function definition missing for getS (79-type-nested-fields-deep1.c:37:3-37:20) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (79-type-nested-fields-deep1.c:37:3-37:20) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (79-type-nested-fields-deep1.c:37:3-37:20) - [Info][Unsound] Unknown address in {&tmp} has escaped. (79-type-nested-fields-deep1.c:37:3-37:20) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (79-type-nested-fields-deep1.c:37:3-37:20) + $ goblint --enable warn.deterministic --enable allglobs 79-type-nested-fields-deep1.c [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (79-type-nested-fields-deep1.c:37:3-37:20) - [Info][Unsound] Write to unknown address: privatization is unsound. (79-type-nested-fields-deep1.c:37:3-37:20) - [Error][Imprecise][Unsound] Function definition missing for getU (79-type-nested-fields-deep1.c:44:3-44:24) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (79-type-nested-fields-deep1.c:44:3-44:24) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (79-type-nested-fields-deep1.c:44:3-44:24) - [Info][Unsound] Unknown address in {&tmp} has escaped. (79-type-nested-fields-deep1.c:44:3-44:24) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (79-type-nested-fields-deep1.c:44:3-44:24) [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (79-type-nested-fields-deep1.c:44:3-44:24) - [Info][Unsound] Write to unknown address: privatization is unsound. (79-type-nested-fields-deep1.c:44:3-44:24) - [Info][Deadcode] Logical lines of code (LLoC) summary: - live: 7 - dead: 0 - total lines: 7 - [Success][Race] Memory location (struct S).field (safe): - write with [mhp:{tid=[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]}, thread:[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (79-type-nested-fields-deep1.c:37:3-37:20) [Warning][Race] Memory location (struct U).t.s.field (race with conf. 100): write with [mhp:{tid=[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]}, thread:[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (79-type-nested-fields-deep1.c:37:3-37:20) write with [mhp:{tid=[main]; created={[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t.s.field) (79-type-nested-fields-deep1.c:44:3-44:24) @@ -27,3 +9,21 @@ vulnerable: 0 unsafe: 1 total memory locations: 2 + [Success][Race] Memory location (struct S).field (safe): + write with [mhp:{tid=[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]}, thread:[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (79-type-nested-fields-deep1.c:37:3-37:20) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 0 + total lines: 7 + [Info][Unsound] Unknown address in {&tmp} has escaped. (79-type-nested-fields-deep1.c:37:3-37:20) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (79-type-nested-fields-deep1.c:37:3-37:20) + [Info][Unsound] Write to unknown address: privatization is unsound. (79-type-nested-fields-deep1.c:37:3-37:20) + [Info][Unsound] Unknown address in {&tmp} has escaped. (79-type-nested-fields-deep1.c:44:3-44:24) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (79-type-nested-fields-deep1.c:44:3-44:24) + [Info][Unsound] Write to unknown address: privatization is unsound. (79-type-nested-fields-deep1.c:44:3-44:24) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (79-type-nested-fields-deep1.c:37:3-37:20) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (79-type-nested-fields-deep1.c:37:3-37:20) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (79-type-nested-fields-deep1.c:44:3-44:24) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (79-type-nested-fields-deep1.c:44:3-44:24) + [Error][Imprecise][Unsound] Function definition missing for getS (79-type-nested-fields-deep1.c:37:3-37:20) + [Error][Imprecise][Unsound] Function definition missing for getU (79-type-nested-fields-deep1.c:44:3-44:24) diff --git a/tests/regression/04-mutex/80-type-nested-fields-deep2.t b/tests/regression/04-mutex/80-type-nested-fields-deep2.t index f14a315de2..a0541235ee 100644 --- a/tests/regression/04-mutex/80-type-nested-fields-deep2.t +++ b/tests/regression/04-mutex/80-type-nested-fields-deep2.t @@ -1,24 +1,6 @@ - $ goblint --enable allglobs 80-type-nested-fields-deep2.c - [Error][Imprecise][Unsound] Function definition missing for getT (80-type-nested-fields-deep2.c:37:3-37:22) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (80-type-nested-fields-deep2.c:37:3-37:22) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (80-type-nested-fields-deep2.c:37:3-37:22) - [Info][Unsound] Unknown address in {&tmp} has escaped. (80-type-nested-fields-deep2.c:37:3-37:22) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (80-type-nested-fields-deep2.c:37:3-37:22) + $ goblint --enable warn.deterministic --enable allglobs 80-type-nested-fields-deep2.c [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (80-type-nested-fields-deep2.c:37:3-37:22) - [Info][Unsound] Write to unknown address: privatization is unsound. (80-type-nested-fields-deep2.c:37:3-37:22) - [Error][Imprecise][Unsound] Function definition missing for getU (80-type-nested-fields-deep2.c:44:3-44:24) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (80-type-nested-fields-deep2.c:44:3-44:24) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (80-type-nested-fields-deep2.c:44:3-44:24) - [Info][Unsound] Unknown address in {&tmp} has escaped. (80-type-nested-fields-deep2.c:44:3-44:24) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (80-type-nested-fields-deep2.c:44:3-44:24) [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (80-type-nested-fields-deep2.c:44:3-44:24) - [Info][Unsound] Write to unknown address: privatization is unsound. (80-type-nested-fields-deep2.c:44:3-44:24) - [Info][Deadcode] Logical lines of code (LLoC) summary: - live: 7 - dead: 0 - total lines: 7 - [Success][Race] Memory location (struct T).s.field (safe): - write with [mhp:{tid=[main, t_fun@80-type-nested-fields-deep2.c:43:3-43:40#top]}, thread:[main, t_fun@80-type-nested-fields-deep2.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->s.field) (80-type-nested-fields-deep2.c:37:3-37:22) [Warning][Race] Memory location (struct U).t.s.field (race with conf. 100): write with [mhp:{tid=[main, t_fun@80-type-nested-fields-deep2.c:43:3-43:40#top]}, thread:[main, t_fun@80-type-nested-fields-deep2.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->s.field) (80-type-nested-fields-deep2.c:37:3-37:22) write with [mhp:{tid=[main]; created={[main, t_fun@80-type-nested-fields-deep2.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t.s.field) (80-type-nested-fields-deep2.c:44:3-44:24) @@ -27,3 +9,21 @@ vulnerable: 0 unsafe: 1 total memory locations: 2 + [Success][Race] Memory location (struct T).s.field (safe): + write with [mhp:{tid=[main, t_fun@80-type-nested-fields-deep2.c:43:3-43:40#top]}, thread:[main, t_fun@80-type-nested-fields-deep2.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->s.field) (80-type-nested-fields-deep2.c:37:3-37:22) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 0 + total lines: 7 + [Info][Unsound] Unknown address in {&tmp} has escaped. (80-type-nested-fields-deep2.c:37:3-37:22) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (80-type-nested-fields-deep2.c:37:3-37:22) + [Info][Unsound] Write to unknown address: privatization is unsound. (80-type-nested-fields-deep2.c:37:3-37:22) + [Info][Unsound] Unknown address in {&tmp} has escaped. (80-type-nested-fields-deep2.c:44:3-44:24) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (80-type-nested-fields-deep2.c:44:3-44:24) + [Info][Unsound] Write to unknown address: privatization is unsound. (80-type-nested-fields-deep2.c:44:3-44:24) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (80-type-nested-fields-deep2.c:37:3-37:22) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (80-type-nested-fields-deep2.c:37:3-37:22) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (80-type-nested-fields-deep2.c:44:3-44:24) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (80-type-nested-fields-deep2.c:44:3-44:24) + [Error][Imprecise][Unsound] Function definition missing for getT (80-type-nested-fields-deep2.c:37:3-37:22) + [Error][Imprecise][Unsound] Function definition missing for getU (80-type-nested-fields-deep2.c:44:3-44:24) diff --git a/tests/regression/04-mutex/90-distribute-fields-type-1.t b/tests/regression/04-mutex/90-distribute-fields-type-1.t index 65689ff4d4..fb6af7ceca 100644 --- a/tests/regression/04-mutex/90-distribute-fields-type-1.t +++ b/tests/regression/04-mutex/90-distribute-fields-type-1.t @@ -1,31 +1,31 @@ - $ goblint --enable allglobs 90-distribute-fields-type-1.c - [Error][Imprecise][Unsound] Function definition missing for getS (90-distribute-fields-type-1.c:32:3-32:20) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (90-distribute-fields-type-1.c:32:3-32:20) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (90-distribute-fields-type-1.c:32:3-32:20) - [Info][Unsound] Unknown address in {&tmp} has escaped. (90-distribute-fields-type-1.c:32:3-32:20) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (90-distribute-fields-type-1.c:32:3-32:20) + $ goblint --enable warn.deterministic --enable allglobs 90-distribute-fields-type-1.c [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (90-distribute-fields-type-1.c:32:3-32:20) - [Info][Unsound] Write to unknown address: privatization is unsound. (90-distribute-fields-type-1.c:32:3-32:20) - [Error][Imprecise][Unsound] Function definition missing for getT (90-distribute-fields-type-1.c:40:3-40:17) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (90-distribute-fields-type-1.c:40:3-40:17) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (90-distribute-fields-type-1.c:40:3-40:17) - [Info][Unsound] Unknown address in {&tmp} has escaped. (90-distribute-fields-type-1.c:40:3-40:17) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (90-distribute-fields-type-1.c:40:3-40:17) [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (90-distribute-fields-type-1.c:40:3-40:17) - [Info][Unsound] Write to unknown address: privatization is unsound. (90-distribute-fields-type-1.c:40:3-40:17) - [Info][Deadcode] Logical lines of code (LLoC) summary: - live: 7 - dead: 0 - total lines: 7 - [Success][Race] Memory location (struct T).s (safe): - write with [mhp:{tid=[main]; created={[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->s) (90-distribute-fields-type-1.c:40:3-40:17) [Warning][Race] Memory location (struct T).s.field (race with conf. 100): write with [mhp:{tid=[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]}, thread:[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]] (conf. 100) (exp: & tmp->field) (90-distribute-fields-type-1.c:32:3-32:20) write with [mhp:{tid=[main]; created={[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->s) (90-distribute-fields-type-1.c:40:3-40:17) - [Success][Race] Memory location (struct S).field (safe): - write with [mhp:{tid=[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]}, thread:[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]] (conf. 100) (exp: & tmp->field) (90-distribute-fields-type-1.c:32:3-32:20) [Info][Race] Memory locations race summary: safe: 2 vulnerable: 0 unsafe: 1 total memory locations: 3 + [Success][Race] Memory location (struct S).field (safe): + write with [mhp:{tid=[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]}, thread:[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]] (conf. 100) (exp: & tmp->field) (90-distribute-fields-type-1.c:32:3-32:20) + [Success][Race] Memory location (struct T).s (safe): + write with [mhp:{tid=[main]; created={[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->s) (90-distribute-fields-type-1.c:40:3-40:17) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 0 + total lines: 7 + [Info][Unsound] Unknown address in {&tmp} has escaped. (90-distribute-fields-type-1.c:32:3-32:20) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (90-distribute-fields-type-1.c:32:3-32:20) + [Info][Unsound] Write to unknown address: privatization is unsound. (90-distribute-fields-type-1.c:32:3-32:20) + [Info][Unsound] Unknown address in {&tmp} has escaped. (90-distribute-fields-type-1.c:40:3-40:17) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (90-distribute-fields-type-1.c:40:3-40:17) + [Info][Unsound] Write to unknown address: privatization is unsound. (90-distribute-fields-type-1.c:40:3-40:17) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (90-distribute-fields-type-1.c:32:3-32:20) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (90-distribute-fields-type-1.c:32:3-32:20) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (90-distribute-fields-type-1.c:40:3-40:17) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (90-distribute-fields-type-1.c:40:3-40:17) + [Error][Imprecise][Unsound] Function definition missing for getS (90-distribute-fields-type-1.c:32:3-32:20) + [Error][Imprecise][Unsound] Function definition missing for getT (90-distribute-fields-type-1.c:40:3-40:17) diff --git a/tests/regression/04-mutex/91-distribute-fields-type-2.t b/tests/regression/04-mutex/91-distribute-fields-type-2.t index be365577f2..0b4ec982e8 100644 --- a/tests/regression/04-mutex/91-distribute-fields-type-2.t +++ b/tests/regression/04-mutex/91-distribute-fields-type-2.t @@ -1,31 +1,31 @@ - $ goblint --enable allglobs 91-distribute-fields-type-2.c - [Error][Imprecise][Unsound] Function definition missing for getS (91-distribute-fields-type-2.c:33:3-33:17) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (91-distribute-fields-type-2.c:33:3-33:17) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (91-distribute-fields-type-2.c:33:3-33:17) - [Info][Unsound] Unknown address in {&tmp} has escaped. (91-distribute-fields-type-2.c:33:3-33:17) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (91-distribute-fields-type-2.c:33:3-33:17) + $ goblint --enable warn.deterministic --enable allglobs 91-distribute-fields-type-2.c [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (91-distribute-fields-type-2.c:33:3-33:17) - [Info][Unsound] Write to unknown address: privatization is unsound. (91-distribute-fields-type-2.c:33:3-33:17) - [Error][Imprecise][Unsound] Function definition missing for getT (91-distribute-fields-type-2.c:41:3-41:17) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (91-distribute-fields-type-2.c:41:3-41:17) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (91-distribute-fields-type-2.c:41:3-41:17) - [Info][Unsound] Unknown address in {&tmp} has escaped. (91-distribute-fields-type-2.c:41:3-41:17) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (91-distribute-fields-type-2.c:41:3-41:17) [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (91-distribute-fields-type-2.c:41:3-41:17) - [Info][Unsound] Write to unknown address: privatization is unsound. (91-distribute-fields-type-2.c:41:3-41:17) - [Info][Deadcode] Logical lines of code (LLoC) summary: - live: 7 - dead: 0 - total lines: 7 - [Success][Race] Memory location (struct T) (safe): - write with [mhp:{tid=[main]; created={[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]}}, thread:[main]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:41:3-41:17) [Warning][Race] Memory location (struct T).s (race with conf. 100): write with [mhp:{tid=[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]}, thread:[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:33:3-33:17) write with [mhp:{tid=[main]; created={[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]}}, thread:[main]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:41:3-41:17) - [Success][Race] Memory location (struct S) (safe): - write with [mhp:{tid=[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]}, thread:[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:33:3-33:17) [Info][Race] Memory locations race summary: safe: 2 vulnerable: 0 unsafe: 1 total memory locations: 3 + [Success][Race] Memory location (struct S) (safe): + write with [mhp:{tid=[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]}, thread:[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:33:3-33:17) + [Success][Race] Memory location (struct T) (safe): + write with [mhp:{tid=[main]; created={[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]}}, thread:[main]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:41:3-41:17) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 0 + total lines: 7 + [Info][Unsound] Unknown address in {&tmp} has escaped. (91-distribute-fields-type-2.c:33:3-33:17) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (91-distribute-fields-type-2.c:33:3-33:17) + [Info][Unsound] Write to unknown address: privatization is unsound. (91-distribute-fields-type-2.c:33:3-33:17) + [Info][Unsound] Unknown address in {&tmp} has escaped. (91-distribute-fields-type-2.c:41:3-41:17) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (91-distribute-fields-type-2.c:41:3-41:17) + [Info][Unsound] Write to unknown address: privatization is unsound. (91-distribute-fields-type-2.c:41:3-41:17) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (91-distribute-fields-type-2.c:33:3-33:17) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (91-distribute-fields-type-2.c:33:3-33:17) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (91-distribute-fields-type-2.c:41:3-41:17) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (91-distribute-fields-type-2.c:41:3-41:17) + [Error][Imprecise][Unsound] Function definition missing for getS (91-distribute-fields-type-2.c:33:3-33:17) + [Error][Imprecise][Unsound] Function definition missing for getT (91-distribute-fields-type-2.c:41:3-41:17) diff --git a/tests/regression/04-mutex/92-distribute-fields-type-deep.t b/tests/regression/04-mutex/92-distribute-fields-type-deep.t index c0f3beae2c..aedc66f101 100644 --- a/tests/regression/04-mutex/92-distribute-fields-type-deep.t +++ b/tests/regression/04-mutex/92-distribute-fields-type-deep.t @@ -1,26 +1,6 @@ - $ goblint --enable allglobs 92-distribute-fields-type-deep.c - [Error][Imprecise][Unsound] Function definition missing for getS (92-distribute-fields-type-deep.c:37:3-37:20) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (92-distribute-fields-type-deep.c:37:3-37:20) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (92-distribute-fields-type-deep.c:37:3-37:20) - [Info][Unsound] Unknown address in {&tmp} has escaped. (92-distribute-fields-type-deep.c:37:3-37:20) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (92-distribute-fields-type-deep.c:37:3-37:20) + $ goblint --enable warn.deterministic --enable allglobs 92-distribute-fields-type-deep.c [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (92-distribute-fields-type-deep.c:37:3-37:20) - [Info][Unsound] Write to unknown address: privatization is unsound. (92-distribute-fields-type-deep.c:37:3-37:20) - [Error][Imprecise][Unsound] Function definition missing for getU (92-distribute-fields-type-deep.c:45:3-45:17) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (92-distribute-fields-type-deep.c:45:3-45:17) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (92-distribute-fields-type-deep.c:45:3-45:17) - [Info][Unsound] Unknown address in {&tmp} has escaped. (92-distribute-fields-type-deep.c:45:3-45:17) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (92-distribute-fields-type-deep.c:45:3-45:17) [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (92-distribute-fields-type-deep.c:45:3-45:17) - [Info][Unsound] Write to unknown address: privatization is unsound. (92-distribute-fields-type-deep.c:45:3-45:17) - [Info][Deadcode] Logical lines of code (LLoC) summary: - live: 7 - dead: 0 - total lines: 7 - [Success][Race] Memory location (struct S).field (safe): - write with [mhp:{tid=[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}, thread:[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (92-distribute-fields-type-deep.c:37:3-37:20) - [Success][Race] Memory location (struct U).t (safe): - write with [mhp:{tid=[main]; created={[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t) (92-distribute-fields-type-deep.c:45:3-45:17) [Warning][Race] Memory location (struct U).t.s.field (race with conf. 100): write with [mhp:{tid=[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}, thread:[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (92-distribute-fields-type-deep.c:37:3-37:20) write with [mhp:{tid=[main]; created={[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t) (92-distribute-fields-type-deep.c:45:3-45:17) @@ -29,3 +9,23 @@ vulnerable: 0 unsafe: 1 total memory locations: 3 + [Success][Race] Memory location (struct S).field (safe): + write with [mhp:{tid=[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}, thread:[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (92-distribute-fields-type-deep.c:37:3-37:20) + [Success][Race] Memory location (struct U).t (safe): + write with [mhp:{tid=[main]; created={[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t) (92-distribute-fields-type-deep.c:45:3-45:17) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 0 + total lines: 7 + [Info][Unsound] Unknown address in {&tmp} has escaped. (92-distribute-fields-type-deep.c:37:3-37:20) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (92-distribute-fields-type-deep.c:37:3-37:20) + [Info][Unsound] Write to unknown address: privatization is unsound. (92-distribute-fields-type-deep.c:37:3-37:20) + [Info][Unsound] Unknown address in {&tmp} has escaped. (92-distribute-fields-type-deep.c:45:3-45:17) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (92-distribute-fields-type-deep.c:45:3-45:17) + [Info][Unsound] Write to unknown address: privatization is unsound. (92-distribute-fields-type-deep.c:45:3-45:17) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (92-distribute-fields-type-deep.c:37:3-37:20) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (92-distribute-fields-type-deep.c:37:3-37:20) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (92-distribute-fields-type-deep.c:45:3-45:17) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (92-distribute-fields-type-deep.c:45:3-45:17) + [Error][Imprecise][Unsound] Function definition missing for getS (92-distribute-fields-type-deep.c:37:3-37:20) + [Error][Imprecise][Unsound] Function definition missing for getU (92-distribute-fields-type-deep.c:45:3-45:17) diff --git a/tests/regression/04-mutex/93-distribute-fields-type-global.t b/tests/regression/04-mutex/93-distribute-fields-type-global.t index 3761e9f7b4..fcc3e804c7 100644 --- a/tests/regression/04-mutex/93-distribute-fields-type-global.t +++ b/tests/regression/04-mutex/93-distribute-fields-type-global.t @@ -1,25 +1,25 @@ - $ goblint --enable allglobs 93-distribute-fields-type-global.c - [Error][Imprecise][Unsound] Function definition missing for getS (93-distribute-fields-type-global.c:14:3-14:29) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (93-distribute-fields-type-global.c:14:3-14:29) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(s, NoOffset)) (93-distribute-fields-type-global.c:14:3-14:29) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (93-distribute-fields-type-global.c:14:3-14:29) - [Info][Unsound] Unknown address in {&tmp} has escaped. (93-distribute-fields-type-global.c:14:3-14:29) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (93-distribute-fields-type-global.c:14:3-14:29) + $ goblint --enable warn.deterministic --enable allglobs 93-distribute-fields-type-global.c [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (93-distribute-fields-type-global.c:14:3-14:29) - [Info][Unsound] Write to unknown address: privatization is unsound. (93-distribute-fields-type-global.c:14:3-14:29) - [Info][Deadcode] Logical lines of code (LLoC) summary: - live: 7 - dead: 0 - total lines: 7 - [Success][Race] Memory location s (safe): (93-distribute-fields-type-global.c:9:10-9:11) - write with [mhp:{tid=[main]; created={[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]}}, thread:[main]] (conf. 110) (exp: & s) (93-distribute-fields-type-global.c:23:3-23:9) [Warning][Race] Memory location s.field (race with conf. 110): (93-distribute-fields-type-global.c:9:10-9:11) read with [mhp:{tid=[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]}, thread:[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]] (conf. 100) (exp: & tmp->field) (93-distribute-fields-type-global.c:14:3-14:29) write with [mhp:{tid=[main]; created={[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]}}, thread:[main]] (conf. 110) (exp: & s) (93-distribute-fields-type-global.c:23:3-23:9) - [Success][Race] Memory location (struct S).field (safe): - read with [mhp:{tid=[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]}, thread:[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]] (conf. 100) (exp: & tmp->field) (93-distribute-fields-type-global.c:14:3-14:29) [Info][Race] Memory locations race summary: safe: 2 vulnerable: 0 unsafe: 1 total memory locations: 3 + [Success][Race] Memory location (struct S).field (safe): + read with [mhp:{tid=[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]}, thread:[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]] (conf. 100) (exp: & tmp->field) (93-distribute-fields-type-global.c:14:3-14:29) + [Success][Race] Memory location s (safe): (93-distribute-fields-type-global.c:9:10-9:11) + write with [mhp:{tid=[main]; created={[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]}}, thread:[main]] (conf. 110) (exp: & s) (93-distribute-fields-type-global.c:23:3-23:9) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 0 + total lines: 7 + [Info][Unsound] Unknown address in {&tmp} has escaped. (93-distribute-fields-type-global.c:14:3-14:29) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (93-distribute-fields-type-global.c:14:3-14:29) + [Info][Unsound] Write to unknown address: privatization is unsound. (93-distribute-fields-type-global.c:14:3-14:29) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (93-distribute-fields-type-global.c:14:3-14:29) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(s, NoOffset)) (93-distribute-fields-type-global.c:14:3-14:29) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (93-distribute-fields-type-global.c:14:3-14:29) + [Error][Imprecise][Unsound] Function definition missing for getS (93-distribute-fields-type-global.c:14:3-14:29) From 21332cc346d78e84f5b51d24800e005971db2a08 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 15 Aug 2023 14:17:18 +0300 Subject: [PATCH 0435/1312] Refactor distribute_outer Co-authored-by: Simmo Saan --- src/analyses/raceAnalysis.ml | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index 93c2f0994f..5f100c869a 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -119,20 +119,21 @@ struct | `Type (TSArray (ts, _, _)), `Index ((), offset') -> Some (`Type ts, offset') | _, `Index ((), offset') -> None (* TODO: why indexing on non-array? *) - let rec distribute_outer ctx ((root, offset) : Access.Memo.t) : Access.AS.t = + let rec find_type_suffix' ctx ((root, offset) as memo : Access.Memo.t) : Access.AS.t = let trie = G.access (ctx.global (V.access root)) in let accs = match OffsetTrie.find offset trie with | `Lifted accs -> accs | `Bot -> Access.AS.empty () in - let type_suffix = - match type_suffix_memo (root, offset) with - | Some type_suffix_memo -> distribute_outer ctx type_suffix_memo - | None -> Access.AS.empty () - in + let type_suffix = find_type_suffix ctx memo in Access.AS.union accs type_suffix + and find_type_suffix ctx (memo : Access.Memo.t) : Access.AS.t = + match type_suffix_memo memo with + | Some type_suffix_memo -> find_type_suffix' ctx type_suffix_memo + | None -> Access.AS.empty () + let query ctx (type a) (q: a Queries.t): a Queries.result = match q with | WarnGlobal g -> @@ -148,11 +149,7 @@ struct | `Lifted accs -> accs | `Bot -> Access.AS.empty () in - let type_suffix = - match type_suffix_memo (g', offset) with - | Some type_suffix_memo -> distribute_outer ctx type_suffix_memo - | None -> Access.AS.empty () - in + let type_suffix = find_type_suffix ctx (g', offset) in if not (Access.AS.is_empty accs) || (not (Access.AS.is_empty prefix) && not (Access.AS.is_empty type_suffix)) then ( let memo = (g', offset) in let mem_loc_str = GobPretty.sprint Access.Memo.pretty memo in From e3e322a7ee08a8ab8424329a746355b9d59f46dc Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 15 Aug 2023 16:08:41 +0300 Subject: [PATCH 0436/1312] Clean up and comment lazy outer distribution changes --- src/analyses/raceAnalysis.ml | 27 ++++++++++++++--------- src/domains/access.ml | 42 ++++++++++++++++++++---------------- 2 files changed, 40 insertions(+), 29 deletions(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index 5f100c869a..3265bc6979 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -12,7 +12,7 @@ struct let name () = "race" (* Two global invariants: - 1. memoroot -> (offset -> accesses) -- used for warnings + 1. memoroot -> (offset --trie--> accesses) -- used for warnings 2. varinfo -> set of memo -- used for IterSysVars Global *) module V = @@ -52,6 +52,9 @@ struct module OffsetTrie = struct + (* LiftBot such that add_distribute_outer can side-effect empty set to indicate + all offsets that exist for prefix-type_suffix race checking. + Otherwise, there are no trie nodes to traverse to where this check must happen. *) include TrieDomain.Make (OneOffset) (Lattice.LiftBot (Access.AS)) let rec find (offset : Offset.Unit.t) ((accs, children) : t) : value = @@ -105,18 +108,19 @@ struct ctx.sideg (V.access memoroot) (G.create_access (OffsetTrie.singleton offset (`Lifted (Access.AS.singleton (conf, w, loc, e, a))))); side_vars ctx memo - let side_access0 ctx ((memoroot, offset) as memo) = - (* ignore (Pretty.printf "memo: %a\n" Access.Memo.pretty memo); *) + (** Side-effect empty access set for prefix-type_suffix race checking. *) + let side_access_empty ctx ((memoroot, offset) as memo) = if !AnalysisState.should_warn then ctx.sideg (V.access memoroot) (G.create_access (OffsetTrie.singleton offset (`Lifted (Access.AS.empty ())))); side_vars ctx memo + (** Get immediate type_suffix memo. *) let type_suffix_memo ((root, offset) : Access.Memo.t) : Access.Memo.t option = match root, offset with - | `Var v, _ -> Some (`Type (Cil.typeSig v.vtype), offset) (* TODO: Alloc variables void type *) - | _, `NoOffset -> None - | _, `Field (f, offset') -> Some (`Type (Cil.typeSig f.ftype), offset') - | `Type (TSArray (ts, _, _)), `Index ((), offset') -> Some (`Type ts, offset') + | `Var v, _ -> Some (`Type (Cil.typeSig v.vtype), offset) (* global.foo.bar -> (struct S).foo.bar *) (* TODO: Alloc variables void type *) + | _, `NoOffset -> None (* primitive type *) + | _, `Field (f, offset') -> Some (`Type (Cil.typeSig f.ftype), offset') (* (struct S).foo.bar -> (struct T).bar *) + | `Type (TSArray (ts, _, _)), `Index ((), offset') -> Some (`Type ts, offset') (* (int[])[*] -> int *) | _, `Index ((), offset') -> None (* TODO: why indexing on non-array? *) let rec find_type_suffix' ctx ((root, offset) as memo : Access.Memo.t) : Access.AS.t = @@ -129,6 +133,7 @@ struct let type_suffix = find_type_suffix ctx memo in Access.AS.union accs type_suffix + (** Find accesses from all type_suffixes transitively. *) and find_type_suffix ctx (memo : Access.Memo.t) : Access.AS.t = match type_suffix_memo memo with | Some type_suffix_memo -> find_type_suffix' ctx type_suffix_memo @@ -153,8 +158,10 @@ struct if not (Access.AS.is_empty accs) || (not (Access.AS.is_empty prefix) && not (Access.AS.is_empty type_suffix)) then ( let memo = (g', offset) in let mem_loc_str = GobPretty.sprint Access.Memo.pretty memo in - Timing.wrap ~args:[("memory location", `String mem_loc_str)] "race" (Access.warn_global ~safe ~vulnerable ~unsafe {prefix; type_suffix_prefix; type_suffix; node=accs}) memo + Timing.wrap ~args:[("memory location", `String mem_loc_str)] "race" (Access.warn_global ~safe ~vulnerable ~unsafe {node=accs; prefix; type_suffix; type_suffix_prefix}) memo ); + + (* Recurse to children. *) let prefix' = Access.AS.union prefix accs in let type_suffix_prefix' = Access.AS.union type_suffix_prefix type_suffix in OffsetTrie.ChildMap.iter (fun child_key child_trie -> @@ -184,11 +191,11 @@ struct let loc = Option.get !Node.current_node in let add_access conf voffs = let a = part_access (Option.map fst voffs) in - Access.add (side_access octx (conf, kind, loc, e, a)) (side_access0 octx) e voffs; + Access.add ~side:(side_access octx (conf, kind, loc, e, a)) ~side_empty:(side_access_empty octx) e voffs; in let add_access_struct conf ci = let a = part_access None in - Access.add_one (side_access octx (conf, kind, loc, e, a)) (`Type (TSComp (ci.cstruct, ci.cname, [])), `NoOffset) + Access.add_one ~side:(side_access octx (conf, kind, loc, e, a)) (`Type (TSComp (ci.cstruct, ci.cname, [])), `NoOffset) in let has_escaped g = octx.ask (Queries.MayEscape g) in (* The following function adds accesses to the lval-set ls diff --git a/src/domains/access.ml b/src/domains/access.ml index 457063950d..07d1632529 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -93,7 +93,7 @@ struct match vt with | `Var v -> CilType.Varinfo.pretty () v | `Type (TSComp (_, name, _)) -> Pretty.dprintf "(struct %s)" name - | `Type t -> Pretty.dprintf "(%a)" CilType.Typsig.pretty t + | `Type t -> Pretty.dprintf "(%a)" CilType.Typsig.pretty t (* TODO: fix TSBase printing *) include Printable.SimplePretty ( struct @@ -116,7 +116,7 @@ struct match vt with | `Var v -> Pretty.dprintf "%a%a" CilType.Varinfo.pretty v Offset.Unit.pretty o | `Type (TSComp (_, name, _)) -> Pretty.dprintf "(struct %s)%a" name Offset.Unit.pretty o - | `Type t -> Pretty.dprintf "(%a)%a" CilType.Typsig.pretty t Offset.Unit.pretty o + | `Type t -> Pretty.dprintf "(%a)%a" CilType.Typsig.pretty t Offset.Unit.pretty o (* TODO: fix TSBase printing *) include Printable.SimplePretty ( struct @@ -194,42 +194,44 @@ let get_val_type e: acc_typ = (** Add access to {!Memo} after distributing. *) -let add_one side memo: unit = +let add_one ~side memo: unit = let mv = Memo.to_mval memo in let ignorable = is_ignorable mv in if M.tracing then M.trace "access" "add_one %a (ignorable = %B)\n" Memo.pretty memo ignorable; if not ignorable then side memo -(** Distribute type-based access to variables and containing fields. *) -let rec add_distribute_outer side side0 (t: typ) (o: Offset.Unit.t) = +(** Distribute empty access set for type-based access to variables and containing fields. + Empty access sets are needed for prefix-type_suffix race checking. *) +let rec add_distribute_outer ~side ~side_empty (t: typ) (o: Offset.Unit.t) = let ts = typeSig t in let memo = (`Type ts, o) in if M.tracing then M.tracei "access" "add_distribute_outer %a\n" Memo.pretty memo; - add_one side memo; + add_one ~side memo; (* Add actual access for non-recursive call, or empty access for recursive call when side is side_empty. *) (* distribute to variables of the type *) let vars = TSH.find_all typeVar ts in List.iter (fun v -> - add_one side0 (`Var v, o) (* same offset, but on variable *) + (* same offset, but on variable *) + add_one ~side:side_empty (`Var v, o) (* Switch to side_empty. *) ) vars; (* recursively distribute to fields containing the type *) let fields = TSH.find_all typeIncl ts in List.iter (fun f -> (* prepend field and distribute to outer struct *) - add_distribute_outer side0 side0 (TComp (f.fcomp, [])) (`Field (f, o)) + add_distribute_outer ~side:side_empty ~side_empty (TComp (f.fcomp, [])) (`Field (f, o)) (* Switch to side_empty. *) ) fields; if M.tracing then M.traceu "access" "add_distribute_outer\n" (** Add access to known variable with offsets or unknown variable from expression. *) -let add side side0 e voffs = +let add ~side ~side_empty e voffs = begin match voffs with | Some (v, o) -> (* known variable *) if M.tracing then M.traceli "access" "add var %a%a\n" CilType.Varinfo.pretty v CilType.Offset.pretty o; let memo = (`Var v, Offset.Unit.of_cil o) in - add_one side memo + add_one ~side memo | None -> (* unknown variable *) if M.tracing then M.traceli "access" "add type %a\n" CilType.Exp.pretty e; let ty = get_val_type e in (* extract old acc_typ from expression *) @@ -239,7 +241,7 @@ let add side side0 e voffs = in match o with | `NoOffset when not !collect_direct_arithmetic && isArithmeticType t -> () - | _ -> add_distribute_outer side side0 t o (* distribute to variables and outer offsets *) + | _ -> add_distribute_outer ~side ~side_empty t o (* distribute to variables and outer offsets *) end; if M.tracing then M.traceu "access" "add\n" @@ -368,13 +370,14 @@ let may_race (conf,(kind: AccessKind.t),loc,e,a) (conf2,(kind2: AccessKind.t),lo else true +(** Access sets for race detection and warnings. *) module WarnAccs = struct type t = { - node: AS.t; - prefix: AS.t; - type_suffix: AS.t; - type_suffix_prefix: AS.t; + node: AS.t; (** Accesses for current memo. From accesses at trie node corresponding to memo offset. *) + prefix: AS.t; (** Accesses for all prefixes. From accesses to trie node ancestors. *) + type_suffix: AS.t; (** Accesses for all type suffixes. From offset suffixes in other tries. *) + type_suffix_prefix: AS.t; (** Accesses to all prefixes of all type suffixes. *) } let diff w1 w2 = { @@ -404,9 +407,9 @@ let group_may_race (warn_accs:WarnAccs.t) = if M.tracing then M.tracei "access" "group_may_race %a\n" WarnAccs.pretty warn_accs; (* BFS to traverse one component with may_race edges *) let rec bfs' warn_accs ~todo ~visited = - let warn_accs' = WarnAccs.diff warn_accs todo in let todo_all = WarnAccs.union_all todo in - let visited' = AS.union visited todo_all in + let visited' = AS.union visited todo_all in (* Add all todo accesses to component. *) + let warn_accs' = WarnAccs.diff warn_accs todo in (* Todo accesses don't need to be considered as step targets, because they're already in the component. *) let step_may_race ~todo ~accs = (* step from todo to accs if may_race *) AS.fold (fun acc todo' -> @@ -437,7 +440,7 @@ let group_may_race (warn_accs:WarnAccs.t) = prefix = step_may_race ~todo:(AS.union todo.node todo.type_suffix) ~accs:warn_accs'.prefix; type_suffix = step_may_race ~todo:(AS.union todo.node todo.prefix) ~accs:warn_accs'.type_suffix; type_suffix_prefix = step_may_race ~todo:todo.node ~accs:warn_accs'.type_suffix_prefix - } + } in if WarnAccs.is_empty todo' then @@ -446,7 +449,7 @@ let group_may_race (warn_accs:WarnAccs.t) = (bfs' [@tailcall]) warn_accs' ~todo:todo' ~visited:visited' in let bfs warn_accs todo = bfs' warn_accs ~todo ~visited:(AS.empty ()) in - (* repeat BFS to find all components *) + (* repeat BFS to find all components starting from node accesses *) let rec components comps (warn_accs:WarnAccs.t) = if AS.is_empty warn_accs.node then (comps, warn_accs) @@ -459,6 +462,7 @@ let group_may_race (warn_accs:WarnAccs.t) = in let (comps, warn_accs) = components [] warn_accs in if M.tracing then M.trace "access" "components %a\n" WarnAccs.pretty warn_accs; + (* repeat BFS to find all prefix-type_suffix-only components starting from prefix accesses (symmetric) *) let rec components_cross comps ~prefix ~type_suffix = if AS.is_empty prefix then comps From 81dac32fd56807e6695197c36e4c375eb073962a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 15 Aug 2023 16:58:39 +0300 Subject: [PATCH 0437/1312] Document new race analysis --- src/analyses/raceAnalysis.ml | 79 ++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index 3265bc6979..f617df2048 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -3,6 +3,85 @@ open GoblintCil open Analyses +(** Data race analysis with tries and hookers. + + Accesses are to memory locations ({{!Access.Memo} memos}) which consist of a root and offset. + {{!Access.MemoRoot} Root} can be: + + variable, if access is to known global variable or alloc-variable; + + type, if access is to unknown pointer. + + Accesses are (now) collected to sets for each corresponding memo, + after points-to sets are resolved, during postsolving. + + Race checking is performed per-memo, + except must additionally account for accesses to other memos (see diagram below): + + access to [s.f] can race with access to a prefix like [s], which writes an entire struct at once; + + access to [s.f] can race with type-based access like [(struct S).f]; + + access to [(struct S).f] can race with type-based access to a suffix like [(int)]. + + access to [(struct T).s.f] can race with type-based access like [(struct S)], which is a combination of the above. + + These are accounted for lazily (unlike in the past). + + Prefixes (a.k.a. inner distribution) are handled using a trie data structure enriched with lattice properties. + Race checking starts at the root and passes accesses to ancestor nodes down to children. + + Type suffixes (a.k.a. outer distribution) are handled by computing successive immediate type suffixes transitively + and accessing corresponding offsets from corresponding root tries in the global invariant. + + Type suffix prefixes (for the combination of the two) are handled by passing type suffix accesses down when traversing the prefix trie. + + Race checking happens at each trie node with the above three access sets at hand using {!Access.group_may_race}. + All necessary combinations between the four classes are handled, but unnecessary repeated work is carefully avoided. + E.g. accesses which are pairwise checked at some prefix are not re-checked pairwise at a node. + Thus, races (with prefixes or type suffixes) are reported for most precise memos with actual accesses: + at the longest prefix and longest type suffix. + + Additionally, accesses between prefix and type suffix intersecting at a node are checked. + These races are reported at the unique memo at the intersection of the prefix and the type suffix. + This requires an implementation hack to still eagerly do outer distribution, but only of empty access sets. + It ensures that corresponding trie nodes exist for traversal later. *) + +(** Example structure of related memos for race checking: + {v + (int) (S) (T) + \ / \ / \ + f s t + \ / \ / + f s + \ / + f + v} + where: + - [(int)] is a type-based memo root for the primitive [int] type; + - [(S)] and [(T)] are short for [(struct S)] and [(struct T)], which are type-based memo roots; + - [f] is a field of [S] and [s] is a field of [T]; + - [t] is a global variable of type [T]. + - prefix relations are indicated by [/]; + - type suffix relations are indicated by [\ ]. + + Prefix races: + - Race between [t.s.f] and [t.s] is checked/reported at [t.s.f]. + - Race between [t.s.f] and [t] is checked/reported at [t.s.f]. + - Race between [t.s] and [t] is checked/reported at [t.s]. + - Race between [t] and [t] is checked/reported at [t]. + - Race between [(S).f] and [(S)] is checked/reported at [(S).f]. + + Type suffix races: + - Race between [t.s.f] and [(T).s.f] is checked/reported at [t.s.f]. + - Race between [t.s.f] and [(S).f] is checked/reported at [t.s.f]. + - Race between [t.s.f] and [(int)] is checked/reported at [t.s.f]. + - Race between [(S).f] and [(int)] is checked/reported at [(S).f]. + + Type suffix prefix races: + - Race between [t.s.f] and [(T).s] is checked/reported at [t.s.f]. + - Race between [t.s.f] and [(T)] is checked/reported at [t.s.f]. + - Race between [t.s.f] and [(S)] is checked/reported at [t.s.f]. + - Race between [(T).s.f] and [(S)] is checked/reported at [(T).s.f]. + + Prefix-type suffix races: + - Race between [(T).s] and [(S).f] is checked/reported at [(T).s.f]. + - Race between [t] and [(S).f] is checked/reported at [t.s.f]. *) + (** Data race analyzer without base --- this is the new standard *) module Spec = From 7297241f591bc1719011dda4fe0ef0906ec2cddd Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 15 Aug 2023 17:15:48 +0300 Subject: [PATCH 0438/1312] Remove --enable ana.race.direct-arithmetic from some tests Irrelevant because no (int) accesses. --- .../regression/04-mutex/49-type-invariants.c | 1 - .../regression/04-mutex/49-type-invariants.t | 48 +++++++++---------- .../04-mutex/77-type-nested-fields.c | 5 +- .../04-mutex/77-type-nested-fields.t | 34 ++++++------- tests/regression/04-mutex/78-type-array.c | 1 - .../04-mutex/79-type-nested-fields-deep1.c | 5 +- .../04-mutex/79-type-nested-fields-deep1.t | 34 ++++++------- .../04-mutex/80-type-nested-fields-deep2.c | 5 +- .../04-mutex/80-type-nested-fields-deep2.t | 34 ++++++------- .../04-mutex/90-distribute-fields-type-1.c | 5 +- .../04-mutex/90-distribute-fields-type-1.t | 36 +++++++------- .../04-mutex/91-distribute-fields-type-2.c | 5 +- .../04-mutex/91-distribute-fields-type-2.t | 36 +++++++------- .../04-mutex/92-distribute-fields-type-deep.c | 5 +- .../04-mutex/92-distribute-fields-type-deep.t | 36 +++++++------- .../93-distribute-fields-type-global.c | 1 - .../93-distribute-fields-type-global.t | 28 +++++------ 17 files changed, 155 insertions(+), 164 deletions(-) diff --git a/tests/regression/04-mutex/49-type-invariants.c b/tests/regression/04-mutex/49-type-invariants.c index 4f69986478..e6ac17dcd9 100644 --- a/tests/regression/04-mutex/49-type-invariants.c +++ b/tests/regression/04-mutex/49-type-invariants.c @@ -1,4 +1,3 @@ -//PARAM: --enable ana.race.direct-arithmetic #include #include diff --git a/tests/regression/04-mutex/49-type-invariants.t b/tests/regression/04-mutex/49-type-invariants.t index 3d3f7442ef..3ddd8f237d 100644 --- a/tests/regression/04-mutex/49-type-invariants.t +++ b/tests/regression/04-mutex/49-type-invariants.t @@ -1,47 +1,47 @@ $ goblint --enable warn.deterministic --enable ana.race.direct-arithmetic --enable allglobs 49-type-invariants.c - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (49-type-invariants.c:22:3-22:21) - [Warning][Race] Memory location s.field (race with conf. 110): (49-type-invariants.c:9:10-9:11) - write with [mhp:{tid=[main]; created={[main, t_fun@49-type-invariants.c:21:3-21:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->field) (49-type-invariants.c:22:3-22:21) - read with [mhp:{tid=[main, t_fun@49-type-invariants.c:21:3-21:40#top]}, thread:[main, t_fun@49-type-invariants.c:21:3-21:40#top]] (conf. 110) (exp: & s.field) (49-type-invariants.c:12:3-12:23) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (49-type-invariants.c:21:3-21:21) + [Warning][Race] Memory location s.field (race with conf. 110): (49-type-invariants.c:8:10-8:11) + write with [mhp:{tid=[main]; created={[main, t_fun@49-type-invariants.c:20:3-20:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->field) (49-type-invariants.c:21:3-21:21) + read with [mhp:{tid=[main, t_fun@49-type-invariants.c:20:3-20:40#top]}, thread:[main, t_fun@49-type-invariants.c:20:3-20:40#top]] (conf. 110) (exp: & s.field) (49-type-invariants.c:11:3-11:23) [Info][Race] Memory locations race summary: safe: 1 vulnerable: 0 unsafe: 1 total memory locations: 2 [Success][Race] Memory location (struct S).field (safe): - write with [mhp:{tid=[main]; created={[main, t_fun@49-type-invariants.c:21:3-21:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->field) (49-type-invariants.c:22:3-22:21) + write with [mhp:{tid=[main]; created={[main, t_fun@49-type-invariants.c:20:3-20:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->field) (49-type-invariants.c:21:3-21:21) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 7 dead: 0 total lines: 7 - [Info][Unsound] Unknown address in {&tmp} has escaped. (49-type-invariants.c:22:3-22:21) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (49-type-invariants.c:22:3-22:21) - [Info][Unsound] Write to unknown address: privatization is unsound. (49-type-invariants.c:22:3-22:21) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (49-type-invariants.c:22:3-22:21) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(s, NoOffset)) (49-type-invariants.c:22:3-22:21) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (49-type-invariants.c:22:3-22:21) - [Error][Imprecise][Unsound] Function definition missing for getS (49-type-invariants.c:22:3-22:21) + [Info][Unsound] Unknown address in {&tmp} has escaped. (49-type-invariants.c:21:3-21:21) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (49-type-invariants.c:21:3-21:21) + [Info][Unsound] Write to unknown address: privatization is unsound. (49-type-invariants.c:21:3-21:21) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (49-type-invariants.c:21:3-21:21) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(s, NoOffset)) (49-type-invariants.c:21:3-21:21) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (49-type-invariants.c:21:3-21:21) + [Error][Imprecise][Unsound] Function definition missing for getS (49-type-invariants.c:21:3-21:21) $ goblint --enable warn.deterministic --disable ana.race.direct-arithmetic --enable allglobs 49-type-invariants.c - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (49-type-invariants.c:22:3-22:21) - [Warning][Race] Memory location s.field (race with conf. 110): (49-type-invariants.c:9:10-9:11) - write with [mhp:{tid=[main]; created={[main, t_fun@49-type-invariants.c:21:3-21:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->field) (49-type-invariants.c:22:3-22:21) - read with [mhp:{tid=[main, t_fun@49-type-invariants.c:21:3-21:40#top]}, thread:[main, t_fun@49-type-invariants.c:21:3-21:40#top]] (conf. 110) (exp: & s.field) (49-type-invariants.c:12:3-12:23) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (49-type-invariants.c:21:3-21:21) + [Warning][Race] Memory location s.field (race with conf. 110): (49-type-invariants.c:8:10-8:11) + write with [mhp:{tid=[main]; created={[main, t_fun@49-type-invariants.c:20:3-20:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->field) (49-type-invariants.c:21:3-21:21) + read with [mhp:{tid=[main, t_fun@49-type-invariants.c:20:3-20:40#top]}, thread:[main, t_fun@49-type-invariants.c:20:3-20:40#top]] (conf. 110) (exp: & s.field) (49-type-invariants.c:11:3-11:23) [Info][Race] Memory locations race summary: safe: 1 vulnerable: 0 unsafe: 1 total memory locations: 2 [Success][Race] Memory location (struct S).field (safe): - write with [mhp:{tid=[main]; created={[main, t_fun@49-type-invariants.c:21:3-21:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->field) (49-type-invariants.c:22:3-22:21) + write with [mhp:{tid=[main]; created={[main, t_fun@49-type-invariants.c:20:3-20:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->field) (49-type-invariants.c:21:3-21:21) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 7 dead: 0 total lines: 7 - [Info][Unsound] Unknown address in {&tmp} has escaped. (49-type-invariants.c:22:3-22:21) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (49-type-invariants.c:22:3-22:21) - [Info][Unsound] Write to unknown address: privatization is unsound. (49-type-invariants.c:22:3-22:21) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (49-type-invariants.c:22:3-22:21) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(s, NoOffset)) (49-type-invariants.c:22:3-22:21) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (49-type-invariants.c:22:3-22:21) - [Error][Imprecise][Unsound] Function definition missing for getS (49-type-invariants.c:22:3-22:21) + [Info][Unsound] Unknown address in {&tmp} has escaped. (49-type-invariants.c:21:3-21:21) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (49-type-invariants.c:21:3-21:21) + [Info][Unsound] Write to unknown address: privatization is unsound. (49-type-invariants.c:21:3-21:21) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (49-type-invariants.c:21:3-21:21) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(s, NoOffset)) (49-type-invariants.c:21:3-21:21) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (49-type-invariants.c:21:3-21:21) + [Error][Imprecise][Unsound] Function definition missing for getS (49-type-invariants.c:21:3-21:21) diff --git a/tests/regression/04-mutex/77-type-nested-fields.c b/tests/regression/04-mutex/77-type-nested-fields.c index a526defb06..00b21e3fcf 100644 --- a/tests/regression/04-mutex/77-type-nested-fields.c +++ b/tests/regression/04-mutex/77-type-nested-fields.c @@ -1,11 +1,10 @@ -//PARAM: --enable ana.race.direct-arithmetic #include #include // (int) (S) (T) (U) -// \ / \ / \ / +// \ / \ / \ / // >f< s t -// \ / \ / +// \ / \ / // >f< s // \ / // f diff --git a/tests/regression/04-mutex/77-type-nested-fields.t b/tests/regression/04-mutex/77-type-nested-fields.t index dc87d0a85e..738784f5d5 100644 --- a/tests/regression/04-mutex/77-type-nested-fields.t +++ b/tests/regression/04-mutex/77-type-nested-fields.t @@ -1,29 +1,29 @@ $ goblint --enable warn.deterministic --enable allglobs 77-type-nested-fields.c - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (77-type-nested-fields.c:32:3-32:20) - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (77-type-nested-fields.c:39:3-39:22) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (77-type-nested-fields.c:31:3-31:20) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (77-type-nested-fields.c:38:3-38:22) [Warning][Race] Memory location (struct T).s.field (race with conf. 100): - write with [mhp:{tid=[main, t_fun@77-type-nested-fields.c:38:3-38:40#top]}, thread:[main, t_fun@77-type-nested-fields.c:38:3-38:40#top]] (conf. 100) (exp: & tmp->field) (77-type-nested-fields.c:32:3-32:20) - write with [mhp:{tid=[main]; created={[main, t_fun@77-type-nested-fields.c:38:3-38:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->s.field) (77-type-nested-fields.c:39:3-39:22) + write with [mhp:{tid=[main, t_fun@77-type-nested-fields.c:37:3-37:40#top]}, thread:[main, t_fun@77-type-nested-fields.c:37:3-37:40#top]] (conf. 100) (exp: & tmp->field) (77-type-nested-fields.c:31:3-31:20) + write with [mhp:{tid=[main]; created={[main, t_fun@77-type-nested-fields.c:37:3-37:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->s.field) (77-type-nested-fields.c:38:3-38:22) [Info][Race] Memory locations race summary: safe: 1 vulnerable: 0 unsafe: 1 total memory locations: 2 [Success][Race] Memory location (struct S).field (safe): - write with [mhp:{tid=[main, t_fun@77-type-nested-fields.c:38:3-38:40#top]}, thread:[main, t_fun@77-type-nested-fields.c:38:3-38:40#top]] (conf. 100) (exp: & tmp->field) (77-type-nested-fields.c:32:3-32:20) + write with [mhp:{tid=[main, t_fun@77-type-nested-fields.c:37:3-37:40#top]}, thread:[main, t_fun@77-type-nested-fields.c:37:3-37:40#top]] (conf. 100) (exp: & tmp->field) (77-type-nested-fields.c:31:3-31:20) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 7 dead: 0 total lines: 7 - [Info][Unsound] Unknown address in {&tmp} has escaped. (77-type-nested-fields.c:32:3-32:20) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (77-type-nested-fields.c:32:3-32:20) - [Info][Unsound] Write to unknown address: privatization is unsound. (77-type-nested-fields.c:32:3-32:20) - [Info][Unsound] Unknown address in {&tmp} has escaped. (77-type-nested-fields.c:39:3-39:22) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (77-type-nested-fields.c:39:3-39:22) - [Info][Unsound] Write to unknown address: privatization is unsound. (77-type-nested-fields.c:39:3-39:22) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (77-type-nested-fields.c:32:3-32:20) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (77-type-nested-fields.c:32:3-32:20) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (77-type-nested-fields.c:39:3-39:22) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (77-type-nested-fields.c:39:3-39:22) - [Error][Imprecise][Unsound] Function definition missing for getS (77-type-nested-fields.c:32:3-32:20) - [Error][Imprecise][Unsound] Function definition missing for getT (77-type-nested-fields.c:39:3-39:22) + [Info][Unsound] Unknown address in {&tmp} has escaped. (77-type-nested-fields.c:31:3-31:20) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (77-type-nested-fields.c:31:3-31:20) + [Info][Unsound] Write to unknown address: privatization is unsound. (77-type-nested-fields.c:31:3-31:20) + [Info][Unsound] Unknown address in {&tmp} has escaped. (77-type-nested-fields.c:38:3-38:22) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (77-type-nested-fields.c:38:3-38:22) + [Info][Unsound] Write to unknown address: privatization is unsound. (77-type-nested-fields.c:38:3-38:22) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (77-type-nested-fields.c:31:3-31:20) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (77-type-nested-fields.c:31:3-31:20) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (77-type-nested-fields.c:38:3-38:22) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (77-type-nested-fields.c:38:3-38:22) + [Error][Imprecise][Unsound] Function definition missing for getS (77-type-nested-fields.c:31:3-31:20) + [Error][Imprecise][Unsound] Function definition missing for getT (77-type-nested-fields.c:38:3-38:22) diff --git a/tests/regression/04-mutex/78-type-array.c b/tests/regression/04-mutex/78-type-array.c index cdffe244b9..835f6163a3 100644 --- a/tests/regression/04-mutex/78-type-array.c +++ b/tests/regression/04-mutex/78-type-array.c @@ -1,4 +1,3 @@ -//PARAM: --enable ana.race.direct-arithmetic #include #include diff --git a/tests/regression/04-mutex/79-type-nested-fields-deep1.c b/tests/regression/04-mutex/79-type-nested-fields-deep1.c index c38e700829..e100404960 100644 --- a/tests/regression/04-mutex/79-type-nested-fields-deep1.c +++ b/tests/regression/04-mutex/79-type-nested-fields-deep1.c @@ -1,11 +1,10 @@ -//PARAM: --enable ana.race.direct-arithmetic #include #include // (int) (S) (T) (U) -// \ / \ / \ / +// \ / \ / \ / // >f< s t -// \ / \ / +// \ / \ / // f s // \ / // >f< diff --git a/tests/regression/04-mutex/79-type-nested-fields-deep1.t b/tests/regression/04-mutex/79-type-nested-fields-deep1.t index 5fc60223b9..2e15f83c39 100644 --- a/tests/regression/04-mutex/79-type-nested-fields-deep1.t +++ b/tests/regression/04-mutex/79-type-nested-fields-deep1.t @@ -1,29 +1,29 @@ $ goblint --enable warn.deterministic --enable allglobs 79-type-nested-fields-deep1.c - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (79-type-nested-fields-deep1.c:37:3-37:20) - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (79-type-nested-fields-deep1.c:44:3-44:24) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (79-type-nested-fields-deep1.c:36:3-36:20) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (79-type-nested-fields-deep1.c:43:3-43:24) [Warning][Race] Memory location (struct U).t.s.field (race with conf. 100): - write with [mhp:{tid=[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]}, thread:[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (79-type-nested-fields-deep1.c:37:3-37:20) - write with [mhp:{tid=[main]; created={[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t.s.field) (79-type-nested-fields-deep1.c:44:3-44:24) + write with [mhp:{tid=[main, t_fun@79-type-nested-fields-deep1.c:42:3-42:40#top]}, thread:[main, t_fun@79-type-nested-fields-deep1.c:42:3-42:40#top]] (conf. 100) (exp: & tmp->field) (79-type-nested-fields-deep1.c:36:3-36:20) + write with [mhp:{tid=[main]; created={[main, t_fun@79-type-nested-fields-deep1.c:42:3-42:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t.s.field) (79-type-nested-fields-deep1.c:43:3-43:24) [Info][Race] Memory locations race summary: safe: 1 vulnerable: 0 unsafe: 1 total memory locations: 2 [Success][Race] Memory location (struct S).field (safe): - write with [mhp:{tid=[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]}, thread:[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (79-type-nested-fields-deep1.c:37:3-37:20) + write with [mhp:{tid=[main, t_fun@79-type-nested-fields-deep1.c:42:3-42:40#top]}, thread:[main, t_fun@79-type-nested-fields-deep1.c:42:3-42:40#top]] (conf. 100) (exp: & tmp->field) (79-type-nested-fields-deep1.c:36:3-36:20) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 7 dead: 0 total lines: 7 - [Info][Unsound] Unknown address in {&tmp} has escaped. (79-type-nested-fields-deep1.c:37:3-37:20) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (79-type-nested-fields-deep1.c:37:3-37:20) - [Info][Unsound] Write to unknown address: privatization is unsound. (79-type-nested-fields-deep1.c:37:3-37:20) - [Info][Unsound] Unknown address in {&tmp} has escaped. (79-type-nested-fields-deep1.c:44:3-44:24) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (79-type-nested-fields-deep1.c:44:3-44:24) - [Info][Unsound] Write to unknown address: privatization is unsound. (79-type-nested-fields-deep1.c:44:3-44:24) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (79-type-nested-fields-deep1.c:37:3-37:20) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (79-type-nested-fields-deep1.c:37:3-37:20) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (79-type-nested-fields-deep1.c:44:3-44:24) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (79-type-nested-fields-deep1.c:44:3-44:24) - [Error][Imprecise][Unsound] Function definition missing for getS (79-type-nested-fields-deep1.c:37:3-37:20) - [Error][Imprecise][Unsound] Function definition missing for getU (79-type-nested-fields-deep1.c:44:3-44:24) + [Info][Unsound] Unknown address in {&tmp} has escaped. (79-type-nested-fields-deep1.c:36:3-36:20) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (79-type-nested-fields-deep1.c:36:3-36:20) + [Info][Unsound] Write to unknown address: privatization is unsound. (79-type-nested-fields-deep1.c:36:3-36:20) + [Info][Unsound] Unknown address in {&tmp} has escaped. (79-type-nested-fields-deep1.c:43:3-43:24) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (79-type-nested-fields-deep1.c:43:3-43:24) + [Info][Unsound] Write to unknown address: privatization is unsound. (79-type-nested-fields-deep1.c:43:3-43:24) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (79-type-nested-fields-deep1.c:36:3-36:20) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (79-type-nested-fields-deep1.c:36:3-36:20) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (79-type-nested-fields-deep1.c:43:3-43:24) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (79-type-nested-fields-deep1.c:43:3-43:24) + [Error][Imprecise][Unsound] Function definition missing for getS (79-type-nested-fields-deep1.c:36:3-36:20) + [Error][Imprecise][Unsound] Function definition missing for getU (79-type-nested-fields-deep1.c:43:3-43:24) diff --git a/tests/regression/04-mutex/80-type-nested-fields-deep2.c b/tests/regression/04-mutex/80-type-nested-fields-deep2.c index 9a1e3028a3..4ddd4684f7 100644 --- a/tests/regression/04-mutex/80-type-nested-fields-deep2.c +++ b/tests/regression/04-mutex/80-type-nested-fields-deep2.c @@ -1,11 +1,10 @@ -//PARAM: --enable ana.race.direct-arithmetic #include #include // (int) (S) (T) (U) -// \ / \ / \ / +// \ / \ / \ / // f s t -// \ / \ / +// \ / \ / // >f< s // \ / // >f< diff --git a/tests/regression/04-mutex/80-type-nested-fields-deep2.t b/tests/regression/04-mutex/80-type-nested-fields-deep2.t index a0541235ee..48e726f623 100644 --- a/tests/regression/04-mutex/80-type-nested-fields-deep2.t +++ b/tests/regression/04-mutex/80-type-nested-fields-deep2.t @@ -1,29 +1,29 @@ $ goblint --enable warn.deterministic --enable allglobs 80-type-nested-fields-deep2.c - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (80-type-nested-fields-deep2.c:37:3-37:22) - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (80-type-nested-fields-deep2.c:44:3-44:24) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (80-type-nested-fields-deep2.c:36:3-36:22) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (80-type-nested-fields-deep2.c:43:3-43:24) [Warning][Race] Memory location (struct U).t.s.field (race with conf. 100): - write with [mhp:{tid=[main, t_fun@80-type-nested-fields-deep2.c:43:3-43:40#top]}, thread:[main, t_fun@80-type-nested-fields-deep2.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->s.field) (80-type-nested-fields-deep2.c:37:3-37:22) - write with [mhp:{tid=[main]; created={[main, t_fun@80-type-nested-fields-deep2.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t.s.field) (80-type-nested-fields-deep2.c:44:3-44:24) + write with [mhp:{tid=[main, t_fun@80-type-nested-fields-deep2.c:42:3-42:40#top]}, thread:[main, t_fun@80-type-nested-fields-deep2.c:42:3-42:40#top]] (conf. 100) (exp: & tmp->s.field) (80-type-nested-fields-deep2.c:36:3-36:22) + write with [mhp:{tid=[main]; created={[main, t_fun@80-type-nested-fields-deep2.c:42:3-42:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t.s.field) (80-type-nested-fields-deep2.c:43:3-43:24) [Info][Race] Memory locations race summary: safe: 1 vulnerable: 0 unsafe: 1 total memory locations: 2 [Success][Race] Memory location (struct T).s.field (safe): - write with [mhp:{tid=[main, t_fun@80-type-nested-fields-deep2.c:43:3-43:40#top]}, thread:[main, t_fun@80-type-nested-fields-deep2.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->s.field) (80-type-nested-fields-deep2.c:37:3-37:22) + write with [mhp:{tid=[main, t_fun@80-type-nested-fields-deep2.c:42:3-42:40#top]}, thread:[main, t_fun@80-type-nested-fields-deep2.c:42:3-42:40#top]] (conf. 100) (exp: & tmp->s.field) (80-type-nested-fields-deep2.c:36:3-36:22) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 7 dead: 0 total lines: 7 - [Info][Unsound] Unknown address in {&tmp} has escaped. (80-type-nested-fields-deep2.c:37:3-37:22) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (80-type-nested-fields-deep2.c:37:3-37:22) - [Info][Unsound] Write to unknown address: privatization is unsound. (80-type-nested-fields-deep2.c:37:3-37:22) - [Info][Unsound] Unknown address in {&tmp} has escaped. (80-type-nested-fields-deep2.c:44:3-44:24) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (80-type-nested-fields-deep2.c:44:3-44:24) - [Info][Unsound] Write to unknown address: privatization is unsound. (80-type-nested-fields-deep2.c:44:3-44:24) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (80-type-nested-fields-deep2.c:37:3-37:22) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (80-type-nested-fields-deep2.c:37:3-37:22) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (80-type-nested-fields-deep2.c:44:3-44:24) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (80-type-nested-fields-deep2.c:44:3-44:24) - [Error][Imprecise][Unsound] Function definition missing for getT (80-type-nested-fields-deep2.c:37:3-37:22) - [Error][Imprecise][Unsound] Function definition missing for getU (80-type-nested-fields-deep2.c:44:3-44:24) + [Info][Unsound] Unknown address in {&tmp} has escaped. (80-type-nested-fields-deep2.c:36:3-36:22) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (80-type-nested-fields-deep2.c:36:3-36:22) + [Info][Unsound] Write to unknown address: privatization is unsound. (80-type-nested-fields-deep2.c:36:3-36:22) + [Info][Unsound] Unknown address in {&tmp} has escaped. (80-type-nested-fields-deep2.c:43:3-43:24) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (80-type-nested-fields-deep2.c:43:3-43:24) + [Info][Unsound] Write to unknown address: privatization is unsound. (80-type-nested-fields-deep2.c:43:3-43:24) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (80-type-nested-fields-deep2.c:36:3-36:22) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (80-type-nested-fields-deep2.c:36:3-36:22) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (80-type-nested-fields-deep2.c:43:3-43:24) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (80-type-nested-fields-deep2.c:43:3-43:24) + [Error][Imprecise][Unsound] Function definition missing for getT (80-type-nested-fields-deep2.c:36:3-36:22) + [Error][Imprecise][Unsound] Function definition missing for getU (80-type-nested-fields-deep2.c:43:3-43:24) diff --git a/tests/regression/04-mutex/90-distribute-fields-type-1.c b/tests/regression/04-mutex/90-distribute-fields-type-1.c index 51f0e52426..062b7421e6 100644 --- a/tests/regression/04-mutex/90-distribute-fields-type-1.c +++ b/tests/regression/04-mutex/90-distribute-fields-type-1.c @@ -1,11 +1,10 @@ -//PARAM: --enable ana.race.direct-arithmetic #include #include // (int) (S) (T) (U) -// \ / \ / \ / +// \ / \ / \ / // >f< >s< t -// \ / \ / +// \ / \ / // f s // \ / // f diff --git a/tests/regression/04-mutex/90-distribute-fields-type-1.t b/tests/regression/04-mutex/90-distribute-fields-type-1.t index fb6af7ceca..ba3e3da0ed 100644 --- a/tests/regression/04-mutex/90-distribute-fields-type-1.t +++ b/tests/regression/04-mutex/90-distribute-fields-type-1.t @@ -1,31 +1,31 @@ $ goblint --enable warn.deterministic --enable allglobs 90-distribute-fields-type-1.c - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (90-distribute-fields-type-1.c:32:3-32:20) - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (90-distribute-fields-type-1.c:40:3-40:17) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (90-distribute-fields-type-1.c:31:3-31:20) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (90-distribute-fields-type-1.c:39:3-39:17) [Warning][Race] Memory location (struct T).s.field (race with conf. 100): - write with [mhp:{tid=[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]}, thread:[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]] (conf. 100) (exp: & tmp->field) (90-distribute-fields-type-1.c:32:3-32:20) - write with [mhp:{tid=[main]; created={[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->s) (90-distribute-fields-type-1.c:40:3-40:17) + write with [mhp:{tid=[main, t_fun@90-distribute-fields-type-1.c:37:3-37:40#top]}, thread:[main, t_fun@90-distribute-fields-type-1.c:37:3-37:40#top]] (conf. 100) (exp: & tmp->field) (90-distribute-fields-type-1.c:31:3-31:20) + write with [mhp:{tid=[main]; created={[main, t_fun@90-distribute-fields-type-1.c:37:3-37:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->s) (90-distribute-fields-type-1.c:39:3-39:17) [Info][Race] Memory locations race summary: safe: 2 vulnerable: 0 unsafe: 1 total memory locations: 3 [Success][Race] Memory location (struct S).field (safe): - write with [mhp:{tid=[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]}, thread:[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]] (conf. 100) (exp: & tmp->field) (90-distribute-fields-type-1.c:32:3-32:20) + write with [mhp:{tid=[main, t_fun@90-distribute-fields-type-1.c:37:3-37:40#top]}, thread:[main, t_fun@90-distribute-fields-type-1.c:37:3-37:40#top]] (conf. 100) (exp: & tmp->field) (90-distribute-fields-type-1.c:31:3-31:20) [Success][Race] Memory location (struct T).s (safe): - write with [mhp:{tid=[main]; created={[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->s) (90-distribute-fields-type-1.c:40:3-40:17) + write with [mhp:{tid=[main]; created={[main, t_fun@90-distribute-fields-type-1.c:37:3-37:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->s) (90-distribute-fields-type-1.c:39:3-39:17) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 7 dead: 0 total lines: 7 - [Info][Unsound] Unknown address in {&tmp} has escaped. (90-distribute-fields-type-1.c:32:3-32:20) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (90-distribute-fields-type-1.c:32:3-32:20) - [Info][Unsound] Write to unknown address: privatization is unsound. (90-distribute-fields-type-1.c:32:3-32:20) - [Info][Unsound] Unknown address in {&tmp} has escaped. (90-distribute-fields-type-1.c:40:3-40:17) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (90-distribute-fields-type-1.c:40:3-40:17) - [Info][Unsound] Write to unknown address: privatization is unsound. (90-distribute-fields-type-1.c:40:3-40:17) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (90-distribute-fields-type-1.c:32:3-32:20) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (90-distribute-fields-type-1.c:32:3-32:20) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (90-distribute-fields-type-1.c:40:3-40:17) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (90-distribute-fields-type-1.c:40:3-40:17) - [Error][Imprecise][Unsound] Function definition missing for getS (90-distribute-fields-type-1.c:32:3-32:20) - [Error][Imprecise][Unsound] Function definition missing for getT (90-distribute-fields-type-1.c:40:3-40:17) + [Info][Unsound] Unknown address in {&tmp} has escaped. (90-distribute-fields-type-1.c:31:3-31:20) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (90-distribute-fields-type-1.c:31:3-31:20) + [Info][Unsound] Write to unknown address: privatization is unsound. (90-distribute-fields-type-1.c:31:3-31:20) + [Info][Unsound] Unknown address in {&tmp} has escaped. (90-distribute-fields-type-1.c:39:3-39:17) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (90-distribute-fields-type-1.c:39:3-39:17) + [Info][Unsound] Write to unknown address: privatization is unsound. (90-distribute-fields-type-1.c:39:3-39:17) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (90-distribute-fields-type-1.c:31:3-31:20) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (90-distribute-fields-type-1.c:31:3-31:20) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (90-distribute-fields-type-1.c:39:3-39:17) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (90-distribute-fields-type-1.c:39:3-39:17) + [Error][Imprecise][Unsound] Function definition missing for getS (90-distribute-fields-type-1.c:31:3-31:20) + [Error][Imprecise][Unsound] Function definition missing for getT (90-distribute-fields-type-1.c:39:3-39:17) diff --git a/tests/regression/04-mutex/91-distribute-fields-type-2.c b/tests/regression/04-mutex/91-distribute-fields-type-2.c index 12866105f6..01c945f730 100644 --- a/tests/regression/04-mutex/91-distribute-fields-type-2.c +++ b/tests/regression/04-mutex/91-distribute-fields-type-2.c @@ -1,11 +1,10 @@ -//PARAM: --enable ana.race.direct-arithmetic #include #include // (int) >(S)< >(T)< (U) -// \ / \ / \ / +// \ / \ / \ / // f s t -// \ / \ / +// \ / \ / // f s // \ / // f diff --git a/tests/regression/04-mutex/91-distribute-fields-type-2.t b/tests/regression/04-mutex/91-distribute-fields-type-2.t index 0b4ec982e8..fd544b0b0b 100644 --- a/tests/regression/04-mutex/91-distribute-fields-type-2.t +++ b/tests/regression/04-mutex/91-distribute-fields-type-2.t @@ -1,31 +1,31 @@ $ goblint --enable warn.deterministic --enable allglobs 91-distribute-fields-type-2.c - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (91-distribute-fields-type-2.c:33:3-33:17) - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (91-distribute-fields-type-2.c:41:3-41:17) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (91-distribute-fields-type-2.c:32:3-32:17) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (91-distribute-fields-type-2.c:40:3-40:17) [Warning][Race] Memory location (struct T).s (race with conf. 100): - write with [mhp:{tid=[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]}, thread:[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:33:3-33:17) - write with [mhp:{tid=[main]; created={[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]}}, thread:[main]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:41:3-41:17) + write with [mhp:{tid=[main, t_fun@91-distribute-fields-type-2.c:38:3-38:40#top]}, thread:[main, t_fun@91-distribute-fields-type-2.c:38:3-38:40#top]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:32:3-32:17) + write with [mhp:{tid=[main]; created={[main, t_fun@91-distribute-fields-type-2.c:38:3-38:40#top]}}, thread:[main]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:40:3-40:17) [Info][Race] Memory locations race summary: safe: 2 vulnerable: 0 unsafe: 1 total memory locations: 3 [Success][Race] Memory location (struct S) (safe): - write with [mhp:{tid=[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]}, thread:[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:33:3-33:17) + write with [mhp:{tid=[main, t_fun@91-distribute-fields-type-2.c:38:3-38:40#top]}, thread:[main, t_fun@91-distribute-fields-type-2.c:38:3-38:40#top]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:32:3-32:17) [Success][Race] Memory location (struct T) (safe): - write with [mhp:{tid=[main]; created={[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]}}, thread:[main]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:41:3-41:17) + write with [mhp:{tid=[main]; created={[main, t_fun@91-distribute-fields-type-2.c:38:3-38:40#top]}}, thread:[main]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:40:3-40:17) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 7 dead: 0 total lines: 7 - [Info][Unsound] Unknown address in {&tmp} has escaped. (91-distribute-fields-type-2.c:33:3-33:17) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (91-distribute-fields-type-2.c:33:3-33:17) - [Info][Unsound] Write to unknown address: privatization is unsound. (91-distribute-fields-type-2.c:33:3-33:17) - [Info][Unsound] Unknown address in {&tmp} has escaped. (91-distribute-fields-type-2.c:41:3-41:17) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (91-distribute-fields-type-2.c:41:3-41:17) - [Info][Unsound] Write to unknown address: privatization is unsound. (91-distribute-fields-type-2.c:41:3-41:17) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (91-distribute-fields-type-2.c:33:3-33:17) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (91-distribute-fields-type-2.c:33:3-33:17) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (91-distribute-fields-type-2.c:41:3-41:17) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (91-distribute-fields-type-2.c:41:3-41:17) - [Error][Imprecise][Unsound] Function definition missing for getS (91-distribute-fields-type-2.c:33:3-33:17) - [Error][Imprecise][Unsound] Function definition missing for getT (91-distribute-fields-type-2.c:41:3-41:17) + [Info][Unsound] Unknown address in {&tmp} has escaped. (91-distribute-fields-type-2.c:32:3-32:17) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (91-distribute-fields-type-2.c:32:3-32:17) + [Info][Unsound] Write to unknown address: privatization is unsound. (91-distribute-fields-type-2.c:32:3-32:17) + [Info][Unsound] Unknown address in {&tmp} has escaped. (91-distribute-fields-type-2.c:40:3-40:17) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (91-distribute-fields-type-2.c:40:3-40:17) + [Info][Unsound] Write to unknown address: privatization is unsound. (91-distribute-fields-type-2.c:40:3-40:17) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (91-distribute-fields-type-2.c:32:3-32:17) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (91-distribute-fields-type-2.c:32:3-32:17) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (91-distribute-fields-type-2.c:40:3-40:17) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (91-distribute-fields-type-2.c:40:3-40:17) + [Error][Imprecise][Unsound] Function definition missing for getS (91-distribute-fields-type-2.c:32:3-32:17) + [Error][Imprecise][Unsound] Function definition missing for getT (91-distribute-fields-type-2.c:40:3-40:17) diff --git a/tests/regression/04-mutex/92-distribute-fields-type-deep.c b/tests/regression/04-mutex/92-distribute-fields-type-deep.c index 891f5fb51f..59fb09a605 100644 --- a/tests/regression/04-mutex/92-distribute-fields-type-deep.c +++ b/tests/regression/04-mutex/92-distribute-fields-type-deep.c @@ -1,11 +1,10 @@ -//PARAM: --enable ana.race.direct-arithmetic #include #include // (int) (S) (T) (U) -// \ / \ / \ / +// \ / \ / \ / // >f< s >t< -// \ / \ / +// \ / \ / // f s // \ / // f diff --git a/tests/regression/04-mutex/92-distribute-fields-type-deep.t b/tests/regression/04-mutex/92-distribute-fields-type-deep.t index aedc66f101..aefc1520d1 100644 --- a/tests/regression/04-mutex/92-distribute-fields-type-deep.t +++ b/tests/regression/04-mutex/92-distribute-fields-type-deep.t @@ -1,31 +1,31 @@ $ goblint --enable warn.deterministic --enable allglobs 92-distribute-fields-type-deep.c - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (92-distribute-fields-type-deep.c:37:3-37:20) - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (92-distribute-fields-type-deep.c:45:3-45:17) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (92-distribute-fields-type-deep.c:36:3-36:20) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (92-distribute-fields-type-deep.c:44:3-44:17) [Warning][Race] Memory location (struct U).t.s.field (race with conf. 100): - write with [mhp:{tid=[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}, thread:[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (92-distribute-fields-type-deep.c:37:3-37:20) - write with [mhp:{tid=[main]; created={[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t) (92-distribute-fields-type-deep.c:45:3-45:17) + write with [mhp:{tid=[main, t_fun@92-distribute-fields-type-deep.c:42:3-42:40#top]}, thread:[main, t_fun@92-distribute-fields-type-deep.c:42:3-42:40#top]] (conf. 100) (exp: & tmp->field) (92-distribute-fields-type-deep.c:36:3-36:20) + write with [mhp:{tid=[main]; created={[main, t_fun@92-distribute-fields-type-deep.c:42:3-42:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t) (92-distribute-fields-type-deep.c:44:3-44:17) [Info][Race] Memory locations race summary: safe: 2 vulnerable: 0 unsafe: 1 total memory locations: 3 [Success][Race] Memory location (struct S).field (safe): - write with [mhp:{tid=[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}, thread:[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (92-distribute-fields-type-deep.c:37:3-37:20) + write with [mhp:{tid=[main, t_fun@92-distribute-fields-type-deep.c:42:3-42:40#top]}, thread:[main, t_fun@92-distribute-fields-type-deep.c:42:3-42:40#top]] (conf. 100) (exp: & tmp->field) (92-distribute-fields-type-deep.c:36:3-36:20) [Success][Race] Memory location (struct U).t (safe): - write with [mhp:{tid=[main]; created={[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t) (92-distribute-fields-type-deep.c:45:3-45:17) + write with [mhp:{tid=[main]; created={[main, t_fun@92-distribute-fields-type-deep.c:42:3-42:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t) (92-distribute-fields-type-deep.c:44:3-44:17) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 7 dead: 0 total lines: 7 - [Info][Unsound] Unknown address in {&tmp} has escaped. (92-distribute-fields-type-deep.c:37:3-37:20) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (92-distribute-fields-type-deep.c:37:3-37:20) - [Info][Unsound] Write to unknown address: privatization is unsound. (92-distribute-fields-type-deep.c:37:3-37:20) - [Info][Unsound] Unknown address in {&tmp} has escaped. (92-distribute-fields-type-deep.c:45:3-45:17) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (92-distribute-fields-type-deep.c:45:3-45:17) - [Info][Unsound] Write to unknown address: privatization is unsound. (92-distribute-fields-type-deep.c:45:3-45:17) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (92-distribute-fields-type-deep.c:37:3-37:20) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (92-distribute-fields-type-deep.c:37:3-37:20) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (92-distribute-fields-type-deep.c:45:3-45:17) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (92-distribute-fields-type-deep.c:45:3-45:17) - [Error][Imprecise][Unsound] Function definition missing for getS (92-distribute-fields-type-deep.c:37:3-37:20) - [Error][Imprecise][Unsound] Function definition missing for getU (92-distribute-fields-type-deep.c:45:3-45:17) + [Info][Unsound] Unknown address in {&tmp} has escaped. (92-distribute-fields-type-deep.c:36:3-36:20) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (92-distribute-fields-type-deep.c:36:3-36:20) + [Info][Unsound] Write to unknown address: privatization is unsound. (92-distribute-fields-type-deep.c:36:3-36:20) + [Info][Unsound] Unknown address in {&tmp} has escaped. (92-distribute-fields-type-deep.c:44:3-44:17) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (92-distribute-fields-type-deep.c:44:3-44:17) + [Info][Unsound] Write to unknown address: privatization is unsound. (92-distribute-fields-type-deep.c:44:3-44:17) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (92-distribute-fields-type-deep.c:36:3-36:20) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (92-distribute-fields-type-deep.c:36:3-36:20) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (92-distribute-fields-type-deep.c:44:3-44:17) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (92-distribute-fields-type-deep.c:44:3-44:17) + [Error][Imprecise][Unsound] Function definition missing for getS (92-distribute-fields-type-deep.c:36:3-36:20) + [Error][Imprecise][Unsound] Function definition missing for getU (92-distribute-fields-type-deep.c:44:3-44:17) diff --git a/tests/regression/04-mutex/93-distribute-fields-type-global.c b/tests/regression/04-mutex/93-distribute-fields-type-global.c index ad7839d95f..466d47e7fc 100644 --- a/tests/regression/04-mutex/93-distribute-fields-type-global.c +++ b/tests/regression/04-mutex/93-distribute-fields-type-global.c @@ -1,4 +1,3 @@ -//PARAM: --enable ana.race.direct-arithmetic #include #include diff --git a/tests/regression/04-mutex/93-distribute-fields-type-global.t b/tests/regression/04-mutex/93-distribute-fields-type-global.t index fcc3e804c7..2199c689b1 100644 --- a/tests/regression/04-mutex/93-distribute-fields-type-global.t +++ b/tests/regression/04-mutex/93-distribute-fields-type-global.t @@ -1,25 +1,25 @@ $ goblint --enable warn.deterministic --enable allglobs 93-distribute-fields-type-global.c - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (93-distribute-fields-type-global.c:14:3-14:29) - [Warning][Race] Memory location s.field (race with conf. 110): (93-distribute-fields-type-global.c:9:10-9:11) - read with [mhp:{tid=[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]}, thread:[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]] (conf. 100) (exp: & tmp->field) (93-distribute-fields-type-global.c:14:3-14:29) - write with [mhp:{tid=[main]; created={[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]}}, thread:[main]] (conf. 110) (exp: & s) (93-distribute-fields-type-global.c:23:3-23:9) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (93-distribute-fields-type-global.c:13:3-13:29) + [Warning][Race] Memory location s.field (race with conf. 110): (93-distribute-fields-type-global.c:8:10-8:11) + read with [mhp:{tid=[main, t_fun@93-distribute-fields-type-global.c:20:3-20:40#top]}, thread:[main, t_fun@93-distribute-fields-type-global.c:20:3-20:40#top]] (conf. 100) (exp: & tmp->field) (93-distribute-fields-type-global.c:13:3-13:29) + write with [mhp:{tid=[main]; created={[main, t_fun@93-distribute-fields-type-global.c:20:3-20:40#top]}}, thread:[main]] (conf. 110) (exp: & s) (93-distribute-fields-type-global.c:22:3-22:9) [Info][Race] Memory locations race summary: safe: 2 vulnerable: 0 unsafe: 1 total memory locations: 3 [Success][Race] Memory location (struct S).field (safe): - read with [mhp:{tid=[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]}, thread:[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]] (conf. 100) (exp: & tmp->field) (93-distribute-fields-type-global.c:14:3-14:29) - [Success][Race] Memory location s (safe): (93-distribute-fields-type-global.c:9:10-9:11) - write with [mhp:{tid=[main]; created={[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]}}, thread:[main]] (conf. 110) (exp: & s) (93-distribute-fields-type-global.c:23:3-23:9) + read with [mhp:{tid=[main, t_fun@93-distribute-fields-type-global.c:20:3-20:40#top]}, thread:[main, t_fun@93-distribute-fields-type-global.c:20:3-20:40#top]] (conf. 100) (exp: & tmp->field) (93-distribute-fields-type-global.c:13:3-13:29) + [Success][Race] Memory location s (safe): (93-distribute-fields-type-global.c:8:10-8:11) + write with [mhp:{tid=[main]; created={[main, t_fun@93-distribute-fields-type-global.c:20:3-20:40#top]}}, thread:[main]] (conf. 110) (exp: & s) (93-distribute-fields-type-global.c:22:3-22:9) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 7 dead: 0 total lines: 7 - [Info][Unsound] Unknown address in {&tmp} has escaped. (93-distribute-fields-type-global.c:14:3-14:29) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (93-distribute-fields-type-global.c:14:3-14:29) - [Info][Unsound] Write to unknown address: privatization is unsound. (93-distribute-fields-type-global.c:14:3-14:29) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (93-distribute-fields-type-global.c:14:3-14:29) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(s, NoOffset)) (93-distribute-fields-type-global.c:14:3-14:29) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (93-distribute-fields-type-global.c:14:3-14:29) - [Error][Imprecise][Unsound] Function definition missing for getS (93-distribute-fields-type-global.c:14:3-14:29) + [Info][Unsound] Unknown address in {&tmp} has escaped. (93-distribute-fields-type-global.c:13:3-13:29) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (93-distribute-fields-type-global.c:13:3-13:29) + [Info][Unsound] Write to unknown address: privatization is unsound. (93-distribute-fields-type-global.c:13:3-13:29) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (93-distribute-fields-type-global.c:13:3-13:29) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(s, NoOffset)) (93-distribute-fields-type-global.c:13:3-13:29) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (93-distribute-fields-type-global.c:13:3-13:29) + [Error][Imprecise][Unsound] Function definition missing for getS (93-distribute-fields-type-global.c:13:3-13:29) From 48884b3a2a17474092fff20f5298e410ff7be239 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 15 Aug 2023 17:40:46 +0300 Subject: [PATCH 0439/1312] Fix non-struct typsig pretty in MemoRoot --- src/domains/access.ml | 4 +- src/util/cilfacade.ml | 95 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 2 deletions(-) diff --git a/src/domains/access.ml b/src/domains/access.ml index 07d1632529..4069763b99 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -93,7 +93,7 @@ struct match vt with | `Var v -> CilType.Varinfo.pretty () v | `Type (TSComp (_, name, _)) -> Pretty.dprintf "(struct %s)" name - | `Type t -> Pretty.dprintf "(%a)" CilType.Typsig.pretty t (* TODO: fix TSBase printing *) + | `Type t -> Pretty.dprintf "(%a)" Cilfacade.pretty_typsig_like_typ t include Printable.SimplePretty ( struct @@ -116,7 +116,7 @@ struct match vt with | `Var v -> Pretty.dprintf "%a%a" CilType.Varinfo.pretty v Offset.Unit.pretty o | `Type (TSComp (_, name, _)) -> Pretty.dprintf "(struct %s)%a" name Offset.Unit.pretty o - | `Type t -> Pretty.dprintf "(%a)%a" CilType.Typsig.pretty t Offset.Unit.pretty o (* TODO: fix TSBase printing *) + | `Type t -> Pretty.dprintf "(%a)%a" Cilfacade.pretty_typsig_like_typ t Offset.Unit.pretty o include Printable.SimplePretty ( struct diff --git a/src/util/cilfacade.ml b/src/util/cilfacade.ml index d0dcc428ad..c77d2cd738 100644 --- a/src/util/cilfacade.ml +++ b/src/util/cilfacade.ml @@ -359,6 +359,101 @@ let split_anoncomp_name name = else invalid_arg "Cilfacade.split_anoncomp_name" +(** Pretty-print typsig like typ, because + {!d_typsig} prints with CIL constructors. *) +let rec pretty_typsig_like_typ (nameOpt: Pretty.doc option) () ts = + (* Copied & modified from Cil.defaultCilPrinterClass#pType. *) + let open Pretty in + let name = match nameOpt with None -> nil | Some d -> d in + let printAttributes (a: attributes) = + let pa = d_attrlist () a in + match nameOpt with + | None when not !print_CIL_Input -> + (* Cannot print the attributes in this case because gcc does not + like them here, except if we are printing for CIL. *) + if pa = nil then nil else + text "/*" ++ pa ++ text "*/" + | _ -> pa + in + match ts with + | TSBase t -> dn_type () t + | TSComp (cstruct, cname, a) -> + let su = if cstruct then "struct" else "union" in + text (su ^ " " ^ cname ^ " ") + ++ d_attrlist () a + ++ name + | TSEnum (ename, a) -> + text ("enum " ^ ename ^ " ") + ++ d_attrlist () a + ++ name + | TSPtr (bt, a) -> + (* Parenthesize the ( * attr name) if a pointer to a function or an + array. *) + let (paren: doc option), (bt': typsig) = + match bt with + | TSFun _ | TSArray _ -> Some (text "("), bt + | _ -> None, bt + in + let name' = text "*" ++ printAttributes a ++ name in + let name'' = (* Put the parenthesis *) + match paren with + Some p -> p ++ name' ++ text ")" + | _ -> name' + in + pretty_typsig_like_typ + (Some name'') + () + bt' + + | TSArray (elemt, lo, a) -> + (* ignore the const attribute for arrays *) + let a' = dropAttributes [ "pconst" ] a in + let name' = + if a' == [] then name else + if nameOpt == None then printAttributes a' else + text "(" ++ printAttributes a' ++ name ++ text ")" + in + pretty_typsig_like_typ + (Some (name' + ++ text "[" + ++ (match lo with None -> nil | Some e -> text (Z.to_string e)) + ++ text "]")) + () + elemt + + | TSFun (restyp, args, isvararg, a) -> + let name' = + if a == [] then name else + if nameOpt == None then printAttributes a else + text "(" ++ printAttributes a ++ name ++ text ")" + in + pretty_typsig_like_typ + (Some + (name' + ++ text "(" + ++ (align + ++ + (if args = Some [] && isvararg then + text "..." + else + (if args = None then nil + else if args = Some [] then text "void" + else + let pArg atype = + (pretty_typsig_like_typ None () atype) + in + (docList ~sep:(chr ',' ++ break) pArg) () + (match args with None -> [] | Some args -> args)) + ++ (if isvararg then break ++ text ", ..." else nil)) + ++ unalign) + ++ text ")")) + () + restyp + +(** Pretty-print typsig like typ, because + {!d_typsig} prints with CIL constructors. *) +let pretty_typsig_like_typ = pretty_typsig_like_typ None + (** HashSet of line numbers *) let locs = Hashtbl.create 200 From 789dd00022dfe69776c4e589b1d799c9120d433b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 15 Aug 2023 17:51:56 +0300 Subject: [PATCH 0440/1312] Use fewer Cil.typeSig calls for Access.add_distribute_outer --- src/domains/access.ml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/domains/access.ml b/src/domains/access.ml index 4069763b99..675d1c2e72 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -203,8 +203,7 @@ let add_one ~side memo: unit = (** Distribute empty access set for type-based access to variables and containing fields. Empty access sets are needed for prefix-type_suffix race checking. *) -let rec add_distribute_outer ~side ~side_empty (t: typ) (o: Offset.Unit.t) = - let ts = typeSig t in +let rec add_distribute_outer ~side ~side_empty (ts: typsig) (o: Offset.Unit.t) = let memo = (`Type ts, o) in if M.tracing then M.tracei "access" "add_distribute_outer %a\n" Memo.pretty memo; add_one ~side memo; (* Add actual access for non-recursive call, or empty access for recursive call when side is side_empty. *) @@ -220,7 +219,7 @@ let rec add_distribute_outer ~side ~side_empty (t: typ) (o: Offset.Unit.t) = let fields = TSH.find_all typeIncl ts in List.iter (fun f -> (* prepend field and distribute to outer struct *) - add_distribute_outer ~side:side_empty ~side_empty (TComp (f.fcomp, [])) (`Field (f, o)) (* Switch to side_empty. *) + add_distribute_outer ~side:side_empty ~side_empty (TSComp (f.fcomp.cstruct, f.fcomp.cname, [])) (`Field (f, o)) (* Switch to side_empty. *) ) fields; if M.tracing then M.traceu "access" "add_distribute_outer\n" @@ -241,7 +240,7 @@ let add ~side ~side_empty e voffs = in match o with | `NoOffset when not !collect_direct_arithmetic && isArithmeticType t -> () - | _ -> add_distribute_outer ~side ~side_empty t o (* distribute to variables and outer offsets *) + | _ -> add_distribute_outer ~side ~side_empty (Cil.typeSig t) o (* distribute to variables and outer offsets *) end; if M.tracing then M.traceu "access" "add\n" From 5713231a6cc58d4edf85529f657f50e2ec2b51b0 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Wed, 16 Aug 2023 17:14:35 +0300 Subject: [PATCH 0441/1312] Add some library functions that are used in the silver searcher --- src/analyses/libraryFunctions.ml | 15 +++++++++++++++ src/util/options.schema.json | 3 ++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 5dc311a587..42fa55a2b7 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -118,6 +118,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("_setjmp", special [__ "env" [w]] @@ fun env -> Setjmp { env }); (* only has one underscore *) ("setjmp", special [__ "env" [w]] @@ fun env -> Setjmp { env }); ("longjmp", special [__ "env" [r]; __ "value" []] @@ fun env value -> Longjmp { env; value }); + ("fnmatch", unknown [drop "pattern" [r]; drop "string" [r]; drop "flags" []]); ] (** C POSIX library functions. @@ -191,6 +192,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("unsetenv", unknown ~attrs:[ThreadUnsafe] [drop "name" [r]]); ("lseek", unknown [drop "fd" []; drop "offset" []; drop "whence" []]); ("fcntl", unknown (drop "fd" [] :: drop "cmd" [] :: VarArgs (drop' [r; w]))); + ("__open_missing_mode", unknown []); ("fseeko", unknown [drop "stream" [r_deep; w_deep]; drop "offset" []; drop "whence" []]); ("fileno", unknown [drop "stream" [r_deep; w_deep]]); ("fdopen", unknown [drop "fd" []; drop "mode" [r]]); @@ -344,6 +346,8 @@ let pthread_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("pthread_mutexattr_destroy", unknown [drop "attr" [f]]); ("pthread_attr_setschedparam", unknown [drop "attr" [r; w]; drop "param" [r]]); ("sem_timedwait", unknown [drop "sem" [r]; drop "abs_timeout" [r]]); (* no write accesses to sem because sync primitive itself has no race *) + ("pthread_setaffinity_np", unknown [drop "thread" [w]; drop "cpusetsize" []; drop "cpuset" [r; w]]); + ("pthread_getaffinity_np", unknown [drop "thread" [w]; drop "cpusetsize" []; drop "cpuset" [r; w]]); ] (** GCC builtin functions. @@ -454,6 +458,7 @@ let linux_userspace_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("sysinfo", unknown [drop "info" [w_deep]]); ("__xpg_basename", unknown [drop "path" [r]]); ("ptrace", unknown (drop "request" [] :: VarArgs (drop' [r_deep; w_deep]))); (* man page has 4 arguments, but header has varargs and real-world programs may call with <4 *) + ("madvise", unknown [drop "addr" []; drop "length" []; drop "advice" []]); ] let big_kernel_lock = AddrOf (Cil.var (Cilfacade.create_var (makeGlobalVar "[big kernel lock]" intType))) @@ -803,6 +808,15 @@ let ncurses_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("wbkgd", unknown [drop "win" [r_deep; w_deep]; drop "ch" []]); ] +let pcre_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ + ("pcre_compile", unknown [drop "pattern" [r]; drop "options" []; drop "errptr" [w]; drop "erroffset" [w]; drop "tableptr" [r]]); + ("pcre_compile2", unknown [drop "pattern" [r]; drop "options" []; drop "errorcodeptr" [w]; drop "errptr" [w]; drop "erroffset" [w]; drop "tableptr" [r]]); + ("pcre_config", unknown [drop "what" []; drop "where" [w]]); + ("pcre_exec", unknown [drop "code" [r_deep]; drop "extra" [r_deep]; drop "subject" [r]; drop "length" []; drop "startoffset" []; drop "options" []; drop "ovector" [w]; drop "ovecsize" []]); + ("pcre_study", unknown [drop "code" [r_deep]; drop "options" []; drop "errptr" [w]]); + ("pcre_version", unknown []); + ] + let libraries = Hashtbl.of_list [ ("c", c_descs_list @ math_descs_list); ("posix", posix_descs_list); @@ -815,6 +829,7 @@ let libraries = Hashtbl.of_list [ ("sv-comp", svcomp_descs_list); ("ncurses", ncurses_descs_list); ("zstd", zstd_descs_list); + ("pcre", pcre_descs_list); ] let activated_library_descs: (string, LibraryDesc.t) Hashtbl.t ResettableLazy.t = diff --git a/src/util/options.schema.json b/src/util/options.schema.json index 0e89ede0b5..1ef73378fb 100644 --- a/src/util/options.schema.json +++ b/src/util/options.schema.json @@ -1284,7 +1284,8 @@ "goblint", "sv-comp", "ncurses", - "zstd" + "zstd", + "pcre" ] }, "default": [ From a7656fbb3b926bc55b7e8b6afb1555caa7ceef98 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 18 Aug 2023 09:43:49 +0200 Subject: [PATCH 0442/1312] Add check for memory deallocation at an offset --- src/analyses/base.ml | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index a213170ba2..2ef8257d59 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2013,14 +2013,21 @@ struct let st' = invalidate ~deep:false ~ctx (Analyses.ask_of_ctx ctx) gs st shallow_addrs in invalidate ~deep:true ~ctx (Analyses.ask_of_ctx ctx) gs st' deep_addrs - let check_free_of_non_heap_mem ctx special_fn ptr = + let check_invalid_mem_dealloc ctx special_fn ptr = + let has_offset = function + | `NoOffset -> false + | `Field _ + | `Index _ -> true + in match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local ptr with | Address a -> let points_to_set = addrToLvalSet a in if Q.LS.is_top points_to_set then - M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Points-to set for pointer %a in function %s is top. Potential free of non-dynamically allocated memory may occur" d_exp ptr special_fn.vname + M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Points-to set for pointer %a in function %s is top. Potentially invalid memory deallocation may occur" d_exp ptr special_fn.vname else if (Q.LS.exists (fun (v, _) -> not (ctx.ask (Q.IsHeapVar v))) points_to_set) || (AD.mem Addr.UnknownPtr a) then M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Free of non-dynamically allocated memory in function %s for pointer %a" special_fn.vname d_exp ptr + else if Q.LS.exists (fun (_, o) -> has_offset o) points_to_set then + M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 761] "Free of memory not at start of buffer in function %s for pointer %a" special_fn.vname d_exp ptr | _ -> M.warn ~category:MessageCategory.Analyzer "Pointer %a in function %s doesn't evaluate to a valid address." d_exp ptr special_fn.vname let special ctx (lv:lval option) (f: varinfo) (args: exp list) = @@ -2304,7 +2311,7 @@ struct end | Realloc { ptr = p; size }, _ -> (* Realloc shouldn't be passed non-dynamically allocated memory *) - check_free_of_non_heap_mem ctx f p; + check_invalid_mem_dealloc ctx f p; begin match lv with | Some lv -> let ask = Analyses.ask_of_ctx ctx in @@ -2338,7 +2345,7 @@ struct end | Free ptr, _ -> (* Free shouldn't be passed non-dynamically allocated memory *) - check_free_of_non_heap_mem ctx f ptr; + check_invalid_mem_dealloc ctx f ptr; st | Assert { exp; refine; _ }, _ -> assert_fn ctx exp refine | Setjmp { env }, _ -> From 6ac2a6f2b0e2a149b064937369bb6d098efd7077 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 18 Aug 2023 09:46:31 +0200 Subject: [PATCH 0443/1312] Refactor out "has_offset" as helper function --- src/analyses/base.ml | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 2ef8257d59..1087678c8b 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -138,6 +138,11 @@ struct let project ask p_opt cpa fundec = CPA.mapi (fun varinfo value -> project_val ask (attributes_varinfo varinfo fundec) p_opt value (is_privglob varinfo)) cpa + let has_offset = function + | `NoOffset -> false + | `Field _ + | `Index _ -> true + (************************************************************************** * Initializing my variables @@ -1262,11 +1267,6 @@ struct match p with | Address a -> let s = addrToLvalSet a in - let has_offset = function - | `NoOffset -> false - | `Field _ - | `Index _ -> true - in (* If there's a non-heap var or an offset in the lval set, we answer with bottom *) if ValueDomainQueries.LS.exists (fun (v, o) -> (not @@ ctx.ask (Queries.IsHeapVar v)) || has_offset o) s then Queries.Result.bot q @@ -2014,11 +2014,6 @@ struct invalidate ~deep:true ~ctx (Analyses.ask_of_ctx ctx) gs st' deep_addrs let check_invalid_mem_dealloc ctx special_fn ptr = - let has_offset = function - | `NoOffset -> false - | `Field _ - | `Index _ -> true - in match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local ptr with | Address a -> let points_to_set = addrToLvalSet a in From 0ed93f2db6901bc94e2d9ef4162d11064cf44b3f Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 18 Aug 2023 10:02:05 +0200 Subject: [PATCH 0444/1312] Add more regression test cases for invalid dealloc --- .../75-invalid_dealloc/05-free-at-offset.c | 9 +++++++++ .../75-invalid_dealloc/06-realloc-at-offset.c | 11 +++++++++++ .../75-invalid_dealloc/07-free-at-struct-offset.c | 15 +++++++++++++++ .../08-realloc-at-struct-offset.c | 15 +++++++++++++++ 4 files changed, 50 insertions(+) create mode 100644 tests/regression/75-invalid_dealloc/05-free-at-offset.c create mode 100644 tests/regression/75-invalid_dealloc/06-realloc-at-offset.c create mode 100644 tests/regression/75-invalid_dealloc/07-free-at-struct-offset.c create mode 100644 tests/regression/75-invalid_dealloc/08-realloc-at-struct-offset.c diff --git a/tests/regression/75-invalid_dealloc/05-free-at-offset.c b/tests/regression/75-invalid_dealloc/05-free-at-offset.c new file mode 100644 index 0000000000..c9ec66c769 --- /dev/null +++ b/tests/regression/75-invalid_dealloc/05-free-at-offset.c @@ -0,0 +1,9 @@ +#include + +int main(int argc, char const *argv[]) { + char *ptr = malloc(42 * sizeof(char)); + ptr = ptr + 7; + free(ptr); //WARN + + return 0; +} diff --git a/tests/regression/75-invalid_dealloc/06-realloc-at-offset.c b/tests/regression/75-invalid_dealloc/06-realloc-at-offset.c new file mode 100644 index 0000000000..64a42654e1 --- /dev/null +++ b/tests/regression/75-invalid_dealloc/06-realloc-at-offset.c @@ -0,0 +1,11 @@ +#include + +#define MAX_SIZE 5000 + +int main(int argc, char const *argv[]) { + char *ptr = malloc(42 * sizeof(char)); + ptr = ptr + 7; + realloc(ptr, MAX_SIZE); //WARN + + return 0; +} diff --git a/tests/regression/75-invalid_dealloc/07-free-at-struct-offset.c b/tests/regression/75-invalid_dealloc/07-free-at-struct-offset.c new file mode 100644 index 0000000000..f5f727f280 --- /dev/null +++ b/tests/regression/75-invalid_dealloc/07-free-at-struct-offset.c @@ -0,0 +1,15 @@ +#include + +typedef struct custom_t { + char *x; + int y; +} custom_t; + +int main(int argc, char const *argv[]) { + custom_t *struct_ptr = malloc(sizeof(custom_t)); + struct_ptr->x = malloc(10 * sizeof(char)); + free(struct_ptr->x); //NOWARN + free(struct_ptr->y); //WARN + free(struct_ptr); //NOWARN + return 0; +} diff --git a/tests/regression/75-invalid_dealloc/08-realloc-at-struct-offset.c b/tests/regression/75-invalid_dealloc/08-realloc-at-struct-offset.c new file mode 100644 index 0000000000..c163396524 --- /dev/null +++ b/tests/regression/75-invalid_dealloc/08-realloc-at-struct-offset.c @@ -0,0 +1,15 @@ +#include + +typedef struct custom_t { + char *x; + int y; +} custom_t; + +int main(int argc, char const *argv[]) { + custom_t *struct_ptr = malloc(sizeof(custom_t)); + struct_ptr->x = malloc(10 * sizeof(char)); + realloc(struct_ptr->x, 50); //NOWARN + realloc(struct_ptr->y, 50); //WARN + realloc(struct_ptr, 2 * sizeof(custom_t)); //NOWARN + return 0; +} From cb811e1f6d6ff42c4f3f62f93d18c34c767fad75 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 18 Aug 2023 11:19:28 +0200 Subject: [PATCH 0445/1312] Replace has_offset with comparisons --- src/analyses/base.ml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 1087678c8b..558646a8e2 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -138,11 +138,6 @@ struct let project ask p_opt cpa fundec = CPA.mapi (fun varinfo value -> project_val ask (attributes_varinfo varinfo fundec) p_opt value (is_privglob varinfo)) cpa - let has_offset = function - | `NoOffset -> false - | `Field _ - | `Index _ -> true - (************************************************************************** * Initializing my variables @@ -1268,7 +1263,7 @@ struct | Address a -> let s = addrToLvalSet a in (* If there's a non-heap var or an offset in the lval set, we answer with bottom *) - if ValueDomainQueries.LS.exists (fun (v, o) -> (not @@ ctx.ask (Queries.IsHeapVar v)) || has_offset o) s then + if ValueDomainQueries.LS.exists (fun (v, o) -> (not @@ ctx.ask (Queries.IsHeapVar v)) || o <> `NoOffset) s then Queries.Result.bot q else ( let r = get ~full:true (Analyses.ask_of_ctx ctx) ctx.global ctx.local a None in @@ -2021,7 +2016,7 @@ struct M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Points-to set for pointer %a in function %s is top. Potentially invalid memory deallocation may occur" d_exp ptr special_fn.vname else if (Q.LS.exists (fun (v, _) -> not (ctx.ask (Q.IsHeapVar v))) points_to_set) || (AD.mem Addr.UnknownPtr a) then M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Free of non-dynamically allocated memory in function %s for pointer %a" special_fn.vname d_exp ptr - else if Q.LS.exists (fun (_, o) -> has_offset o) points_to_set then + else if Q.LS.exists (fun (_, o) -> o <> `NoOffset) points_to_set then M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 761] "Free of memory not at start of buffer in function %s for pointer %a" special_fn.vname d_exp ptr | _ -> M.warn ~category:MessageCategory.Analyzer "Pointer %a in function %s doesn't evaluate to a valid address." d_exp ptr special_fn.vname From 0e61b9edf96f6c9fbb80121d0f3b81c9bfdbdfff Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 18 Aug 2023 12:48:34 +0300 Subject: [PATCH 0446/1312] Add script for finding modules missing from API documentation --- scripts/goblint-lib-modules.py | 58 ++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100755 scripts/goblint-lib-modules.py diff --git a/scripts/goblint-lib-modules.py b/scripts/goblint-lib-modules.py new file mode 100755 index 0000000000..0347f5dc86 --- /dev/null +++ b/scripts/goblint-lib-modules.py @@ -0,0 +1,58 @@ +#!/usr/bin/python3 + +from pathlib import Path +import re +import sys + +src_root_path = Path("./src") + +goblint_lib_path = src_root_path / "goblint_lib.ml" +goblint_lib_modules = set() + +with goblint_lib_path.open() as goblint_lib_file: + for line in goblint_lib_file: + line = line.strip() + m = re.match(r"module (.*) = .*", line) + if m is not None: + module_name = m.group(1) + goblint_lib_modules.add(module_name) + +src_vendor_path = src_root_path / "vendor" +exclude_module_names = set([ + "Goblint_lib", # itself + + # executables + "Goblint", + "MessagesCompare", + "PrivPrecCompare", + "ApronPrecCompare", + "Mainspec", + + # libraries + "Goblint_timing", + "Goblint_backtrace", + "Goblint_sites", + "Goblint_build_info", + + "MessageCategory", # included in Messages + "PreValueDomain", # included in ValueDomain + "SpecCore", # spec stuff + "SpecUtil", # spec stuff +]) + +src_modules = set() + +for ml_path in src_root_path.glob("**/*.ml"): + if str(ml_path).startswith(str(src_vendor_path)): + continue + + module_name = ml_path.with_suffix("").with_suffix("").name + module_name = module_name[0].upper() + module_name[1:] + if module_name.endswith("0") or module_name.endswith("_intf") or module_name in exclude_module_names: + continue + + src_modules.add(module_name) + +if len(src_modules) > 0: + print(f"Modules missing from {goblint_lib_path}: {src_modules - goblint_lib_modules}") + sys.exit(1) From f618594ef2e9b1644a79890cab29b46340157d9b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 18 Aug 2023 12:52:10 +0300 Subject: [PATCH 0447/1312] Add missing analyses to API documentation --- src/analyses/tmpSpecial.ml | 7 ++++--- src/goblint_lib.ml | 3 +++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/analyses/tmpSpecial.ml b/src/analyses/tmpSpecial.ml index c4af80906e..2d38972d7a 100644 --- a/src/analyses/tmpSpecial.ml +++ b/src/analyses/tmpSpecial.ml @@ -1,5 +1,6 @@ -(* Analysis that tracks which variables hold the results of calls to math library functions. - For each equivalence a set of expressions is tracked, that contains the arguments of the corresponding call as well as the Lval it is assigned to, so an equivalence can be removed if one of these expressions may be changed.*) +(** Analysis that tracks which variables hold the results of calls to math library functions ([tmpSpecial]). *) + +(** For each equivalence a set of expressions is tracked, that contains the arguments of the corresponding call as well as the Lval it is assigned to, so an equivalence can be removed if one of these expressions may be changed. *) module VarEq = VarEq.Spec @@ -69,7 +70,7 @@ struct (* actually it would be necessary to check here, if one of the arguments is written by the call. However this is not the case for any of the math functions and no other functions are covered so far *) if List.exists (fun arg -> VarEq.may_change ask (mkAddrOf lv) arg) arglist then d - else + else D.add (v, Offset.Exp.of_cil offs) ((ML.lift fun_args, Deps.of_list ((Lval lv)::arglist))) d | _ -> d diff --git a/src/goblint_lib.ml b/src/goblint_lib.ml index d8d74acc0f..625e81f18b 100644 --- a/src/goblint_lib.ml +++ b/src/goblint_lib.ml @@ -74,6 +74,7 @@ module ApronAnalysis = ApronAnalysis module AffineEqualityAnalysis = AffineEqualityAnalysis module VarEq = VarEq module CondVars = CondVars +module TmpSpecial = TmpSpecial (** {2 Heap} @@ -82,6 +83,8 @@ module CondVars = CondVars module Region = Region module MallocFresh = MallocFresh module Malloc_null = Malloc_null +module MemLeak = MemLeak +module UseAfterFree = UseAfterFree (** {2 Concurrency} From b9de7cb70349a8d6d416fffb3a7cc917166a217e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 18 Aug 2023 12:54:15 +0300 Subject: [PATCH 0448/1312] Remove unused OSEK FlagModeDomain --- src/cdomains/flagModeDomain.ml | 52 ---------------------------------- 1 file changed, 52 deletions(-) delete mode 100644 src/cdomains/flagModeDomain.ml diff --git a/src/cdomains/flagModeDomain.ml b/src/cdomains/flagModeDomain.ml deleted file mode 100644 index 70ee6d0015..0000000000 --- a/src/cdomains/flagModeDomain.ml +++ /dev/null @@ -1,52 +0,0 @@ -(* TODO: unused *) - -module Eq = IntDomain.MakeBooleans (struct let truename="==" let falsename="!=" end) -module Method = IntDomain.MakeBooleans (struct let truename="guard" let falsename="assign" end) - -module L_names = -struct - let bot_name = "unreachable" - let top_name = "unknown" -end - -module P = -struct - include Lattice.Flat (Printable.Prod3 (Method) (Eq) (IntDomain.FlatPureIntegers)) (L_names) - let show x = match x with - | `Lifted (m,b,e) -> Method.show m ^"ed "^ Eq.show b ^ " " ^ IntDomain.FlatPureIntegers.show e - | `Top -> top_name - | `Bot -> bot_name - - let join x y = match x,y with - | `Bot , z | z , `Bot -> z - | `Lifted (false,_,c1),`Lifted (false,_,c2) when c1=c2 -> y - | `Lifted (true,false,c1),`Lifted (true,false,c2) when c1=c2 -> y - | `Lifted (true,true,c1),`Lifted (true, true, c2) when c1=c2 -> y - | `Lifted (true,true,c1),`Lifted (true, false, c2) when not(c1=c2) -> y - | `Lifted (true,false,c1),`Lifted (true, true, c2) when not(c1=c2) -> x - | _ -> `Top - - - let leq (x:t) (y:t) = match x,y with - | `Bot , _ -> true - | _ , `Top -> true - | _, `Bot -> false - | `Top ,_ -> false - | `Lifted (false,_,c1), `Lifted (false,_,c2) -> c1=c2 - | _, `Lifted (false,_,_) -> false - | `Lifted (false,_,_), _ -> true - | `Lifted (true,true,c1),`Lifted (true, true, c2) -> c1=c2 - | _, `Lifted (true,true,_) -> false - | `Lifted (true, true, _), _ -> true - | `Lifted (true,false,c1),`Lifted (true,false,c2) -> c1=c2 - (* | _, `Lifted (true,false,c1) -> false - | `Lifted (true,false,_), _ -> true *) - (* | _ -> false *) -end - -module Dom = -struct - include MapDomain.MapTop_LiftBot (Basetype.Variables) (P) - - (* let find k x = if mem k x then find k x else P.top() *) -end From 67dcdfd73d30797884d29f55832f609d8c1986bc Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 18 Aug 2023 12:54:25 +0300 Subject: [PATCH 0449/1312] Fix goblint-lib-modules.py exit code --- scripts/goblint-lib-modules.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/goblint-lib-modules.py b/scripts/goblint-lib-modules.py index 0347f5dc86..342f9a76bd 100755 --- a/scripts/goblint-lib-modules.py +++ b/scripts/goblint-lib-modules.py @@ -53,6 +53,7 @@ src_modules.add(module_name) -if len(src_modules) > 0: - print(f"Modules missing from {goblint_lib_path}: {src_modules - goblint_lib_modules}") +missing_modules = src_modules - goblint_lib_modules +if len(missing_modules) > 0: + print(f"Modules missing from {goblint_lib_path}: {missing_modules}") sys.exit(1) From a135dea1f9a0146893f1b38458f3839d8a75f8f6 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 18 Aug 2023 12:55:45 +0300 Subject: [PATCH 0450/1312] Add goblint-lib-modules.py to docs workflow --- .github/workflows/docs.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index cd0414d6fe..aa69baf958 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -32,6 +32,9 @@ jobs: - name: Checkout code uses: actions/checkout@v3 + - name: Check for undocumented modules + run: python scripts/goblint-lib-modules.py + - name: Set up OCaml ${{ matrix.ocaml-compiler }} env: # otherwise setup-ocaml pins non-locked dependencies From 8614b522db13b1586bbe0f6ad0d162d07695e13d Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 18 Aug 2023 12:01:24 +0200 Subject: [PATCH 0451/1312] Fix two test cases by taking addresses of struct fields --- .../regression/75-invalid_dealloc/07-free-at-struct-offset.c | 4 ++-- .../75-invalid_dealloc/08-realloc-at-struct-offset.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/regression/75-invalid_dealloc/07-free-at-struct-offset.c b/tests/regression/75-invalid_dealloc/07-free-at-struct-offset.c index f5f727f280..f64d66d8fc 100644 --- a/tests/regression/75-invalid_dealloc/07-free-at-struct-offset.c +++ b/tests/regression/75-invalid_dealloc/07-free-at-struct-offset.c @@ -8,8 +8,8 @@ typedef struct custom_t { int main(int argc, char const *argv[]) { custom_t *struct_ptr = malloc(sizeof(custom_t)); struct_ptr->x = malloc(10 * sizeof(char)); - free(struct_ptr->x); //NOWARN - free(struct_ptr->y); //WARN + free(&struct_ptr->x); //NOWARN + free(&struct_ptr->y); //WARN free(struct_ptr); //NOWARN return 0; } diff --git a/tests/regression/75-invalid_dealloc/08-realloc-at-struct-offset.c b/tests/regression/75-invalid_dealloc/08-realloc-at-struct-offset.c index c163396524..fddb8a7694 100644 --- a/tests/regression/75-invalid_dealloc/08-realloc-at-struct-offset.c +++ b/tests/regression/75-invalid_dealloc/08-realloc-at-struct-offset.c @@ -8,8 +8,8 @@ typedef struct custom_t { int main(int argc, char const *argv[]) { custom_t *struct_ptr = malloc(sizeof(custom_t)); struct_ptr->x = malloc(10 * sizeof(char)); - realloc(struct_ptr->x, 50); //NOWARN - realloc(struct_ptr->y, 50); //WARN + realloc(&struct_ptr->x, 50); //NOWARN + realloc(&struct_ptr->y, 50); //WARN realloc(struct_ptr, 2 * sizeof(custom_t)); //NOWARN return 0; } From 92c0dcae14badbb2be23a62d7c5e15951f877bc2 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 18 Aug 2023 12:01:45 +0200 Subject: [PATCH 0452/1312] Use cmp_zero_offset for checking invalid free at offset --- src/analyses/base.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 558646a8e2..824fde18d9 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2016,7 +2016,7 @@ struct M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Points-to set for pointer %a in function %s is top. Potentially invalid memory deallocation may occur" d_exp ptr special_fn.vname else if (Q.LS.exists (fun (v, _) -> not (ctx.ask (Q.IsHeapVar v))) points_to_set) || (AD.mem Addr.UnknownPtr a) then M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Free of non-dynamically allocated memory in function %s for pointer %a" special_fn.vname d_exp ptr - else if Q.LS.exists (fun (_, o) -> o <> `NoOffset) points_to_set then + else if Q.LS.exists (fun (_, o) -> Offset.Exp.cmp_zero_offset o <> `MustZero) points_to_set then M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 761] "Free of memory not at start of buffer in function %s for pointer %a" special_fn.vname d_exp ptr | _ -> M.warn ~category:MessageCategory.Analyzer "Pointer %a in function %s doesn't evaluate to a valid address." d_exp ptr special_fn.vname From 434a4c1202382e0a0965a0792948423181bf3813 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 20 Aug 2023 18:39:19 +0200 Subject: [PATCH 0453/1312] Enable intervals for regression tests --- tests/regression/73-mem-oob/01-oob-heap-simple.c | 2 +- tests/regression/73-mem-oob/02-oob-stack-simple.c | 2 +- tests/regression/73-mem-oob/03-oob-loop.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/regression/73-mem-oob/01-oob-heap-simple.c b/tests/regression/73-mem-oob/01-oob-heap-simple.c index 91d9c7605a..10c7864184 100644 --- a/tests/regression/73-mem-oob/01-oob-heap-simple.c +++ b/tests/regression/73-mem-oob/01-oob-heap-simple.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.activated[+] memOutOfBounds +// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval #include int main(int argc, char const *argv[]) { diff --git a/tests/regression/73-mem-oob/02-oob-stack-simple.c b/tests/regression/73-mem-oob/02-oob-stack-simple.c index deedd52781..8d022feca4 100644 --- a/tests/regression/73-mem-oob/02-oob-stack-simple.c +++ b/tests/regression/73-mem-oob/02-oob-stack-simple.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.activated[+] memOutOfBounds +// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval #include int main(int argc, char const *argv[]) { diff --git a/tests/regression/73-mem-oob/03-oob-loop.c b/tests/regression/73-mem-oob/03-oob-loop.c index 5e0e08c381..c800597757 100644 --- a/tests/regression/73-mem-oob/03-oob-loop.c +++ b/tests/regression/73-mem-oob/03-oob-loop.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.activated[+] memOutOfBounds +// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval #include #include From 3db3d8c7c3c42588dc970d4b8f0219ee04a3d6a9 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 20 Aug 2023 19:06:51 +0200 Subject: [PATCH 0454/1312] Fix memOutOfBounds analysis and make it work --- src/analyses/memOutOfBounds.ml | 212 ++++++++++----------------------- 1 file changed, 66 insertions(+), 146 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 85e8530de7..c4e5f59bc2 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -3,28 +3,22 @@ open Analyses open MessageCategory module AS = AnalysisState +module VDQ = ValueDomainQueries module Spec = struct include Analyses.IdentitySpec module D = Lattice.Unit - module C = Lattice.Unit + module C = D - (* TODO: Do this later *) + (* TODO: Check out later for benchmarking *) let context _ _ = () let name () = "memOutOfBounds" (* HELPER FUNCTIONS *) - - (* A safe way to call [cilint_to_int] without having to worry about exceptions *) - let cilint_to_int_wrapper i = - try - Some (cilint_to_int i) - with _ -> None - let rec exp_contains_a_ptr (exp:exp) = match exp with | Const _ @@ -59,128 +53,57 @@ struct in host_contains_a_ptr host || offset_contains_a_ptr offset - let lval_is_ptr_var (lval:lval) = - let (host, _) = lval in - match host with - | Var v -> isPointerType v.vtype - (* Intuition: If the lval has a Mem host, then it's not a direct ptr which is what we're looking for here *) - | Mem e -> false - - let exp_points_to_heap ctx (exp:exp) = - match ctx.ask (Queries.MayPointTo exp) with + let points_to_heap_only ctx ptr = + match ctx.ask (Queries.MayPointTo ptr) with | a when not (Queries.LS.is_top a) && not (Queries.LS.mem (dummyFunDec.svar, `NoOffset) a) -> - Queries.LS.elements a - |> List.map fst - |> List.exists (fun x -> ctx.ask (Queries.IsHeapVar x)) - | _ -> false (* TODO: Is this sound? Maybe not quite. *) - - let get_size_for_heap_ptr ctx (exp:exp) = - (* TODO: - BlobSize always seems to respond with top when it's passed an Lval exp of a ptr var which in turn contains mem allocated via malloc. - Am I doing smth wrong here? - *) - match ctx.ask (Queries.BlobSize exp) with - | a when not (Queries.ID.is_top a) -> - begin match Queries.ID.to_int a with - | Some i -> Some (IntOps.BigIntOps.to_int i) - | None -> None - end - | _ -> None - - (* TODO: Here we assume that the given exp is a Lval exp *) - let get_size_for_stack_ptr ctx (exp:exp) = - match exp with - | Lval lval -> - if lval_is_ptr_var lval then - let (host, _) = lval in - begin match host with - | Var v -> - begin match sizeOf v.vtype with - | Const (CInt (i, _, _)) -> cilint_to_int_wrapper i - | _ -> None - end - | _ -> None + Queries.LS.for_all (fun (v, _) -> ctx.ask (Queries.IsHeapVar v)) a + | _ -> false + + let get_size_of_ptr_target ctx ptr = + (* Call Queries.BlobSize only if ptr points solely to the heap. Otherwise we get bot *) + if points_to_heap_only ctx ptr then + ctx.ask (Queries.BlobSize ptr) + else + match ctx.ask (Queries.MayPointTo ptr) with + | a when not (Queries.LS.is_top a) -> + let pts_list = Queries.LS.elements a in + let pts_elems_to_sizes (v, _) = + if isArrayType v.vtype then + ctx.ask (Queries.EvalLength ptr) + else + let var_type_size = match Cil.getInteger (sizeOf v.vtype) with + | Some z -> + let ikindOfTypeSize = intKindForValue z true in + VDQ.ID.of_int ikindOfTypeSize z + | None -> VDQ.ID.bot () + in + var_type_size + in + (* Map each points-to-set element to its size *) + let pts_sizes = List.map pts_elems_to_sizes pts_list in + (* Take the smallest of all sizes that ptr's contents may have *) + begin match pts_sizes with + | [] -> VDQ.ID.bot () + | [x] -> x + | x::xs -> List.fold_left (fun acc elem -> + if VDQ.ID.compare acc elem >= 0 then elem else acc + ) x xs end - else None - | _ -> None + | _ -> + M.warn "Pointer %a has a points-to-set of top. An invalid memory access might occur" d_exp ptr; + VDQ.ID.top () - let get_ptr_size_for_exp ctx (exp:exp) = - match exp_points_to_heap ctx exp with - (* We're dealing with a ptr that points to the heap *) - | true -> get_size_for_heap_ptr ctx exp - (* Assumption here is that if it doesn't point to the heap, then it points to the stack *) - | false -> get_size_for_stack_ptr ctx exp + let eval_ptr_offset_in_binop ctx exp = + ctx.ask (Queries.EvalInt exp) - (** - * If we get [None], then the offset's size/value is unknown - * In the case [NoOffset], [Some 0] indicates that this offset type simply has value 0 - *) - let rec get_offset_size = function - | NoOffset -> Some 0 - | Index (e, o) -> - let exp_val = begin match constFold true e with - | Const (CInt (i, _, _)) -> cilint_to_int_wrapper i - | _ -> None - end - in - begin match exp_val, get_offset_size o with - | Some ei, Some oi -> Some (ei + oi) - | _, _ -> None - end - | Field (f, o) -> - begin match get_offset_size o, sizeOf f.ftype with - | Some oi, Const (CInt (i, _, _)) -> - begin match cilint_to_int_wrapper i with - | Some i -> Some (oi + i) - | None -> None - end - | _, _ -> None - end - - let rec check_lval_for_oob_access ctx (lval:lval) = - let undefined_behavior = Undefined MemoryOutOfBoundsAccess in - let cwe_number = 823 in - match lval_contains_a_ptr lval with - | false -> () (* Nothing to do here *) - | true -> - let (host, offset) = lval in - match host, get_offset_size offset with - | _, None -> - AS.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior undefined_behavior) "Offset size for lval %a not known. A memory out-of-bounds access may occur" CilType.Lval.pretty lval - | Var v, Some oi -> - begin match sizeOf v.vtype with - | Const (CInt (i, _, _)) -> - begin match cilint_to_int_wrapper i with - | Some i -> - if i < oi then - AS.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Offset bigger than var type's size for lval %a. A memory out-of-bounds access must occur" CilType.Lval.pretty lval - | _ -> - AS.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Unknown size of var %a for lval %a. A memory out-of-bounds access might occur" CilType.Varinfo.pretty v CilType.Lval.pretty lval - end - | _ -> - AS.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Unknown size of var %a for lval %a. A memory out-of-bounds access might occur" CilType.Varinfo.pretty v CilType.Lval.pretty lval - end - | Mem e, Some oi -> - check_exp_for_oob_access ctx e; - (* TODO: - * Not sure if we actually need these checks below. - * They never seem to apply - * I.e., for ptrs, it always seems to be the case that we have a binop which adds the offset to the ptr - instead of having the offset represented as an offset of the lval - * For this reason, the checks below are currently commented out - *) - (* begin match get_ptr_size_for_exp ctx e with - | Some ei -> - if ei < oi then - M.warn "Offset bigger than size of pointer memory, denoted by expression %a in lval %a" d_exp e CilType.Lval.pretty lval - | _ -> M.warn "Unknown size of pointer memory, denoted by exp %a for lval %a" d_exp e CilType.Lval.pretty lval - end *) + let rec check_lval_for_oob_access ctx lval = + if not @@ lval_contains_a_ptr lval then () + else + match lval with + | Var _, _ -> () + | Mem e, _ -> check_exp_for_oob_access ctx e - and check_exp_for_oob_access ctx (exp:exp) = + and check_exp_for_oob_access ctx exp = match exp with | Const _ | SizeOf _ @@ -193,8 +116,8 @@ struct | AlignOfE e | UnOp (_, e, _) | CastE (_, e) -> check_exp_for_oob_access ctx e - | BinOp (bop, e1, e2, _) -> - check_binop_exp ctx bop e1 e2; + | BinOp (bop, e1, e2, t) -> + check_binop_exp ctx bop e1 e2 t; check_exp_for_oob_access ctx e1; check_exp_for_oob_access ctx e2 | Question (e1, e2, e3, _) -> @@ -205,34 +128,31 @@ struct | StartOf lval | AddrOf lval -> check_lval_for_oob_access ctx lval - and check_binop_exp ctx (binop:binop) (e1:exp) (e2:exp) = - let undefined_behavior = Undefined MemoryOutOfBoundsAccess in + and check_binop_exp ctx binop e1 e2 t = + let binopexp = BinOp (binop, e1, e2, t) in + let behavior = Undefined MemoryOutOfBoundsAccess in let cwe_number = 823 in match binop with | PlusPI | IndexPI | MinusPI -> - let ptr_size = get_ptr_size_for_exp ctx e1 in - let offset_size = eval_ptr_offset_in_binop e2 in - begin match ptr_size, offset_size with - | Some pi, Some oi -> - if pi < oi then - AS.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Pointer size in expression %a %a %a is smaller than offset for pointer arithmetic. Memory out-of-bounds access must occur" d_exp e1 CilType.Binop.pretty binop d_exp e2 - | None, _ -> + let ptr_size = get_size_of_ptr_target ctx e1 in + let offset_size = eval_ptr_offset_in_binop ctx e2 in + begin match VDQ.ID.is_top ptr_size, VDQ.ID.is_top offset_size with + | true, _ -> AS.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Pointer (%a) size in expression %a %a %a not known. Memory out-of-bounds access might occur" d_exp e1 d_exp e1 CilType.Binop.pretty binop d_exp e2 - | _, None -> + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Pointer (%a) size in expression %a not known. Memory out-of-bounds access might occur" d_exp e1 d_exp binopexp + | _, true -> AS.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Operand value for pointer arithmetic in expression %a %a %a not known. Memory out-of-bounds access might occur" d_exp e1 CilType.Binop.pretty binop d_exp e2 + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Operand value for pointer arithmetic in expression %a not known. Memory out-of-bounds access might occur" d_exp binopexp + | false, false -> + if ptr_size < offset_size then begin + AS.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Pointer size (%a) in expression %a is smaller than offset (%a) for pointer arithmetic. Memory out-of-bounds access must occur" VDQ.ID.pretty ptr_size d_exp binopexp VDQ.ID.pretty offset_size + end end | _ -> () - and eval_ptr_offset_in_binop (exp:exp) = - match constFold true exp with - | Const (CInt (i, _, _)) -> cilint_to_int_wrapper i - | _ -> None (* TODO: Maybe try to also Eval the exp via Queries and not rely only on constFold *) - (* TRANSFER FUNCTIONS *) From 6e7664d47f2c85e3cc40cd4ff571038ee55da059 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 20 Aug 2023 19:08:28 +0200 Subject: [PATCH 0455/1312] Remove unused transfer funs --- src/analyses/memOutOfBounds.ml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index c4e5f59bc2..655596d3ec 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -174,12 +174,6 @@ struct List.iter (fun arg -> check_exp_for_oob_access ctx arg) arglist; ctx.local - let enter ctx (lval:lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = - [ctx.local, ctx.local] - - let combine_env ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (callee_local:D.t) (f_ask:Queries.ask) : D.t = - ctx.local - let combine_assign ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (callee_local:D.t) (f_ask:Queries.ask) : D.t = Option.iter (fun x -> check_lval_for_oob_access ctx x) lval; List.iter (fun arg -> check_exp_for_oob_access ctx arg) args; From 79d4319ae752893680ed1fa7d7ed2be067629b16 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 20 Aug 2023 19:10:54 +0200 Subject: [PATCH 0456/1312] Move regression tests to a correctly numbered folder --- tests/regression/{73-mem-oob => 77-mem-oob}/01-oob-heap-simple.c | 0 tests/regression/{73-mem-oob => 77-mem-oob}/02-oob-stack-simple.c | 0 tests/regression/{73-mem-oob => 77-mem-oob}/03-oob-loop.c | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename tests/regression/{73-mem-oob => 77-mem-oob}/01-oob-heap-simple.c (100%) rename tests/regression/{73-mem-oob => 77-mem-oob}/02-oob-stack-simple.c (100%) rename tests/regression/{73-mem-oob => 77-mem-oob}/03-oob-loop.c (100%) diff --git a/tests/regression/73-mem-oob/01-oob-heap-simple.c b/tests/regression/77-mem-oob/01-oob-heap-simple.c similarity index 100% rename from tests/regression/73-mem-oob/01-oob-heap-simple.c rename to tests/regression/77-mem-oob/01-oob-heap-simple.c diff --git a/tests/regression/73-mem-oob/02-oob-stack-simple.c b/tests/regression/77-mem-oob/02-oob-stack-simple.c similarity index 100% rename from tests/regression/73-mem-oob/02-oob-stack-simple.c rename to tests/regression/77-mem-oob/02-oob-stack-simple.c diff --git a/tests/regression/73-mem-oob/03-oob-loop.c b/tests/regression/77-mem-oob/03-oob-loop.c similarity index 100% rename from tests/regression/73-mem-oob/03-oob-loop.c rename to tests/regression/77-mem-oob/03-oob-loop.c From 84bd2cc7331e949607d5b48c3b0766d4c436e20e Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 20 Aug 2023 19:13:37 +0200 Subject: [PATCH 0457/1312] Add comments to 77/03 test case --- tests/regression/77-mem-oob/03-oob-loop.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/regression/77-mem-oob/03-oob-loop.c b/tests/regression/77-mem-oob/03-oob-loop.c index c800597757..502a4cc0e2 100644 --- a/tests/regression/77-mem-oob/03-oob-loop.c +++ b/tests/regression/77-mem-oob/03-oob-loop.c @@ -9,8 +9,10 @@ int main(int argc, char const *argv[]) { ptr++; } - printf("%s", *ptr); //WARN - free(ptr); //WARN + //TODO: We cannot currently detect OOB memory accesses happening due to loops like the one above + // => Both lines below can't have WARNs for now + printf("%s", *ptr); //NOWARN + free(ptr); //NOWARN return 0; } From fe1660e0e74d6934aec68d590ade46d29b171d0c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 21 Aug 2023 10:24:32 +0300 Subject: [PATCH 0458/1312] Remove RaceAnalysis joke --- src/analyses/raceAnalysis.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index f617df2048..462834e2f4 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -3,7 +3,7 @@ open GoblintCil open Analyses -(** Data race analysis with tries and hookers. +(** Data race analysis with tries for offsets and type-based memory locations for open code. Accesses are to memory locations ({{!Access.Memo} memos}) which consist of a root and offset. {{!Access.MemoRoot} Root} can be: From 00fb4964810643a998fb522cfc4e470b07a21ec8 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 21 Aug 2023 10:30:09 +0300 Subject: [PATCH 0459/1312] Add C declarations to race analysis documentation --- src/analyses/raceAnalysis.ml | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index 462834e2f4..d452b6b131 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -41,7 +41,20 @@ open Analyses This requires an implementation hack to still eagerly do outer distribution, but only of empty access sets. It ensures that corresponding trie nodes exist for traversal later. *) -(** Example structure of related memos for race checking: +(** Given C declarations: + {@c[ + struct S { + int f; + }; + + struct T { + struct S s; + }; + + struct T t; + ]} + + Example structure of related memos for race checking: {v (int) (S) (T) \ / \ / \ @@ -54,9 +67,7 @@ open Analyses where: - [(int)] is a type-based memo root for the primitive [int] type; - [(S)] and [(T)] are short for [(struct S)] and [(struct T)], which are type-based memo roots; - - [f] is a field of [S] and [s] is a field of [T]; - - [t] is a global variable of type [T]. - - prefix relations are indicated by [/]; + - prefix relations are indicated by [/], so access paths run diagonally from top-right to bottom-left; - type suffix relations are indicated by [\ ]. Prefix races: From 0329b30ad74cce93b55d4998f169faf108ec3eca Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 21 Aug 2023 10:44:05 +0300 Subject: [PATCH 0460/1312] Make race analysis documentation example races list complete --- src/analyses/raceAnalysis.ml | 46 ++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index d452b6b131..4ca1094b4a 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -70,28 +70,60 @@ open Analyses - prefix relations are indicated by [/], so access paths run diagonally from top-right to bottom-left; - type suffix relations are indicated by [\ ]. - Prefix races: + All same-node races: + - Race between [t.s.f] and [t.s.f] is checked/reported at [t.s.f]. + - Race between [t.s] and [t.s] is checked/reported at [t.s]. + - Race between [t] and [t] is checked/reported at [t]. + - Race between [(T).s.f] and [(T).s.f] is checked/reported at [(T).s.f]. + - Race between [(T).s] and [(T).s] is checked/reported at [(T).s]. + - Race between [(T)] and [(T)] is checked/reported at [(T)]. + - Race between [(S).f] and [(S).f] is checked/reported at [(S).f]. + - Race between [(S)] and [(S)] is checked/reported at [(S)]. + - Race between [(int)] and [(int)] is checked/reported at [(int)]. + + All prefix races: - Race between [t.s.f] and [t.s] is checked/reported at [t.s.f]. - Race between [t.s.f] and [t] is checked/reported at [t.s.f]. - Race between [t.s] and [t] is checked/reported at [t.s]. - - Race between [t] and [t] is checked/reported at [t]. + - Race between [(T).s.f] and [(T).s] is checked/reported at [(T).s.f]. + - Race between [(T).s.f] and [(T)] is checked/reported at [(T).s.f]. + - Race between [(T).s] and [(T)] is checked/reported at [(T).s]. - Race between [(S).f] and [(S)] is checked/reported at [(S).f]. - Type suffix races: + All type suffix races: - Race between [t.s.f] and [(T).s.f] is checked/reported at [t.s.f]. - Race between [t.s.f] and [(S).f] is checked/reported at [t.s.f]. - Race between [t.s.f] and [(int)] is checked/reported at [t.s.f]. + - Race between [(T).s.f] and [(S).f] is checked/reported at [(T).s.f]. + - Race between [(T).s.f] and [(int)] is checked/reported at [(T).s.f]. - Race between [(S).f] and [(int)] is checked/reported at [(S).f]. + - Race between [t.s] and [(T).s] is checked/reported at [t.s]. + - Race between [t.s] and [(S)] is checked/reported at [t.s]. + - Race between [(T).s] and [(S)] is checked/reported at [(T).s]. + - Race between [t] and [(T)] is checked/reported at [t]. - Type suffix prefix races: + All type suffix prefix races: - Race between [t.s.f] and [(T).s] is checked/reported at [t.s.f]. - Race between [t.s.f] and [(T)] is checked/reported at [t.s.f]. - Race between [t.s.f] and [(S)] is checked/reported at [t.s.f]. - Race between [(T).s.f] and [(S)] is checked/reported at [(T).s.f]. - - Prefix-type suffix races: + - Race between [t.s] and [(T)] is checked/reported at [t.s]. + + All prefix-type suffix races: + - Race between [t.s] and [(T).s.f] is checked/reported at [t.s.f]. + - Race between [t.s] and [(S).f] is checked/reported at [t.s.f]. + - Race between [t.s] and [(int)] is checked/reported at [t.s.f]. + - Race between [t] and [(T).s.f] is checked/reported at [t.s.f]. + - Race between [t] and [(S).f] is checked/reported at [t.s.f]. + - Race between [t] and [(int)] is checked/reported at [t.s.f]. + - Race between [t] and [(T).s] is checked/reported at [t.s]. + - Race between [t] and [(S)] is checked/reported at [t.s]. - Race between [(T).s] and [(S).f] is checked/reported at [(T).s.f]. - - Race between [t] and [(S).f] is checked/reported at [t.s.f]. *) + - Race between [(T).s] and [(int)] is checked/reported at [(T).s.f]. + - Race between [(T)] and [(S).f] is checked/reported at [(T).s.f]. + - Race between [(T)] and [(int)] is checked/reported at [(T).s.f]. + - Race between [(T)] and [(S)] is checked/reported at [(T).s]. + - Race between [(S)] and [(int)] is checked/reported at [(S).f]. *) (** Data race analyzer without base --- this is the new standard *) From 039edfe67d1fe91cae4099407f9416dccb422f24 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Mon, 21 Aug 2023 12:41:25 +0300 Subject: [PATCH 0461/1312] Move fnmatch to posix group --- src/analyses/libraryFunctions.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 42fa55a2b7..6cb9675e9f 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -118,7 +118,6 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("_setjmp", special [__ "env" [w]] @@ fun env -> Setjmp { env }); (* only has one underscore *) ("setjmp", special [__ "env" [w]] @@ fun env -> Setjmp { env }); ("longjmp", special [__ "env" [r]; __ "value" []] @@ fun env value -> Longjmp { env; value }); - ("fnmatch", unknown [drop "pattern" [r]; drop "string" [r]; drop "flags" []]); ] (** C POSIX library functions. @@ -281,6 +280,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__sigsetjmp", special [__ "env" [w]; drop "savesigs" []] @@ fun env -> Setjmp { env }); (* has two underscores *) ("sigsetjmp", special [__ "env" [w]; drop "savesigs" []] @@ fun env -> Setjmp { env }); ("siglongjmp", special [__ "env" [r]; __ "value" []] @@ fun env value -> Longjmp { env; value }); + ("fnmatch", unknown [drop "pattern" [r]; drop "string" [r]; drop "flags" []]); ] (** Pthread functions. *) From c416816f05b46b6a52e00f92ec4a283ec4901f80 Mon Sep 17 00:00:00 2001 From: Karoliine Holter <44437975+karoliineh@users.noreply.github.com> Date: Mon, 21 Aug 2023 12:42:10 +0300 Subject: [PATCH 0462/1312] Fix pthread_setaffinity_np and pthread_getaffinity_np Co-authored-by: Simmo Saan --- src/analyses/libraryFunctions.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 6cb9675e9f..deafddb473 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -346,8 +346,8 @@ let pthread_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("pthread_mutexattr_destroy", unknown [drop "attr" [f]]); ("pthread_attr_setschedparam", unknown [drop "attr" [r; w]; drop "param" [r]]); ("sem_timedwait", unknown [drop "sem" [r]; drop "abs_timeout" [r]]); (* no write accesses to sem because sync primitive itself has no race *) - ("pthread_setaffinity_np", unknown [drop "thread" [w]; drop "cpusetsize" []; drop "cpuset" [r; w]]); - ("pthread_getaffinity_np", unknown [drop "thread" [w]; drop "cpusetsize" []; drop "cpuset" [r; w]]); + ("pthread_setaffinity_np", unknown [drop "thread" []; drop "cpusetsize" []; drop "cpuset" [r]]); + ("pthread_getaffinity_np", unknown [drop "thread" []; drop "cpusetsize" []; drop "cpuset" [w]]); ] (** GCC builtin functions. From a8a8899d94f237cb76825b8a78f508fa50d3f450 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 21 Aug 2023 17:43:25 +0300 Subject: [PATCH 0463/1312] Add TODO for ana.race.direct-arithmetic in lazy outer distribution --- src/analyses/raceAnalysis.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index 4ca1094b4a..d6621154b7 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -241,6 +241,7 @@ struct match root, offset with | `Var v, _ -> Some (`Type (Cil.typeSig v.vtype), offset) (* global.foo.bar -> (struct S).foo.bar *) (* TODO: Alloc variables void type *) | _, `NoOffset -> None (* primitive type *) + (* TODO: should handle ana.race.direct-arithmetic special case here? *) | _, `Field (f, offset') -> Some (`Type (Cil.typeSig f.ftype), offset') (* (struct S).foo.bar -> (struct T).bar *) | `Type (TSArray (ts, _, _)), `Index ((), offset') -> Some (`Type ts, offset') (* (int[])[*] -> int *) | _, `Index ((), offset') -> None (* TODO: why indexing on non-array? *) From 6ed476e3216d808fb6e6a16a6b15ca19c93efbfd Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Mon, 21 Aug 2023 18:17:46 +0200 Subject: [PATCH 0464/1312] Warn for function args in enter and not in combine_assign --- src/analyses/memOutOfBounds.ml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 655596d3ec..cfab5c8b73 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -174,9 +174,12 @@ struct List.iter (fun arg -> check_exp_for_oob_access ctx arg) arglist; ctx.local + let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = + List.iter (fun arg -> check_exp_for_oob_access ctx arg) args; + [ctx.local, ctx.local] + let combine_assign ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (callee_local:D.t) (f_ask:Queries.ask) : D.t = Option.iter (fun x -> check_lval_for_oob_access ctx x) lval; - List.iter (fun arg -> check_exp_for_oob_access ctx arg) args; ctx.local let startstate v = () From 24c07c4af851ce6856f0551aca995e2c7edf5049 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 11:34:40 +0300 Subject: [PATCH 0465/1312] Add MayPointTo query with address domain Co-authored-by: Simmo Saan --- src/analyses/base.ml | 6 ++++++ src/domains/queries.ml | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index a213170ba2..a08e4a14f4 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1289,6 +1289,12 @@ struct | Bot -> Queries.Result.bot q (* TODO: remove *) | _ -> Queries.Result.top q end + | Q.MayPointToA e -> begin + match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local e with + | Address a -> a + | Bot -> Queries.Result.bot q (* TODO: remove *) + | _ -> Queries.Result.top q + end | Q.EvalThread e -> begin let v = eval_rv (Analyses.ask_of_ctx ctx) ctx.global ctx.local e in (* ignore (Pretty.eprintf "evalthread %a (%a): %a" d_exp e d_plainexp e VD.pretty v); *) diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 214fcf1384..fcbaa4cba8 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -34,6 +34,7 @@ module FlatYojson = Lattice.Flat (Printable.Yojson) (struct module SD = Basetype.Strings module VD = ValueDomain.Compound +module AD = ValueDomain.AD module MayBool = BoolDomain.MayBool module MustBool = BoolDomain.MustBool @@ -72,6 +73,7 @@ type invariant_context = Invariant.context = { type _ t = | EqualSet: exp -> ES.t t | MayPointTo: exp -> LS.t t + | MayPointToA: exp -> AD.t t | ReachableFrom: exp -> LS.t t | ReachableUkTypes: exp -> TS.t t | Regions: exp -> LS.t t @@ -141,6 +143,7 @@ struct | EqualSet _ -> (module ES) | CondVars _ -> (module ES) | MayPointTo _ -> (module LS) + | MayPointToA _ -> (module AD) | ReachableFrom _ -> (module LS) | Regions _ -> (module LS) | MustLockset -> (module LS) @@ -205,6 +208,7 @@ struct | EqualSet _ -> ES.top () | CondVars _ -> ES.top () | MayPointTo _ -> LS.top () + | MayPointToA _ -> AD.top () | ReachableFrom _ -> LS.top () | Regions _ -> LS.top () | MustLockset -> LS.top () @@ -265,6 +269,7 @@ struct let order = function | Any (EqualSet _) -> 0 | Any (MayPointTo _) -> 1 + | Any (MayPointToA _) -> 999 | Any (ReachableFrom _) -> 2 | Any (ReachableUkTypes _) -> 3 | Any (Regions _) -> 4 @@ -321,6 +326,7 @@ struct match a, b with | Any (EqualSet e1), Any (EqualSet e2) -> CilType.Exp.compare e1 e2 | Any (MayPointTo e1), Any (MayPointTo e2) -> CilType.Exp.compare e1 e2 + | Any (MayPointToA e1), Any (MayPointToA e2) -> CilType.Exp.compare e1 e2 | Any (ReachableFrom e1), Any (ReachableFrom e2) -> CilType.Exp.compare e1 e2 | Any (ReachableUkTypes e1), Any (ReachableUkTypes e2) -> CilType.Exp.compare e1 e2 | Any (Regions e1), Any (Regions e2) -> CilType.Exp.compare e1 e2 @@ -366,6 +372,7 @@ struct let rec hash_arg = function | Any (EqualSet e) -> CilType.Exp.hash e | Any (MayPointTo e) -> CilType.Exp.hash e + | Any (MayPointToA e) -> CilType.Exp.hash e | Any (ReachableFrom e) -> CilType.Exp.hash e | Any (ReachableUkTypes e) -> CilType.Exp.hash e | Any (Regions e) -> CilType.Exp.hash e @@ -408,6 +415,7 @@ struct let rec pretty () = function | Any (EqualSet e) -> Pretty.dprintf "EqualSet %a" CilType.Exp.pretty e | Any (MayPointTo e) -> Pretty.dprintf "MayPointTo %a" CilType.Exp.pretty e + | Any (MayPointToA e) -> Pretty.dprintf "MayPointToA %a" CilType.Exp.pretty e | Any (ReachableFrom e) -> Pretty.dprintf "ReachableFrom %a" CilType.Exp.pretty e | Any (ReachableUkTypes e) -> Pretty.dprintf "ReachableUkTypes %a" CilType.Exp.pretty e | Any (Regions e) -> Pretty.dprintf "Regions %a" CilType.Exp.pretty e From 0ecf5755fc758d46620c70b0f8fddf7b2ee2c5d1 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 11:41:08 +0300 Subject: [PATCH 0466/1312] Use MayPointToA in mutexEventAnalysis Co-authored-by: Simmo Saan --- src/analyses/mutexEventsAnalysis.ml | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/src/analyses/mutexEventsAnalysis.ml b/src/analyses/mutexEventsAnalysis.ml index 2c57fa360b..e5839741b2 100644 --- a/src/analyses/mutexEventsAnalysis.ml +++ b/src/analyses/mutexEventsAnalysis.ml @@ -18,24 +18,12 @@ struct include UnitAnalysis.Spec let name () = "mutexEvents" - (* TODO: Use AddressDomain for queries *) - let eval_exp_addr (a: Queries.ask) exp = - let gather_addr (v,o) b = ValueDomain.Addr.of_mval (v, Addr.Offs.of_exp o) :: b in - match a.f (Queries.MayPointTo exp) with - | a when Queries.LS.is_top a -> - [Addr.UnknownPtr] - | a -> - let top_elt = (dummyFunDec.svar, `NoOffset) in - let addrs = Queries.LS.fold gather_addr (Queries.LS.remove top_elt a) [] in - if Queries.LS.mem top_elt a then - Addr.UnknownPtr :: addrs - else - addrs + let eval_exp_addr (a: Queries.ask) exp = a.f (Queries.MayPointToA exp) let lock ctx rw may_fail nonzero_return_when_aquired a lv arg = match lv with | None -> - List.iter (fun e -> + Queries.AD.iter (fun e -> ctx.split () [Events.Lock (e, rw)] ) (eval_exp_addr a arg); if may_fail then @@ -43,7 +31,7 @@ struct raise Analyses.Deadcode | Some lv -> let sb = Events.SplitBranch (Lval lv, nonzero_return_when_aquired) in - List.iter (fun e -> + Queries.AD.iter (fun e -> ctx.split () [sb; Events.Lock (e, rw)]; ) (eval_exp_addr a arg); if may_fail then ( @@ -67,7 +55,7 @@ struct let special (ctx: (unit, _, _, _) ctx) lv f arglist : D.t = let remove_rw x = x in let unlock arg remove_fn = - List.iter (fun e -> + Queries.AD.iter (fun e -> ctx.split () [Events.Unlock (remove_fn e)] ) (eval_exp_addr (Analyses.ask_of_ctx ctx) arg); raise Analyses.Deadcode @@ -83,7 +71,7 @@ struct (* mutex is unlocked while waiting but relocked when returns *) (* emit unlock-lock events for privatization *) let ms = eval_exp_addr (Analyses.ask_of_ctx ctx) m_arg in - List.iter (fun m -> + Queries.AD.iter (fun m -> (* unlock-lock each possible mutex as a split to be dependent *) (* otherwise may-point-to {a, b} might unlock a, but relock b *) ctx.split () [Events.Unlock m; Events.Lock (m, true)]; From fb43b5fff6197013f038b529dfbd4a8ef742b5bc Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 11:50:27 +0300 Subject: [PATCH 0467/1312] Use MayPointToA in threadEscape Co-authored-by: Simmo Saan --- src/analyses/threadEscape.ml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/analyses/threadEscape.ml b/src/analyses/threadEscape.ml index 8a14f4102e..e8499f2fbf 100644 --- a/src/analyses/threadEscape.ml +++ b/src/analyses/threadEscape.ml @@ -4,6 +4,7 @@ open GoblintCil open Analyses module M = Messages +module AD = Queries.AD let has_escaped (ask: Queries.ask) (v: varinfo): bool = assert (not v.vglob); @@ -35,13 +36,17 @@ struct D.empty () let mpt (ask: Queries.ask) e: D.t = - match ask.f (Queries.MayPointTo e) with - | a when not (Queries.LS.is_top a) -> - let to_extra (v,o) set = D.add v set in - Queries.LS.fold to_extra (Queries.LS.remove (dummyFunDec.svar, `NoOffset) a) (D.empty ()) + match ask.f (Queries.MayPointToA e) with + | a when not (AD.is_top a) -> + let to_extra addr set = + match addr with + | AD.Addr.Addr (v,_) -> D.add v set + | _ -> set + in + AD.fold to_extra (AD.remove UnknownPtr a) (D.empty ()) (* Ignore soundness warnings, as invalidation proper will raise them. *) | a -> - if M.tracing then M.tracel "escape" "mpt %a: %a\n" d_exp e Queries.LS.pretty a; + if M.tracing then M.tracel "escape" "mpt %a: %a\n" d_exp e AD.pretty a; D.empty () let thread_id ctx = From e1263bc86e179b9daeb92b82762ed867638aba9f Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 12:15:51 +0300 Subject: [PATCH 0468/1312] Use MayPointToA in memLeak --- src/analyses/memLeak.ml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index 99df5695a7..026a526439 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -49,14 +49,15 @@ struct | _ -> state end | Free ptr -> - begin match ctx.ask (Queries.MayPointTo ptr) with - | a when not (Queries.LS.is_top a) && not (Queries.LS.mem (dummyFunDec.svar, `NoOffset) a) && Queries.LS.cardinal a = 1 -> + begin match ctx.ask (Queries.MayPointToA ptr) with + | a when not (Queries.AD.is_top a) && not (Queries.AD.mem UnknownPtr a) && Queries.AD.cardinal a = 1 -> (* Note: Need to always set "ana.malloc.unique_address_count" to a value > 0 *) let unique_pointed_to_heap_vars = - Queries.LS.filter (fun (v, _) -> ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsMultiple v)) a - |> Queries.LS.elements - |> List.map fst - |> D.of_list + Queries.AD.fold (fun addr s -> + match addr with + | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsMultiple v) -> D.add v s + | _ -> s + ) a (D.empty ()) in D.diff state unique_pointed_to_heap_vars | _ -> state From eb951b669d2295b7d8552e85fcc7bf88e53d69e9 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 12:27:00 +0300 Subject: [PATCH 0469/1312] Use MayPointToA in mutexTypeAnalysis --- src/analyses/mutexTypeAnalysis.ml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/analyses/mutexTypeAnalysis.ml b/src/analyses/mutexTypeAnalysis.ml index 00e49260b4..815b3fdb67 100644 --- a/src/analyses/mutexTypeAnalysis.ml +++ b/src/analyses/mutexTypeAnalysis.ml @@ -54,9 +54,13 @@ struct match desc.special arglist with | MutexInit {mutex = mutex; attr = attr} -> let attr = ctx.ask (Queries.EvalMutexAttr attr) in - let mutexes = ctx.ask (Queries.MayPointTo mutex) in + let mutexes = ctx.ask (Queries.MayPointToA mutex) in (* It is correct to iter over these sets here, as mutexes need to be intialized before being used, and an analysis that detects usage before initialization is a different analysis. *) - Queries.LS.iter (function (v, o) -> ctx.sideg (v,O.of_offs o) attr) mutexes; + Queries.AD.iter (function addr -> + match addr with + | Queries.AD.Addr.Addr (v,o) -> ctx.sideg (v,O.of_offs o) attr + | _ -> () + ) mutexes; ctx.local | _ -> ctx.local From 4bd38f5c55fe4282c8f19322bddf98732c582684 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 12:45:19 +0300 Subject: [PATCH 0470/1312] Use MayPointToA in pthreadSignals --- src/analyses/pthreadSignals.ml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/analyses/pthreadSignals.ml b/src/analyses/pthreadSignals.ml index 036d1bd2c6..f5d0e2c9d0 100644 --- a/src/analyses/pthreadSignals.ml +++ b/src/analyses/pthreadSignals.ml @@ -17,13 +17,7 @@ struct module C = MustSignals module G = SetDomain.ToppedSet (MHP) (struct let topname = "All Threads" end) - (* TODO: Use AddressDomain for queries *) - let eval_exp_addr (a: Queries.ask) exp = - let gather_addr (v,o) b = ValueDomain.Addr.of_mval (v, ValueDomain.Addr.Offs.of_exp o) :: b in - match a.f (Queries.MayPointTo exp) with - | a when not (Queries.LS.is_top a) && not (Queries.LS.mem (dummyFunDec.svar,`NoOffset) a) -> - Queries.LS.fold gather_addr (Queries.LS.remove (dummyFunDec.svar, `NoOffset) a) [] - | _ -> [] + let eval_exp_addr (a: Queries.ask) exp = Queries.AD.elements (a.f (Queries.MayPointToA exp)) let possible_vinfos a cv_arg = List.filter_map ValueDomain.Addr.to_var_may (eval_exp_addr a cv_arg) From 68f5bb2ddae927199811ac13685237c90eaa67c3 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 12:59:33 +0300 Subject: [PATCH 0471/1312] Use MayPointToA in useAfterFree --- src/analyses/useAfterFree.ml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index c3aebc985e..52935a0c12 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -82,16 +82,18 @@ struct | Var _ -> Lval lval | Mem _ -> mkAddrOf lval (* Take the lval's address if its lhost is of the form *p, where p is a ptr *) in - match ctx.ask (Queries.MayPointTo lval_to_query) with - | a when not (Queries.LS.is_top a) && not (Queries.LS.mem (dummyFunDec.svar, `NoOffset) a) -> + match ctx.ask (Queries.MayPointToA lval_to_query) with + | a when not (Queries.AD.is_top a) && not (Queries.AD.mem UnknownPtr a) -> let warn_for_heap_var var = if D.mem var state then M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "lval (%s) in \"%s\" points to a maybe freed memory region" var.vname transfer_fn_name in let pointed_to_heap_vars = - Queries.LS.elements a - |> List.map fst - |> List.filter (fun var -> ctx.ask (Queries.IsHeapVar var)) + Queries.AD.fold (fun addr l -> + match addr with + | Queries.AD.Addr.Addr (var, _) when ctx.ask (Queries.IsHeapVar var) -> var :: l + | _ -> l + ) a [] in List.iter warn_for_heap_var pointed_to_heap_vars; (* Warn for all heap vars that the lval possibly points to *) (* Warn for a potential multi-threaded UAF for all heap vars that the lval possibly points to *) From 940a77119cc2834ce4c1d5dd72c6d98dfd41080f Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 13:33:34 +0300 Subject: [PATCH 0472/1312] Use MayPointToA in relationAnalysis --- src/analyses/apron/relationAnalysis.apron.ml | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index c5ad08ec76..dcc1a2d10d 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -157,15 +157,14 @@ struct {st' with rel = rel''} ) | (Mem v, NoOffset) -> - (let r = ask.f (Queries.MayPointTo v) in - match r with - | `Top -> - st - | `Lifted s -> - let lvals = Queries.LS.elements r in - let ass' = List.map (fun lv -> assign_to_global_wrapper ask getg sideg st (Mval.Exp.to_cil lv) f) lvals in - List.fold_right D.join ass' (D.bot ()) - ) + begin match ask.f (Queries.MayPointToA v) with + | a when Queries.AD.is_top a -> + st + | a -> + let lvals = List.filter_map Queries.AD.Addr.to_mval (Queries.AD.elements a) in + let ass' = List.map (fun lv -> assign_to_global_wrapper ask getg sideg st (ValueDomain.Addr.Mval.to_cil lv) f) lvals in + List.fold_right D.join ass' (D.bot ()) + end (* Ignoring all other assigns *) | _ -> st From ca39dafa55bdd4c675ece1857674313ad63cd170 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 13:36:47 +0300 Subject: [PATCH 0473/1312] Add mutex addr to unlock warning Co-authored-by: Simmo Saan --- src/analyses/mutexAnalysis.ml | 2 +- tests/regression/06-symbeq/21-mult_accs_rc.t | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 5a3aeb55ce..154f7ba183 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -155,7 +155,7 @@ struct let remove' ctx ~warn l = let s, m = ctx.local in let rm s = Lockset.remove (l, true) (Lockset.remove (l, false) s) in - if warn && (not (Lockset.mem (l,true) s || Lockset.mem (l,false) s)) then M.warn "unlocking mutex which may not be held"; + if warn && (not (Lockset.mem (l,true) s || Lockset.mem (l,false) s)) then M.warn "unlocking mutex (%a) which may not be held" Addr.pretty l; match Addr.to_mval l with | Some mval when MutexTypeAnalysis.must_be_recursive ctx mval -> let m',rmed = Multiplicity.decrement l m in diff --git a/tests/regression/06-symbeq/21-mult_accs_rc.t b/tests/regression/06-symbeq/21-mult_accs_rc.t index 7a4439141d..9b02dcde76 100644 --- a/tests/regression/06-symbeq/21-mult_accs_rc.t +++ b/tests/regression/06-symbeq/21-mult_accs_rc.t @@ -10,6 +10,7 @@ Disable info messages because race summary contains (safe) memory location count [Warning][Race] Memory location (struct s).data (race with conf. 100): write with [mhp:{tid=[main, t_fun@21-mult_accs_rc.c:31:3-31:37#top]}, thread:[main, t_fun@21-mult_accs_rc.c:31:3-31:37#top]] (conf. 100) (exp: & s->data) (21-mult_accs_rc.c:16:3-16:14) write with [symblock:{p-lock:*.mutex}, mhp:{tid=[main]; created={[main, t_fun@21-mult_accs_rc.c:31:3-31:37#top]}}, thread:[main]] (conf. 100) (exp: & *d) (21-mult_accs_rc.c:34:3-34:9) + [Warning][Unknown] unlocking mutex (NULL) which may not be held (21-mult_accs_rc.c:35:3-35:26) [Warning][Unknown] unlocking unknown mutex which may not be held (21-mult_accs_rc.c:35:3-35:26) [Error][Imprecise][Unsound] Function definition missing for get_s (21-mult_accs_rc.c:13:3-13:14) [Error][Imprecise][Unsound] Function definition missing for get_s (21-mult_accs_rc.c:15:3-15:14) @@ -24,6 +25,7 @@ Disable info messages because race summary contains (safe) memory location count [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (21-mult_accs_rc.c:34:3-34:9) [Success][Race] Memory location (struct s).data (safe): write with [mhp:{tid=[main, t_fun@21-mult_accs_rc.c:31:3-31:37#top]}, thread:[main, t_fun@21-mult_accs_rc.c:31:3-31:37#top]] (conf. 100) (exp: & s->data) (21-mult_accs_rc.c:16:3-16:14) + [Warning][Unknown] unlocking mutex (NULL) which may not be held (21-mult_accs_rc.c:35:3-35:26) [Warning][Unknown] unlocking unknown mutex which may not be held (21-mult_accs_rc.c:35:3-35:26) [Error][Imprecise][Unsound] Function definition missing for get_s (21-mult_accs_rc.c:13:3-13:14) [Error][Imprecise][Unsound] Function definition missing for get_s (21-mult_accs_rc.c:15:3-15:14) From 158a4505c4727fd64c551ff7d6eff804dba85304 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 14:13:23 +0300 Subject: [PATCH 0474/1312] Use MayPointToA in relationAnalysis cont. --- src/analyses/apron/relationAnalysis.apron.ml | 26 ++++++++++++-------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index dcc1a2d10d..5052631d72 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -216,12 +216,16 @@ struct | CastE (t,e) -> CastE (t, inner e) | Lval (Var v, off) -> Lval (Var v, off) | Lval (Mem e, NoOffset) -> - (match ask (Queries.MayPointTo e) with - | a when not (Queries.LS.is_top a || Queries.LS.mem (dummyFunDec.svar, `NoOffset) a) && (Queries.LS.cardinal a) = 1 -> - Mval.Exp.to_cil_exp (Queries.LS.choose a) - (* It would be possible to do better here, exploiting e.g. that the things pointed to are known to be equal *) - (* see: https://github.com/goblint/analyzer/pull/742#discussion_r879099745 *) - | _ -> Lval (Mem e, NoOffset)) + begin match ask (Queries.MayPointToA e) with + | a when not (Queries.AD.is_top a) && (Queries.AD.cardinal a) = 1 -> + begin match Queries.AD.Addr.to_mval (Queries.AD.choose a) with + | Some mval -> ValueDomain.Addr.Mval.to_cil_exp mval + | None -> Lval (Mem e, NoOffset) + end + (* It would be possible to do better here, exploiting e.g. that the things pointed to are known to be equal *) + (* see: https://github.com/goblint/analyzer/pull/742#discussion_r879099745 *) + | _ -> Lval (Mem e, NoOffset) + end | e -> e (* TODO: Potentially recurse further? *) in inner e @@ -514,10 +518,12 @@ struct | None -> st) | _, _ -> let lvallist e = - let s = ask.f (Queries.MayPointTo e) in - match s with - | `Top -> [] - | `Lifted _ -> List.map Mval.Exp.to_cil (Queries.LS.elements s) + match ask.f (Queries.MayPointToA e) with + | a when Queries.AD.is_top a -> [] + | a -> + Queries.AD.elements a + |> List.filter_map Queries.AD.Addr.to_mval + |> List.map ValueDomain.Addr.Mval.to_cil in let shallow_addrs = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = false } args in let deep_addrs = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = true } args in From 4bfa786dee3df950fadfb2c8ad41d0bd9205799a Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 14:13:59 +0300 Subject: [PATCH 0475/1312] Add TODO for removing fold from memLeak --- src/analyses/memLeak.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index 026a526439..ce0c047d3e 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -52,7 +52,7 @@ struct begin match ctx.ask (Queries.MayPointToA ptr) with | a when not (Queries.AD.is_top a) && not (Queries.AD.mem UnknownPtr a) && Queries.AD.cardinal a = 1 -> (* Note: Need to always set "ana.malloc.unique_address_count" to a value > 0 *) - let unique_pointed_to_heap_vars = + let unique_pointed_to_heap_vars = (* TODO: no need for fold due to a being singleton *) Queries.AD.fold (fun addr s -> match addr with | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsMultiple v) -> D.add v s From 71789d172ab6b7f4e3e92129332ecb05afe9232d Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 14:20:34 +0300 Subject: [PATCH 0476/1312] Use MayPointToA in useAfterFree cont. --- src/analyses/useAfterFree.ml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index 52935a0c12..d5d2ba0266 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -83,7 +83,7 @@ struct | Mem _ -> mkAddrOf lval (* Take the lval's address if its lhost is of the form *p, where p is a ptr *) in match ctx.ask (Queries.MayPointToA lval_to_query) with - | a when not (Queries.AD.is_top a) && not (Queries.AD.mem UnknownPtr a) -> + | a when not (Queries.AD.is_top a) -> let warn_for_heap_var var = if D.mem var state then M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "lval (%s) in \"%s\" points to a maybe freed memory region" var.vname transfer_fn_name @@ -93,7 +93,7 @@ struct match addr with | Queries.AD.Addr.Addr (var, _) when ctx.ask (Queries.IsHeapVar var) -> var :: l | _ -> l - ) a [] + ) a [] in List.iter warn_for_heap_var pointed_to_heap_vars; (* Warn for all heap vars that the lval possibly points to *) (* Warn for a potential multi-threaded UAF for all heap vars that the lval possibly points to *) @@ -180,13 +180,14 @@ struct let desc = LibraryFunctions.find f in match desc.special arglist with | Free ptr -> - begin match ctx.ask (Queries.MayPointTo ptr) with - | a when not (Queries.LS.is_top a) && not (Queries.LS.mem (dummyFunDec.svar, `NoOffset) a) -> + begin match ctx.ask (Queries.MayPointToA ptr) with + | a when not (Queries.AD.is_top a) -> let pointed_to_heap_vars = - Queries.LS.elements a - |> List.map fst - |> List.filter (fun var -> ctx.ask (Queries.IsHeapVar var)) - |> D.of_list + Queries.AD.fold (fun addr s -> + match addr with + | Queries.AD.Addr.Addr (var, _) when ctx.ask (Queries.IsHeapVar var) -> D.add var s + | _ -> s + ) a (D.empty ()) in (* Side-effect the tid that's freeing all the heap vars collected here *) side_effect_mem_free ctx pointed_to_heap_vars (get_current_threadid ctx); From 3cbd31f59f3e8914ef418610cdd13dbd829cb1e9 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 14:54:59 +0300 Subject: [PATCH 0477/1312] Use MayPointToA in varEq --- src/analyses/varEq.ml | 75 ++++++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 33 deletions(-) diff --git a/src/analyses/varEq.ml b/src/analyses/varEq.ml index 99307d5d37..90ea4e5eae 100644 --- a/src/analyses/varEq.ml +++ b/src/analyses/varEq.ml @@ -137,7 +137,7 @@ struct (* TODO: why unused? how different from below? *) let may_change_pt ask (b:exp) (a:exp) : bool = - let pt e = ask (Queries.MayPointTo e) in + let pt e = ask (Queries.MayPointToA e) in let rec lval_may_change_pt a bl : bool = let rec may_change_pt_offset o = match o with @@ -175,7 +175,7 @@ struct let may_change (ask: Queries.ask) (b:exp) (a:exp) : bool = (*b should be an address of something that changes*) - let pt e = ask.f (Queries.MayPointTo e) in + let pt e = ask.f (Queries.MayPointToA e) in let bls = pt b in let bt = match unrollTypeDeep (Cilfacade.typeOf b) with @@ -208,26 +208,26 @@ struct | at -> at in bt = voidType || (isIntegralType at && isIntegralType bt) || (deref && typ_equal (TPtr (at,[]) ) bt) || typ_equal at bt || - match a with - | Const _ - | SizeOf _ - | SizeOfE _ - | SizeOfStr _ - | AlignOf _ - | AlignOfE _ - | AddrOfLabel _ -> false (* TODO: some may contain exps? *) - | UnOp (_,e,_) - | Real e - | Imag e -> type_may_change_t deref e - | BinOp (_,e1,e2,_) -> type_may_change_t deref e1 || type_may_change_t deref e2 - | Lval (Var _,o) - | AddrOf (Var _,o) - | StartOf (Var _,o) -> may_change_t_offset o - | Lval (Mem e,o) -> may_change_t_offset o || type_may_change_t true e - | AddrOf (Mem e,o) -> may_change_t_offset o || type_may_change_t false e - | StartOf (Mem e,o) -> may_change_t_offset o || type_may_change_t false e - | CastE (t,e) -> type_may_change_t deref e - | Question (b, t, f, _) -> type_may_change_t deref b || type_may_change_t deref t || type_may_change_t deref f + match a with + | Const _ + | SizeOf _ + | SizeOfE _ + | SizeOfStr _ + | AlignOf _ + | AlignOfE _ + | AddrOfLabel _ -> false (* TODO: some may contain exps? *) + | UnOp (_,e,_) + | Real e + | Imag e -> type_may_change_t deref e + | BinOp (_,e1,e2,_) -> type_may_change_t deref e1 || type_may_change_t deref e2 + | Lval (Var _,o) + | AddrOf (Var _,o) + | StartOf (Var _,o) -> may_change_t_offset o + | Lval (Mem e,o) -> may_change_t_offset o || type_may_change_t true e + | AddrOf (Mem e,o) -> may_change_t_offset o || type_may_change_t false e + | StartOf (Mem e,o) -> may_change_t_offset o || type_may_change_t false e + | CastE (t,e) -> type_may_change_t deref e + | Question (b, t, f, _) -> type_may_change_t deref b || type_may_change_t deref t || type_may_change_t deref f and lval_may_change_pt a bl : bool = let rec may_change_pt_offset o = @@ -255,18 +255,21 @@ struct | `Index (i1,o), `Index (i2,s) when exp_equal i1 i2 -> oleq o s | _ -> false in - if Queries.LS.is_top als + if Queries.AD.is_top als then false - else Queries.LS.exists (fun (u,s) -> CilType.Varinfo.equal v u && oleq o s) als + else Queries.AD.exists (function + | Addr (u,s) -> CilType.Varinfo.equal v u && oleq o (Addr.Offs.to_exp s) + | _ -> false + ) als in let (als, test) = match addrOfExp a with - | None -> (Queries.LS.bot (), false) + | None -> (Queries.AD.bot (), false) | Some e -> let als = pt e in (als, lval_is_not_disjoint bl als) in - if (Queries.LS.is_top als) || Queries.LS.mem (dummyFunDec.svar, `NoOffset) als + if Queries.AD.is_top als then type_may_change_apt a else test || match a with @@ -292,9 +295,12 @@ struct in let r = if Cil.isConstant b then false - else if Queries.LS.is_top bls || Queries.LS.mem (dummyFunDec.svar, `NoOffset) bls + else if Queries.AD.is_top bls then ((*Messages.warn ~category:Analyzer "No PT-set: switching to types ";*) type_may_change_apt a ) - else Queries.LS.exists (lval_may_change_pt a) bls + else Queries.AD.exists (function + | Addr (v,o) -> lval_may_change_pt a (v, Addr.Offs.to_exp o) + | _ -> false + ) bls in (* if r then (Messages.warn ~category:Analyzer ~msg:("Kill " ^sprint 80 (Exp.pretty () a)^" because of "^sprint 80 (Exp.pretty () b)) (); r) @@ -338,9 +344,12 @@ struct | Lval (Var v,_) -> Some (v.vglob || (ask.f (Queries.IsMultiple v) || BaseUtil.is_global ask v)) | Lval (Mem e, _) -> - begin match ask.f (Queries.MayPointTo e) with - | ls when not (Queries.LS.is_top ls) && not (Queries.LS.mem (dummyFunDec.svar, `NoOffset) ls) -> - Some (Queries.LS.exists (fun (v, _) -> is_global_var ask (Lval (var v)) = Some true) ls) + begin match ask.f (Queries.MayPointToA e) with + | ls when not (Queries.AD.is_top ls) -> + Some (Queries.AD.exists (function + | Addr (v, _) -> is_global_var ask (Lval (var v)) = Some true + | _ -> false + ) ls) | _ -> Some true end | CastE (t,e) -> is_global_var ask e @@ -489,8 +498,8 @@ struct end | ThreadCreate { arg; _ } -> begin match D.is_bot ctx.local with - | true -> raise Analyses.Deadcode - | false -> remove_reachable ~deep:true (Analyses.ask_of_ctx ctx) [arg] ctx.local + | true -> raise Analyses.Deadcode + | false -> remove_reachable ~deep:true (Analyses.ask_of_ctx ctx) [arg] ctx.local end | _ -> unknown_fn ctx lval f args (* query stuff *) From b5f6c4b5e0fe53d66d5ec7043b0c1960376e3c4a Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 15:09:32 +0300 Subject: [PATCH 0478/1312] Use MayPointToA in uninit --- src/analyses/uninit.ml | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/analyses/uninit.ml b/src/analyses/uninit.ml index 850bd677bd..55c9d2052a 100644 --- a/src/analyses/uninit.ml +++ b/src/analyses/uninit.ml @@ -31,10 +31,14 @@ struct (* TODO: Use AddressDomain for queries *) let access_address (ask: Queries.ask) write lv = - match ask.f (Queries.MayPointTo (AddrOf lv)) with - | a when not (Queries.LS.is_top a) -> - let to_extra (v,o) xs = (v, Addr.Offs.of_exp o, write) :: xs in - Queries.LS.fold to_extra a [] + match ask.f (Queries.MayPointToA (AddrOf lv)) with + | a when not (Queries.AD.is_top a) -> + let to_extra addr xs = + match addr with + | Queries.AD.Addr.Addr (v,o) -> (v, o, write) :: xs + | _ -> xs + in + Queries.AD.fold to_extra a [] | _ -> M.info ~category:Unsound "Access to unknown address could be global"; [] @@ -164,10 +168,11 @@ struct let init_vo (v: varinfo) (ofs: lval_offs) : D.t = List.fold_right remove_if_prefix (get_pfx v `NoOffset ofs v.vtype v.vtype) st in - match a.f (Queries.MayPointTo (AddrOf lv)) with - | a when Queries.LS.cardinal a = 1 -> begin - let var, ofs = Queries.LS.choose a in - init_vo var (Addr.Offs.of_exp ofs) + match a.f (Queries.MayPointToA (AddrOf lv)) with + | a when Queries.AD.cardinal a = 1 -> + begin match Queries.AD.Addr.to_mval (Queries.AD.choose a) with + | Some (var, ofs) -> init_vo var ofs + | None -> st end | _ -> st From 4e1cf15f28143ec2e231e791f38f75e25ebd8efe Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 22 Aug 2023 15:10:16 +0300 Subject: [PATCH 0479/1312] Add comment about ana.race.direct-arithmetic --- src/analyses/raceAnalysis.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index d6621154b7..74a98af6be 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -238,10 +238,11 @@ struct (** Get immediate type_suffix memo. *) let type_suffix_memo ((root, offset) : Access.Memo.t) : Access.Memo.t option = + (* No need to make ana.race.direct-arithmetic return None here, + because (int) is empty anyway since Access.add_distribute_outer isn't called. *) match root, offset with | `Var v, _ -> Some (`Type (Cil.typeSig v.vtype), offset) (* global.foo.bar -> (struct S).foo.bar *) (* TODO: Alloc variables void type *) | _, `NoOffset -> None (* primitive type *) - (* TODO: should handle ana.race.direct-arithmetic special case here? *) | _, `Field (f, offset') -> Some (`Type (Cil.typeSig f.ftype), offset') (* (struct S).foo.bar -> (struct T).bar *) | `Type (TSArray (ts, _, _)), `Index ((), offset') -> Some (`Type ts, offset') (* (int[])[*] -> int *) | _, `Index ((), offset') -> None (* TODO: why indexing on non-array? *) From 8937455d739a08a72c04484fc1bdcf4d01ed7324 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 15:19:32 +0300 Subject: [PATCH 0480/1312] Use MayPointToA in spec --- src/analyses/spec.ml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/analyses/spec.ml b/src/analyses/spec.ml index d7328310dd..336b0c82f8 100644 --- a/src/analyses/spec.ml +++ b/src/analyses/spec.ml @@ -204,14 +204,18 @@ struct | _ -> Queries.Result.top q let query_lv ask exp = - match ask (Queries.MayPointTo exp) with - | l when not (Queries.LS.is_top l) -> - Queries.LS.elements l + match ask (Queries.MayPointToA exp) with + | l when not (Queries.AD.is_top l) -> + Queries.AD.elements l | _ -> [] let eval_fv ask exp: varinfo option = match query_lv ask exp with - | [(v,_)] -> Some v + | [addr] -> + begin match addr with + | Queries.AD.Addr.Addr (v,_) -> Some v + | _ -> None + end | _ -> None From e4174431c0cdc358a71bafbff4a926a09af9b0a0 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 15:20:35 +0300 Subject: [PATCH 0481/1312] Use MayPointToA in mallocFresh --- src/analyses/mallocFresh.ml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/analyses/mallocFresh.ml b/src/analyses/mallocFresh.ml index c4a0c035f2..a7c1afb35e 100644 --- a/src/analyses/mallocFresh.ml +++ b/src/analyses/mallocFresh.ml @@ -18,10 +18,13 @@ struct let exitstate _ = D.empty () let assign_lval (ask: Queries.ask) lval local = - match ask.f (MayPointTo (AddrOf lval)) with - | ls when Queries.LS.is_top ls || Queries.LS.mem (dummyFunDec.svar, `NoOffset) ls -> + match ask.f (MayPointToA (AddrOf lval)) with + | ls when Queries.AD.is_top ls -> D.empty () - | ls when Queries.LS.exists (fun (v, _) -> not (D.mem v local) && (v.vglob || ThreadEscape.has_escaped ask v)) ls -> + | ls when Queries.AD.exists (function + | Queries.AD.Addr.Addr (v,_) -> not (D.mem v local) && (v.vglob || ThreadEscape.has_escaped ask v) + | _ -> false + ) ls -> D.empty () | _ -> local From 16f35001545a54d9f5779b023cf9807b85fdfd1f Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 15:32:16 +0300 Subject: [PATCH 0482/1312] Use MayPointToA in malloc_null --- src/analyses/malloc_null.ml | 40 +++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/src/analyses/malloc_null.ml b/src/analyses/malloc_null.ml index 656e1e6f14..d7c1c954e4 100644 --- a/src/analyses/malloc_null.ml +++ b/src/analyses/malloc_null.ml @@ -37,10 +37,12 @@ struct with SetDomain.Unsupported _ -> () end;*) match e with | Lval (Var v, offs) -> - begin match a.f (Queries.MayPointTo (mkAddrOf (Var v,offs))) with - | a when not (Queries.LS.is_top a) - && not (Queries.LS.mem (dummyFunDec.svar,`NoOffset) a) -> - Queries.LS.iter (fun (v,o) -> warn_lval st (v, Offs.of_exp o)) a + begin match a.f (Queries.MayPointToA (mkAddrOf (Var v,offs))) with + | a when not (Queries.AD.is_top a) -> + Queries.AD.iter (function + | Queries.AD.Addr.Addr addr -> warn_lval st addr + | _ -> () + ) a | _ -> () end | _ -> () @@ -112,11 +114,9 @@ struct else D.filter (fun x -> AD.mem x vars) st let get_concrete_lval (ask: Queries.ask) (lval:lval) = - match ask.f (Queries.MayPointTo (mkAddrOf lval)) with - | a when Queries.LS.cardinal a = 1 - && not (Queries.LS.mem (dummyFunDec.svar,`NoOffset) a) -> - let v, o = Queries.LS.choose a in - Some (Var v, Offs.of_exp o) + match ask.f (Queries.MayPointToA (mkAddrOf lval)) with + | a when Queries.AD.cardinal a = 1 && not (Queries.AD.mem UnknownPtr a) -> + Queries.AD.Addr.to_mval (Queries.AD.choose a) | _ -> None let get_concrete_exp (exp:exp) gl (st:D.t) = @@ -126,12 +126,14 @@ struct | _ -> None let might_be_null (ask: Queries.ask) lv gl st = - match ask.f (Queries.MayPointTo (mkAddrOf lv)) with - | a when not (Queries.LS.is_top a) && not (Queries.LS.mem (dummyFunDec.svar,`NoOffset) a) -> - let one_addr_might (v,o) = - D.exists (fun x -> GobOption.exists (fun x -> is_prefix_of (v, Offs.of_exp o) x) (Addr.to_mval x)) st + match ask.f (Queries.MayPointToA (mkAddrOf lv)) with + | a when not (Queries.AD.is_top a) -> + let one_addr_might = function + | Queries.AD.Addr.Addr addr -> + D.exists (fun x -> GobOption.exists (fun x -> is_prefix_of addr x) (Addr.to_mval x)) st + | _ -> false in - Queries.LS.exists one_addr_might a + Queries.AD.exists one_addr_might a | _ -> false (* @@ -143,8 +145,8 @@ struct warn_deref_exp (Analyses.ask_of_ctx ctx) ctx.local (Lval lval) ; warn_deref_exp (Analyses.ask_of_ctx ctx) ctx.local rval; match get_concrete_exp rval ctx.global ctx.local, get_concrete_lval (Analyses.ask_of_ctx ctx) lval with - | Some rv , Some (Var vt,ot) when might_be_null (Analyses.ask_of_ctx ctx) rv ctx.global ctx.local -> - D.add (Addr.of_mval (vt,ot)) ctx.local + | Some rv, Some addr when might_be_null (Analyses.ask_of_ctx ctx) rv ctx.global ctx.local -> + D.add (Addr.of_mval addr) ctx.local | _ -> ctx.local let branch ctx (exp:exp) (tv:bool) : D.t = @@ -185,7 +187,7 @@ struct match lval, D.mem (return_addr ()) au with | Some lv, true -> begin match get_concrete_lval (Analyses.ask_of_ctx ctx) lv with - | Some (Var v,ofs) -> D.add (Addr.of_mval (v,ofs)) ctx.local + | Some addr -> D.add (Addr.of_mval addr) ctx.local | _ -> ctx.local end | _ -> ctx.local @@ -198,9 +200,9 @@ struct | Malloc _, Some lv -> begin match get_concrete_lval (Analyses.ask_of_ctx ctx) lv with - | Some (Var v, offs) -> + | Some addr -> ctx.split ctx.local [Events.SplitBranch ((Lval lv), true)]; - ctx.split (D.add (Addr.of_mval (v,offs)) ctx.local) [Events.SplitBranch ((Lval lv), false)]; + ctx.split (D.add (Addr.of_mval addr) ctx.local) [Events.SplitBranch ((Lval lv), false)]; raise Analyses.Deadcode | _ -> ctx.local end From 68d658852592584f9ff4ed0c9b9b6b49c7f8b01d Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 15:46:35 +0300 Subject: [PATCH 0483/1312] Use MayPointToA in fileUse --- src/analyses/fileUse.ml | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/analyses/fileUse.ml b/src/analyses/fileUse.ml index 174cd6a914..2f88ce03dc 100644 --- a/src/analyses/fileUse.ml +++ b/src/analyses/fileUse.ml @@ -24,22 +24,28 @@ struct (* queries *) let query ctx (type a) (q: a Queries.t) = match q with - | Queries.MayPointTo exp -> if M.tracing then M.tracel "file" "query MayPointTo: %a" d_plainexp exp; Queries.Result.top q + | Queries.MayPointToA exp -> if M.tracing then M.tracel "file" "query MayPointTo: %a" d_plainexp exp; Queries.Result.top q | _ -> Queries.Result.top q let query_lv (ask: Queries.ask) exp = - match ask.f (Queries.MayPointTo exp) with - | l when not (Queries.LS.is_top l) -> - Queries.LS.elements l + match ask.f (Queries.MayPointToA exp) with + | l when not (Queries.AD.is_top l) -> + Queries.AD.elements l | _ -> [] let print_query_lv ?msg:(msg="") ask exp = let xs = query_lv ask exp in (* MayPointTo -> LValSet *) - let pretty_key k = Pretty.text (D.string_of_key k) in - if M.tracing then M.tracel "file" "%s MayPointTo %a = [%a]" msg d_exp exp (Pretty.docList ~sep:(Pretty.text ", ") pretty_key) xs + let pretty_key = function + | Queries.AD.Addr.Addr (v,o) -> Pretty.text (D.string_of_key (v, ValueDomain.Addr.Offs.to_exp o)) + | _ -> Pretty.text "" in + if M.tracing then M.tracel "file" "%s MayPointToA %a = [%a]" msg d_exp exp (Pretty.docList ~sep:(Pretty.text ", ") pretty_key) xs let eval_fv ask exp: varinfo option = match query_lv ask exp with - | [(v,_)] -> Some v + | [addr] -> + begin match addr with + | Queries.AD.Addr.Addr (v,_) -> Some v + | _ -> None + end | _ -> None From e6975045f806420fbf3e5c9c2db543fe213de231 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 15:55:58 +0300 Subject: [PATCH 0484/1312] Use MayPointToA in extractPthread --- src/analyses/extractPthread.ml | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/analyses/extractPthread.ml b/src/analyses/extractPthread.ml index 2041c23e1b..4fa912a75a 100644 --- a/src/analyses/extractPthread.ml +++ b/src/analyses/extractPthread.ml @@ -880,20 +880,23 @@ module Spec : Analyses.MCPSpec = struct module ExprEval = struct let eval_ptr ctx exp = let mayPointTo ctx exp = - let a = ctx.ask (Queries.MayPointTo exp) in - if (not (Queries.LS.is_top a)) && Queries.LS.cardinal a > 0 then - let top_elt = (dummyFunDec.svar, `NoOffset) in + let a = ctx.ask (Queries.MayPointToA exp) in + if (not (Queries.AD.is_top a)) && Queries.AD.cardinal a > 0 then let a' = - if Queries.LS.mem top_elt a + if Queries.AD.mem UnknownPtr a then (* UNSOUND *) - Queries.LS.remove top_elt a + Queries.AD.remove UnknownPtr a else a in - Queries.LS.elements a' + Queries.AD.elements a' else [] in - List.map fst @@ mayPointTo ctx exp + List.fold (fun l addr -> + match addr with + | Queries.AD.Addr.Addr (v,_) -> v :: l + | _ -> l + ) [] (mayPointTo ctx exp) let eval_var ctx exp = From 076da538d68b64a0957db70dff4ba283ac2e6b9f Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 16:10:34 +0300 Subject: [PATCH 0485/1312] Use MayPointToA in condVars Co-authored-by: Simmo Saan --- src/analyses/condVars.ml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/analyses/condVars.ml b/src/analyses/condVars.ml index 5a2e97139c..5f50d3968d 100644 --- a/src/analyses/condVars.ml +++ b/src/analyses/condVars.ml @@ -64,15 +64,17 @@ struct let (>?) = Option.bind let mayPointTo ctx exp = - match ctx.ask (Queries.MayPointTo exp) with - | a when not (Queries.LS.is_top a) && Queries.LS.cardinal a > 0 -> - let top_elt = (dummyFunDec.svar, `NoOffset) in - let a' = if Queries.LS.mem top_elt a then ( + match ctx.ask (Queries.MayPointToA exp) with + | a when not (Queries.AD.is_top a) && Queries.AD.cardinal a > 0 -> + let a' = if Queries.AD.mem UnknownPtr a then ( M.info ~category:Unsound "mayPointTo: query result for %a contains TOP!" d_exp exp; (* UNSOUND *) - Queries.LS.remove top_elt a + Queries.AD.remove UnknownPtr a ) else a in - Queries.LS.elements a' + List.filter_map (function + | ValueDomain.Addr.Addr (v,o) -> Some (v, ValueDomain.Addr.Offs.to_exp o) + | _ -> None + ) (Queries.AD.elements a') | _ -> [] let mustPointTo ctx exp = (* this is just to get Mval.Exp *) From 1c7186cb910e8a041d6b3c6dfd65c3cd2e348e46 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 16:13:38 +0300 Subject: [PATCH 0486/1312] Use MayPointToA in mvalMapDomain --- src/cdomains/mvalMapDomain.ml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/cdomains/mvalMapDomain.ml b/src/cdomains/mvalMapDomain.ml index 9d7625c4f5..78357a6d73 100644 --- a/src/cdomains/mvalMapDomain.ml +++ b/src/cdomains/mvalMapDomain.ml @@ -281,13 +281,19 @@ struct let keys_from_lval lval (ask: Queries.ask) = (* use MayPointTo query to get all possible pointees of &lval *) (* print_query_lv ctx.ask (AddrOf lval); *) - let query_lv (ask: Queries.ask) exp = match ask.f (Queries.MayPointTo exp) with - | l when not (Queries.LS.is_top l) -> Queries.LS.elements l + let query_lv (ask: Queries.ask) exp = match ask.f (Queries.MayPointToA exp) with + | l when not (Queries.AD.is_top l) -> Queries.AD.elements l | _ -> [] in let exp = AddrOf lval in let xs = query_lv ask exp in (* MayPointTo -> LValSet *) + let keys = List.fold (fun l addr -> + match addr with + | Queries.AD.Addr.Addr (v,o) -> (v, ValueDomain.Offs.to_exp o) :: l + | _ -> l + ) [] xs + in let pretty_key k = Pretty.text (string_of_key k) in - Messages.debug ~category:Analyzer "MayPointTo %a = [%a]" d_exp exp (Pretty.docList ~sep:(Pretty.text ", ") pretty_key) xs; - xs + Messages.debug ~category:Analyzer "MayPointTo %a = [%a]" d_exp exp (Pretty.docList ~sep:(Pretty.text ", ") pretty_key) keys; + keys end From f7b38a0ca666e433f79e2057de8cd4eb59a52a7b Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 16:43:21 +0300 Subject: [PATCH 0487/1312] Add ReachableFrom query with address domain --- src/analyses/base.ml | 11 +++++++++++ src/domains/queries.ml | 7 +++++++ 2 files changed, 18 insertions(+) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index a08e4a14f4..7967b10df2 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1317,6 +1317,17 @@ struct addrs | _ -> Q.LS.empty () end + | Q.ReachableFromA e -> begin + match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local e with + | Top -> Queries.Result.top q + | Bot -> Queries.Result.bot q (* TODO: remove *) + | Address a -> + let a' = AD.remove Addr.UnknownPtr a in (* run reachable_vars without unknown just to be safe: TODO why? *) + let xs = reachable_vars (Analyses.ask_of_ctx ctx) [a'] ctx.global ctx.local in + let addrs = List.fold_left (Q.AD.join) (Q.AD.empty ()) xs in + addrs + | _ -> Q.AD.empty () + end | Q.ReachableUkTypes e -> begin match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local e with | Top -> Queries.Result.top q diff --git a/src/domains/queries.ml b/src/domains/queries.ml index fcbaa4cba8..42be039d96 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -75,6 +75,7 @@ type _ t = | MayPointTo: exp -> LS.t t | MayPointToA: exp -> AD.t t | ReachableFrom: exp -> LS.t t + | ReachableFromA: exp -> AD.t t | ReachableUkTypes: exp -> TS.t t | Regions: exp -> LS.t t | MayEscape: varinfo -> MayBool.t t @@ -145,6 +146,7 @@ struct | MayPointTo _ -> (module LS) | MayPointToA _ -> (module AD) | ReachableFrom _ -> (module LS) + | ReachableFromA _ -> (module AD) | Regions _ -> (module LS) | MustLockset -> (module LS) | EvalFunvar _ -> (module LS) @@ -210,6 +212,7 @@ struct | MayPointTo _ -> LS.top () | MayPointToA _ -> AD.top () | ReachableFrom _ -> LS.top () + | ReachableFromA _ -> AD.top () | Regions _ -> LS.top () | MustLockset -> LS.top () | EvalFunvar _ -> LS.top () @@ -271,6 +274,7 @@ struct | Any (MayPointTo _) -> 1 | Any (MayPointToA _) -> 999 | Any (ReachableFrom _) -> 2 + | Any (ReachableFromA _) -> 666 | Any (ReachableUkTypes _) -> 3 | Any (Regions _) -> 4 | Any (MayEscape _) -> 5 @@ -328,6 +332,7 @@ struct | Any (MayPointTo e1), Any (MayPointTo e2) -> CilType.Exp.compare e1 e2 | Any (MayPointToA e1), Any (MayPointToA e2) -> CilType.Exp.compare e1 e2 | Any (ReachableFrom e1), Any (ReachableFrom e2) -> CilType.Exp.compare e1 e2 + | Any (ReachableFromA e1), Any (ReachableFromA e2) -> CilType.Exp.compare e1 e2 | Any (ReachableUkTypes e1), Any (ReachableUkTypes e2) -> CilType.Exp.compare e1 e2 | Any (Regions e1), Any (Regions e2) -> CilType.Exp.compare e1 e2 | Any (MayEscape vi1), Any (MayEscape vi2) -> CilType.Varinfo.compare vi1 vi2 @@ -374,6 +379,7 @@ struct | Any (MayPointTo e) -> CilType.Exp.hash e | Any (MayPointToA e) -> CilType.Exp.hash e | Any (ReachableFrom e) -> CilType.Exp.hash e + | Any (ReachableFromA e) -> CilType.Exp.hash e | Any (ReachableUkTypes e) -> CilType.Exp.hash e | Any (Regions e) -> CilType.Exp.hash e | Any (MayEscape vi) -> CilType.Varinfo.hash vi @@ -417,6 +423,7 @@ struct | Any (MayPointTo e) -> Pretty.dprintf "MayPointTo %a" CilType.Exp.pretty e | Any (MayPointToA e) -> Pretty.dprintf "MayPointToA %a" CilType.Exp.pretty e | Any (ReachableFrom e) -> Pretty.dprintf "ReachableFrom %a" CilType.Exp.pretty e + | Any (ReachableFromA e) -> Pretty.dprintf "ReachableFromA %a" CilType.Exp.pretty e | Any (ReachableUkTypes e) -> Pretty.dprintf "ReachableUkTypes %a" CilType.Exp.pretty e | Any (Regions e) -> Pretty.dprintf "Regions %a" CilType.Exp.pretty e | Any (MayEscape vi) -> Pretty.dprintf "MayEscape %a" CilType.Varinfo.pretty vi From b49f0e0553df9b8fc34205636f9559745bd2e690 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 17:50:06 +0300 Subject: [PATCH 0488/1312] Use MayPointToA and ReachableFromA in accessAnalysis Co-authored-by: Simmo Saan --- src/analyses/accessAnalysis.ml | 33 ++++++++++++--------------- src/analyses/modifiedSinceLongjmp.ml | 13 ++++++++++- src/analyses/mutexAnalysis.ml | 34 +++++++++++++--------------- src/analyses/poisonVariables.ml | 17 +++++++++----- src/analyses/raceAnalysis.ml | 32 +++++++++++++------------- src/domains/events.ml | 4 ++-- 6 files changed, 71 insertions(+), 62 deletions(-) diff --git a/src/analyses/accessAnalysis.ml b/src/analyses/accessAnalysis.ml index bc5330726c..9c2cc31eda 100644 --- a/src/analyses/accessAnalysis.ml +++ b/src/analyses/accessAnalysis.ml @@ -33,7 +33,7 @@ struct let do_access (ctx: (D.t, G.t, C.t, V.t) ctx) (kind:AccessKind.t) (reach:bool) (e:exp) = if M.tracing then M.trace "access" "do_access %a %a %B\n" d_exp e AccessKind.pretty kind reach; - let reach_or_mpt: _ Queries.t = if reach then ReachableFrom e else MayPointTo e in + let reach_or_mpt: _ Queries.t = if reach then ReachableFromA e else MayPointToA e in let ls = ctx.ask reach_or_mpt in ctx.emit (Access {exp=e; lvals=ls; kind; reach}) @@ -138,24 +138,19 @@ struct let event ctx e octx = match e with | Events.Access {lvals; kind; _} when !collect_local && !AnalysisState.postsolving -> - begin match lvals with - | ls when Queries.LS.is_top ls -> - let access: AccessDomain.Event.t = {var_opt = None; offs_opt = None; kind} in - ctx.sideg ctx.node (G.singleton access) - | ls -> - let events = Queries.LS.fold (fun (var, offs) acc -> - let coffs = Offset.Exp.to_cil offs in - let access: AccessDomain.Event.t = - if CilType.Varinfo.equal var dummyFunDec.svar then - {var_opt = None; offs_opt = (Some coffs); kind} - else - {var_opt = (Some var); offs_opt = (Some coffs); kind} - in - G.add access acc - ) ls (G.empty ()) - in - ctx.sideg ctx.node events - end + let events = Queries.AD.fold (fun addr acc -> + match addr with + | Queries.AD.Addr.Addr (var, offs) -> + let coffs = Offset.Exp.to_cil (ValueDomain.Offs.to_exp offs) in + let access: AccessDomain.Event.t = {var_opt = (Some var); offs_opt = (Some coffs); kind} in + G.add access acc + | UnknownPtr -> + let access: AccessDomain.Event.t = {var_opt = None; offs_opt = None; kind} in + G.add access acc + | _ -> acc + ) lvals (G.empty ()) + in + ctx.sideg ctx.node events | _ -> ctx.local end diff --git a/src/analyses/modifiedSinceLongjmp.ml b/src/analyses/modifiedSinceLongjmp.ml index f489b08fe9..836cf6f827 100644 --- a/src/analyses/modifiedSinceLongjmp.ml +++ b/src/analyses/modifiedSinceLongjmp.ml @@ -29,6 +29,17 @@ struct else Queries.LS.fold (fun (v, _) acc -> if is_relevant v then VS.add v acc else acc) ls (VS.empty ()) + let relevants_from_ad ls = + (* TODO: what about AD with both known and unknown pointers? *) + if Queries.AD.is_top ls then + VS.top () + else + Queries.AD.fold (fun addr acc -> + match addr with + | Queries.AD.Addr.Addr (v, _) -> if is_relevant v then VS.add v acc else acc + | _ -> acc + ) ls (VS.empty ()) + (* transfer functions *) let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = [ctx.local, D.bot ()] (* enter with bot as opposed to IdentitySpec *) @@ -63,7 +74,7 @@ struct let event ctx (e: Events.t) octx = match e with | Access {lvals; kind = Write; _} -> - add_to_all_defined (relevants_from_ls lvals) ctx.local + add_to_all_defined (relevants_from_ad lvals) ctx.local | _ -> ctx.local end diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 154f7ba183..7f504badf2 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -296,7 +296,7 @@ struct | Events.Access {exp; lvals; kind; _} when ThreadFlag.has_ever_been_multi (Analyses.ask_of_ctx ctx) -> (* threadflag query in post-threadspawn ctx *) let is_recovered_to_st = not (ThreadFlag.is_currently_multi (Analyses.ask_of_ctx ctx)) in (* must use original (pre-assign, etc) ctx queries *) - let old_access var_opt offs_opt = + let old_access var_opt = (* TODO: this used to use ctx instead of octx, why? *) (*privatization*) match var_opt with @@ -325,24 +325,22 @@ struct ) | None -> M.info ~category:Unsound "Write to unknown address: privatization is unsound." in - let module LS = Queries.LS in + let module AD = Queries.AD in let has_escaped g = octx.ask (Queries.MayEscape g) in - let on_lvals ls = - let ls = LS.filter (fun (g,_) -> g.vglob || has_escaped g) ls in - let f (var, offs) = - let coffs = Offset.Exp.to_cil offs in - if CilType.Varinfo.equal var dummyFunDec.svar then - old_access None (Some coffs) - else - old_access (Some var) (Some coffs) + let on_lvals ad = + let f addr = + match addr with + | AD.Addr.Addr (g,o) when g.vglob || has_escaped g -> old_access (Some g) + | UnknownPtr -> old_access None + | _ -> () in - LS.iter f ls + AD.iter f ad in begin match lvals with - | ls when not (LS.is_top ls) && not (Queries.LS.mem (dummyFunDec.svar,`NoOffset) ls) -> + | ad when not (AD.is_top ad) -> (* the case where the points-to set is non top and does not contain unknown values *) - on_lvals ls - | ls when not (LS.is_top ls) -> + on_lvals ad + | ad -> (* the case where the points-to set is non top and contains unknown values *) (* now we need to access all fields that might be pointed to: is this correct? *) begin match octx.ask (ReachableUkTypes exp) with @@ -354,11 +352,11 @@ struct | _ -> false in if Queries.TS.exists f ts then - old_access None None + old_access None end; - on_lvals ls - | _ -> - old_access None None + on_lvals ad + (* | _ -> + old_access None None *) end; ctx.local | _ -> diff --git a/src/analyses/poisonVariables.ml b/src/analyses/poisonVariables.ml index 5cb34baa26..b124cb90f0 100644 --- a/src/analyses/poisonVariables.ml +++ b/src/analyses/poisonVariables.ml @@ -80,23 +80,28 @@ struct ) longjmp_nodes; D.join modified_locals ctx.local | Access {lvals; kind = Read; _} -> - if Queries.LS.is_top lvals then ( + (* TODO: what about AD with both known and unknown pointers? *) + if Queries.AD.is_top lvals then ( if not (VS.is_empty octx.local) then M.warn ~category:(Behavior (Undefined Other)) "reading unknown memory location, may be tainted!" ) else ( - Queries.LS.iter (fun lv -> + Queries.AD.iter (function (* Use original access state instead of current with removed written vars. *) - check_lval octx.local lv + | Queries.AD.Addr.Addr (v,o) -> check_lval octx.local (v, ValueDomain.Offs.to_exp o) + | _ -> () ) lvals ); ctx.local | Access {lvals; kind = Write; _} -> - if Queries.LS.is_top lvals then + (* TODO: what about AD with both known and unknown pointers? *) + if Queries.AD.is_top lvals then ctx.local else ( - Queries.LS.fold (fun lv acc -> - rem_lval acc lv + Queries.AD.fold (fun addr acc -> + match addr with + | Queries.AD.Addr.Addr (v,o) -> rem_lval acc (v, ValueDomain.Offs.to_exp o) + | _ -> acc ) lvals ctx.local ) | _ -> ctx.local diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index 970895e971..11e77bf902 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -134,7 +134,7 @@ struct | Events.Access {exp=e; lvals; kind; reach} when ThreadFlag.is_currently_multi (Analyses.ask_of_ctx ctx) -> (* threadflag query in post-threadspawn ctx *) (* must use original (pre-assign, etc) ctx queries *) let conf = 110 in - let module LS = Queries.LS in + let module AD = Queries.AD in let part_access (vo:varinfo option): MCPAccess.A.t = (*partitions & locks*) Obj.obj (octx.ask (PartAccess (Memory {exp=e; var_opt=vo; kind}))) @@ -151,24 +151,24 @@ struct let has_escaped g = octx.ask (Queries.MayEscape g) in (* The following function adds accesses to the lval-set ls -- this is the common case if we have a sound points-to set. *) - let on_lvals ls includes_uk = - let ls = LS.filter (fun (g,_) -> g.vglob || has_escaped g) ls in + let on_lvals ad includes_uk = let conf = if reach then conf - 20 else conf in let conf = if includes_uk then conf - 10 else conf in - let f (var, offs) = - let coffs = Offset.Exp.to_cil offs in - if CilType.Varinfo.equal var dummyFunDec.svar then - add_access conf None - else - add_access conf (Some (var, coffs)) + let f addr = + match addr with + | AD.Addr.Addr (g,o) when g.vglob || has_escaped g -> + let coffs = Offset.Exp.to_cil (ValueDomain.Offs.to_exp o) in + add_access conf (Some (g, coffs)) + | UnknownPtr -> add_access conf None + | _ -> () in - LS.iter f ls + AD.iter f ad in begin match lvals with - | ls when not (LS.is_top ls) && not (Queries.LS.mem (dummyFunDec.svar,`NoOffset) ls) -> + | ad when not (AD.is_top ad) -> (* the case where the points-to set is non top and does not contain unknown values *) - on_lvals ls false - | ls when not (LS.is_top ls) -> + on_lvals ad false + | ad -> (* the case where the points-to set is non top and contains unknown values *) let includes_uk = ref false in (* now we need to access all fields that might be pointed to: is this correct? *) @@ -185,9 +185,9 @@ struct in Queries.TS.iter f ts end; - on_lvals ls !includes_uk - | _ -> - add_access (conf - 60) None + on_lvals ad !includes_uk + (* | _ -> + add_access (conf - 60) None *) end; ctx.local | _ -> diff --git a/src/domains/events.ml b/src/domains/events.ml index 2141ad17dd..41e745ed8a 100644 --- a/src/domains/events.ml +++ b/src/domains/events.ml @@ -10,7 +10,7 @@ type t = | EnterMultiThreaded | SplitBranch of exp * bool (** Used to simulate old branch-based split. *) | AssignSpawnedThread of lval * ThreadIdDomain.Thread.t (** Assign spawned thread's ID to lval. *) - | Access of {exp: CilType.Exp.t; lvals: Queries.LS.t; kind: AccessKind.t; reach: bool} + | Access of {exp: CilType.Exp.t; lvals: Queries.AD.t; kind: AccessKind.t; reach: bool} | Assign of {lval: CilType.Lval.t; exp: CilType.Exp.t} (** Used to simulate old [ctx.assign]. *) (* TODO: unused *) | UpdateExpSplit of exp (** Used by expsplit analysis to evaluate [exp] on post-state. *) | Assert of exp @@ -41,7 +41,7 @@ let pretty () = function | EnterMultiThreaded -> text "EnterMultiThreaded" | SplitBranch (exp, tv) -> dprintf "SplitBranch (%a, %B)" d_exp exp tv | AssignSpawnedThread (lval, tid) -> dprintf "AssignSpawnedThread (%a, %a)" d_lval lval ThreadIdDomain.Thread.pretty tid - | Access {exp; lvals; kind; reach} -> dprintf "Access {exp=%a; lvals=%a; kind=%a; reach=%B}" CilType.Exp.pretty exp Queries.LS.pretty lvals AccessKind.pretty kind reach + | Access {exp; lvals; kind; reach} -> dprintf "Access {exp=%a; lvals=%a; kind=%a; reach=%B}" CilType.Exp.pretty exp Queries.AD.pretty lvals AccessKind.pretty kind reach | Assign {lval; exp} -> dprintf "Assign {lval=%a, exp=%a}" CilType.Lval.pretty lval CilType.Exp.pretty exp | UpdateExpSplit exp -> dprintf "UpdateExpSplit %a" d_exp exp | Assert exp -> dprintf "Assert %a" d_exp exp From 20b75c6303e25de30aa9472f87b90dce63d90110 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 18:32:08 +0300 Subject: [PATCH 0489/1312] Use MayPointToA and ReachableFromA in varEq Co-authored-by: Simmo Saan --- src/analyses/libraryFunctions.ml | 1 + src/analyses/varEq.ml | 33 ++++++++++++++------------------ 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 5dc311a587..d9211ce897 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -279,6 +279,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__sigsetjmp", special [__ "env" [w]; drop "savesigs" []] @@ fun env -> Setjmp { env }); (* has two underscores *) ("sigsetjmp", special [__ "env" [w]; drop "savesigs" []] @@ fun env -> Setjmp { env }); ("siglongjmp", special [__ "env" [r]; __ "value" []] @@ fun env value -> Longjmp { env; value }); + ("getaddrinfo", unknown [drop "node" [r]; drop "service" [r]; drop "hints" [r_deep]; drop "res" [w_deep]]); ] (** Pthread functions. *) diff --git a/src/analyses/varEq.ml b/src/analyses/varEq.ml index 90ea4e5eae..71ab214481 100644 --- a/src/analyses/varEq.ml +++ b/src/analyses/varEq.ml @@ -389,17 +389,11 @@ struct (* Give the set of reachables from argument. *) let reachables ~deep (ask: Queries.ask) es = let reachable e st = - match st with - | None -> None - | Some st -> - let q = if deep then Queries.ReachableFrom e else Queries.MayPointTo e in - let vs = ask.f q in - if Queries.LS.is_top vs then - None - else - Some (Queries.LS.join vs st) + let q = if deep then Queries.ReachableFromA e else Queries.MayPointToA e in + let vs = ask.f q in + Queries.AD.join vs st in - List.fold_right reachable es (Some (Queries.LS.empty ())) + List.fold_right reachable es (Queries.AD.empty ()) (* Probably ok as is. *) @@ -460,15 +454,16 @@ struct | None -> ctx.local let remove_reachable ~deep ask es st = - match reachables ~deep ask es with - | None -> D.top () - | Some rs -> - (* Prior to https://github.com/goblint/analyzer/pull/694 checks were done "in the other direction": - each expression in st was checked for reachability from es/rs using very conservative but also unsound reachable_from. - It is unknown, why that was necessary. *) - Queries.LS.fold (fun lval st -> - remove ask (Mval.Exp.to_cil lval) st - ) rs st + let rs = reachables ~deep ask es in + (* Prior to https://github.com/goblint/analyzer/pull/694 checks were done "in the other direction": + each expression in st was checked for reachability from es/rs using very conservative but also unsound reachable_from. + It is unknown, why that was necessary. *) + Queries.AD.fold (fun addr st -> + match addr with + | Queries.AD.Addr.Addr (v,o) -> remove ask (Mval.Exp.to_cil (v,ValueDomain.Offs.to_exp o)) st + | UnknownPtr -> D.top () + | _ -> st + ) rs st let unknown_fn ctx lval f args = let desc = LF.find f in From 5e8e2c7b87586a8fcdeb1e7324a81c0dc72b5273 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 23 Aug 2023 10:33:29 +0300 Subject: [PATCH 0490/1312] Make getaddrinfo specification more precise --- src/analyses/libraryFunctions.ml | 2 +- src/analyses/varEq.ml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index d9211ce897..67ba8c8c20 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -279,7 +279,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__sigsetjmp", special [__ "env" [w]; drop "savesigs" []] @@ fun env -> Setjmp { env }); (* has two underscores *) ("sigsetjmp", special [__ "env" [w]; drop "savesigs" []] @@ fun env -> Setjmp { env }); ("siglongjmp", special [__ "env" [r]; __ "value" []] @@ fun env value -> Longjmp { env; value }); - ("getaddrinfo", unknown [drop "node" [r]; drop "service" [r]; drop "hints" [r_deep]; drop "res" [w_deep]]); + ("getaddrinfo", unknown [drop "node" [r]; drop "service" [r]; drop "hints" [r_deep]; drop "res" [w]]); (* only write res non-deep because it doesn't write to existing fields of res *) ] (** Pthread functions. *) diff --git a/src/analyses/varEq.ml b/src/analyses/varEq.ml index 71ab214481..811d1afffc 100644 --- a/src/analyses/varEq.ml +++ b/src/analyses/varEq.ml @@ -455,6 +455,7 @@ struct let remove_reachable ~deep ask es st = let rs = reachables ~deep ask es in + if M.tracing then M.tracel "var_eq" "remove_reachable %a: %a\n" (Pretty.d_list ", " d_exp) es AD.pretty rs; (* Prior to https://github.com/goblint/analyzer/pull/694 checks were done "in the other direction": each expression in st was checked for reachability from es/rs using very conservative but also unsound reachable_from. It is unknown, why that was necessary. *) From e0792129058919f0ea12ce075e47085d5ad175b7 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 23 Aug 2023 12:46:40 +0300 Subject: [PATCH 0491/1312] Remove unnecessary offset double conversions --- src/analyses/accessAnalysis.ml | 4 ++-- src/analyses/raceAnalysis.ml | 4 ++-- src/analyses/varEq.ml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/analyses/accessAnalysis.ml b/src/analyses/accessAnalysis.ml index 9c2cc31eda..500a6e1494 100644 --- a/src/analyses/accessAnalysis.ml +++ b/src/analyses/accessAnalysis.ml @@ -141,10 +141,10 @@ struct let events = Queries.AD.fold (fun addr acc -> match addr with | Queries.AD.Addr.Addr (var, offs) -> - let coffs = Offset.Exp.to_cil (ValueDomain.Offs.to_exp offs) in + let coffs = ValueDomain.Offs.to_cil offs in let access: AccessDomain.Event.t = {var_opt = (Some var); offs_opt = (Some coffs); kind} in G.add access acc - | UnknownPtr -> + | UnknownPtr -> let access: AccessDomain.Event.t = {var_opt = None; offs_opt = None; kind} in G.add access acc | _ -> acc diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index 11e77bf902..d911dafd91 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -157,10 +157,10 @@ struct let f addr = match addr with | AD.Addr.Addr (g,o) when g.vglob || has_escaped g -> - let coffs = Offset.Exp.to_cil (ValueDomain.Offs.to_exp o) in + let coffs = ValueDomain.Offs.to_cil o in add_access conf (Some (g, coffs)) | UnknownPtr -> add_access conf None - | _ -> () + | _ -> () in AD.iter f ad in diff --git a/src/analyses/varEq.ml b/src/analyses/varEq.ml index 811d1afffc..edec2f40d4 100644 --- a/src/analyses/varEq.ml +++ b/src/analyses/varEq.ml @@ -461,7 +461,7 @@ struct It is unknown, why that was necessary. *) Queries.AD.fold (fun addr st -> match addr with - | Queries.AD.Addr.Addr (v,o) -> remove ask (Mval.Exp.to_cil (v,ValueDomain.Offs.to_exp o)) st + | Queries.AD.Addr.Addr mval -> remove ask (ValueDomain.Mval.to_cil mval) st | UnknownPtr -> D.top () | _ -> st ) rs st From 2f4119d28a487f29d45e0a7cfe80d218a9884565 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 23 Aug 2023 12:58:48 +0300 Subject: [PATCH 0492/1312] Use AddressDomain helper functions for to_mval and to_var_may --- src/analyses/apron/relationAnalysis.apron.ml | 5 ++-- src/analyses/extractPthread.ml | 30 +++++++------------- src/analyses/fileUse.ml | 6 +--- src/analyses/spec.ml | 6 +--- 4 files changed, 15 insertions(+), 32 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index 5052631d72..513663a2cd 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -161,7 +161,7 @@ struct | a when Queries.AD.is_top a -> st | a -> - let lvals = List.filter_map Queries.AD.Addr.to_mval (Queries.AD.elements a) in + let lvals = Queries.AD.to_mval a in let ass' = List.map (fun lv -> assign_to_global_wrapper ask getg sideg st (ValueDomain.Addr.Mval.to_cil lv) f) lvals in List.fold_right D.join ass' (D.bot ()) end @@ -521,8 +521,7 @@ struct match ask.f (Queries.MayPointToA e) with | a when Queries.AD.is_top a -> [] | a -> - Queries.AD.elements a - |> List.filter_map Queries.AD.Addr.to_mval + Queries.AD.to_mval a |> List.map ValueDomain.Addr.Mval.to_cil in let shallow_addrs = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = false } args in diff --git a/src/analyses/extractPthread.ml b/src/analyses/extractPthread.ml index 4fa912a75a..774e115050 100644 --- a/src/analyses/extractPthread.ml +++ b/src/analyses/extractPthread.ml @@ -879,25 +879,17 @@ module Spec : Analyses.MCPSpec = struct module ExprEval = struct let eval_ptr ctx exp = - let mayPointTo ctx exp = - let a = ctx.ask (Queries.MayPointToA exp) in - if (not (Queries.AD.is_top a)) && Queries.AD.cardinal a > 0 then - let a' = - if Queries.AD.mem UnknownPtr a - then (* UNSOUND *) - Queries.AD.remove UnknownPtr a - else a - in - Queries.AD.elements a' - else - [] - in - List.fold (fun l addr -> - match addr with - | Queries.AD.Addr.Addr (v,_) -> v :: l - | _ -> l - ) [] (mayPointTo ctx exp) - + let a = ctx.ask (Queries.MayPointToA exp) in + if (not (Queries.AD.is_top a)) && Queries.AD.cardinal a > 0 then + let a' = + if Queries.AD.mem UnknownPtr a + then (* UNSOUND *) + Queries.AD.remove UnknownPtr a + else a + in + Queries.AD.to_var_may a' + else + [] let eval_var ctx exp = match exp with diff --git a/src/analyses/fileUse.ml b/src/analyses/fileUse.ml index 2f88ce03dc..06b8d43c40 100644 --- a/src/analyses/fileUse.ml +++ b/src/analyses/fileUse.ml @@ -41,11 +41,7 @@ struct let eval_fv ask exp: varinfo option = match query_lv ask exp with - | [addr] -> - begin match addr with - | Queries.AD.Addr.Addr (v,_) -> Some v - | _ -> None - end + | [addr] -> Queries.AD.Addr.to_var_may addr | _ -> None diff --git a/src/analyses/spec.ml b/src/analyses/spec.ml index 336b0c82f8..568482631c 100644 --- a/src/analyses/spec.ml +++ b/src/analyses/spec.ml @@ -211,11 +211,7 @@ struct let eval_fv ask exp: varinfo option = match query_lv ask exp with - | [addr] -> - begin match addr with - | Queries.AD.Addr.Addr (v,_) -> Some v - | _ -> None - end + | [addr] -> Queries.AD.Addr.to_var_may addr | _ -> None From cfab28c088480e8d1bff2b0ae24a5b40684f4bfb Mon Sep 17 00:00:00 2001 From: karoliineh Date: Wed, 23 Aug 2023 13:49:05 +0300 Subject: [PATCH 0493/1312] Use ReachableFromA in extractPthread --- src/analyses/extractPthread.ml | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/analyses/extractPthread.ml b/src/analyses/extractPthread.ml index 774e115050..5361fefbe3 100644 --- a/src/analyses/extractPthread.ml +++ b/src/analyses/extractPthread.ml @@ -244,7 +244,7 @@ let fun_ctx ctx f = f.vname ^ "_" ^ ctx_hash -module Tasks = SetDomain.Make (Lattice.Prod (Queries.LS) (PthreadDomain.D)) +module Tasks = SetDomain.Make (Lattice.Prod (Queries.AD) (PthreadDomain.D)) module rec Env : sig type t @@ -869,7 +869,7 @@ module Spec : Analyses.MCPSpec = struct module C = D (** Set of created tasks to spawn when going multithreaded *) - module Tasks = SetDomain.Make (Lattice.Prod (Queries.LS) (D)) + module Tasks = SetDomain.Make (Lattice.Prod (Queries.AD) (D)) module G = Tasks @@ -1119,18 +1119,21 @@ module Spec : Analyses.MCPSpec = struct let arglist' = List.map (stripCasts % constFold false) arglist in match (LibraryFunctions.find f).special arglist', f.vname, arglist with | ThreadCreate { thread; start_routine = func; _ }, _, _ -> - let funs_ls = - let ls = ctx.ask (Queries.ReachableFrom func) in - Queries.LS.filter - (fun lv -> - let lval = Mval.Exp.to_cil lv in - isFunctionType (typeOfLval lval)) - ls + let funs_ad = + let ad = ctx.ask (Queries.ReachableFromA func) in + Queries.AD.filter + (function + | Queries.AD.Addr.Addr addr -> + isFunctionType (ValueDomain.Addr.Mval.type_of addr) + | _ -> false) + ad in let thread_fun = - funs_ls - |> Queries.LS.elements - |> List.map fst + Queries.AD.fold (fun addr acc -> + match addr with + | Queries.AD.Addr.Addr (v,_) -> v :: acc + | _ -> acc + ) funs_ad [] |> List.unique ~eq:(fun a b -> a.vid = b.vid) |> List.hd in @@ -1143,7 +1146,7 @@ module Spec : Analyses.MCPSpec = struct ; ctx = Ctx.top () } in - Tasks.singleton (funs_ls, f_d) + Tasks.singleton (funs_ad, f_d) in ctx.sideg tasks_var tasks ; in @@ -1255,7 +1258,10 @@ module Spec : Analyses.MCPSpec = struct (* TODO: optimize finding *) let tasks_f = Tasks.filter - (fun (fs, f_d) -> Queries.LS.exists (fun (ls_f, _) -> ls_f = f) fs) + (fun (fs, f_d) -> Queries.AD.exists (function + | Queries.AD.Addr.Addr (ls_f, _) -> ls_f = f + | _ -> false) + fs) tasks in let f_d = snd (Tasks.choose tasks_f) in From 178a9c62e040549f8258fc05d4a281c87caeaa02 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Wed, 23 Aug 2023 13:53:33 +0300 Subject: [PATCH 0494/1312] Remove duplicated Tasks module from extractPthread --- src/analyses/extractPthread.ml | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/analyses/extractPthread.ml b/src/analyses/extractPthread.ml index 5361fefbe3..2cba97425d 100644 --- a/src/analyses/extractPthread.ml +++ b/src/analyses/extractPthread.ml @@ -869,8 +869,6 @@ module Spec : Analyses.MCPSpec = struct module C = D (** Set of created tasks to spawn when going multithreaded *) - module Tasks = SetDomain.Make (Lattice.Prod (Queries.AD) (D)) - module G = Tasks let tasks_var = From 08c9f1d35eb068e81218e870c3571968e89234bf Mon Sep 17 00:00:00 2001 From: karoliineh Date: Wed, 23 Aug 2023 14:03:46 +0300 Subject: [PATCH 0495/1312] Use ReachableFromA in malloc_null --- src/analyses/malloc_null.ml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/analyses/malloc_null.ml b/src/analyses/malloc_null.ml index d7c1c954e4..ee5c23914c 100644 --- a/src/analyses/malloc_null.ml +++ b/src/analyses/malloc_null.ml @@ -95,10 +95,14 @@ struct let remove_unreachable (ask: Queries.ask) (args: exp list) (st: D.t) : D.t = let reachable = let do_exp e = - match ask.f (Queries.ReachableFrom e) with - | a when not (Queries.LS.is_top a) -> - let to_extra (v,o) xs = AD.of_mval (v, Offs.of_exp o) :: xs in - Queries.LS.fold to_extra (Queries.LS.remove (dummyFunDec.svar, `NoOffset) a) [] + match ask.f (Queries.ReachableFromA e) with + | a when not (Queries.AD.is_top a) -> + Queries.AD.fold ( + fun addr xs -> + match addr with + | Queries.AD.Addr.Addr (v,o) -> AD.of_mval (v,o) :: xs + | _ -> xs + ) a [] (* Ignore soundness warnings, as invalidation proper will raise them. *) | _ -> [] in From 5dbfb0255d3b86615371cfaf6bbe39a88481dcb1 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Wed, 23 Aug 2023 14:32:33 +0300 Subject: [PATCH 0496/1312] Use ReachableFromA in poisonVariables --- src/analyses/poisonVariables.ml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/analyses/poisonVariables.ml b/src/analyses/poisonVariables.ml index b124cb90f0..49bd338538 100644 --- a/src/analyses/poisonVariables.ml +++ b/src/analyses/poisonVariables.ml @@ -42,14 +42,17 @@ struct if VS.is_empty ctx.local then [ctx.local,ctx.local] else ( - let reachable_from_args = List.fold (fun ls e -> Queries.LS.join ls (ctx.ask (ReachableFrom e))) (Queries.LS.empty ()) args in - if Queries.LS.is_top reachable_from_args || VS.is_top ctx.local then + let reachable_from_args = List.fold (fun ls e -> Queries.AD.join ls (ctx.ask (ReachableFromA e))) (Queries.AD.empty ()) args in + if Queries.AD.is_top reachable_from_args || VS.is_top ctx.local then [ctx.local, ctx.local] else let reachable_vars = - Queries.LS.elements reachable_from_args - |> List.map fst - |> VS.of_list + let get_vars addr vs = + match addr with + | Queries.AD.Addr.Addr (v,_) -> VS.add v vs + | _ -> vs + in + Queries.AD.fold get_vars reachable_from_args (VS.empty ()) in [VS.diff ctx.local reachable_vars, VS.inter reachable_vars ctx.local] ) From de9c475bcb5e68253e954f66499248da1a2328a8 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Wed, 23 Aug 2023 14:45:43 +0300 Subject: [PATCH 0497/1312] Use ReachableFromA in threadEscape --- src/analyses/threadEscape.ml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/analyses/threadEscape.ml b/src/analyses/threadEscape.ml index e8499f2fbf..3e2b41d903 100644 --- a/src/analyses/threadEscape.ml +++ b/src/analyses/threadEscape.ml @@ -26,13 +26,17 @@ struct module G = ThreadIdSet let reachable (ask: Queries.ask) e: D.t = - match ask.f (Queries.ReachableFrom e) with - | a when not (Queries.LS.is_top a) -> - let to_extra (v,o) set = D.add v set in - Queries.LS.fold to_extra (Queries.LS.remove (dummyFunDec.svar, `NoOffset) a) (D.empty ()) + match ask.f (Queries.ReachableFromA e) with + | a when not (Queries.AD.is_top a) -> + let to_extra addr set = + match addr with + | Queries.AD.Addr.Addr (v,_) -> D.add v set + | _ -> set + in + Queries.AD.fold to_extra a (D.empty ()) (* Ignore soundness warnings, as invalidation proper will raise them. *) | a -> - if M.tracing then M.tracel "escape" "reachable %a: %a\n" d_exp e Queries.LS.pretty a; + if M.tracing then M.tracel "escape" "reachable %a: %a\n" d_exp e Queries.AD.pretty a; D.empty () let mpt (ask: Queries.ask) e: D.t = From 2cd0ae6294e31ba881be57149c48c04ac21a66b9 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Wed, 23 Aug 2023 14:47:10 +0300 Subject: [PATCH 0498/1312] Extract to_extra fun from fold in malloc_null --- src/analyses/malloc_null.ml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/analyses/malloc_null.ml b/src/analyses/malloc_null.ml index ee5c23914c..2bebd5c0f6 100644 --- a/src/analyses/malloc_null.ml +++ b/src/analyses/malloc_null.ml @@ -96,13 +96,13 @@ struct let reachable = let do_exp e = match ask.f (Queries.ReachableFromA e) with - | a when not (Queries.AD.is_top a) -> - Queries.AD.fold ( - fun addr xs -> - match addr with - | Queries.AD.Addr.Addr (v,o) -> AD.of_mval (v,o) :: xs - | _ -> xs - ) a [] + | a when not (Queries.AD.is_top a) -> + let to_extra addr xs = + match addr with + | Queries.AD.Addr.Addr addr -> AD.of_mval addr :: xs + | _ -> xs + in + Queries.AD.fold to_extra a [] (* Ignore soundness warnings, as invalidation proper will raise them. *) | _ -> [] in From e4bef9c9638eb1184bc65388a067b2c32c370b06 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Wed, 23 Aug 2023 14:53:56 +0300 Subject: [PATCH 0499/1312] Use ReachableFromA in uninit --- src/analyses/uninit.ml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/analyses/uninit.ml b/src/analyses/uninit.ml index 55c9d2052a..1f04964d58 100644 --- a/src/analyses/uninit.ml +++ b/src/analyses/uninit.ml @@ -195,10 +195,14 @@ struct let remove_unreachable (ask: Queries.ask) (args: exp list) (st: D.t) : D.t = let reachable = let do_exp e = - match ask.f (Queries.ReachableFrom e) with - | a when not (Queries.LS.is_top a) -> - let to_extra (v,o) xs = AD.of_mval (v, Addr.Offs.of_exp o) :: xs in - Queries.LS.fold to_extra (Queries.LS.remove (dummyFunDec.svar, `NoOffset) a) [] + match ask.f (Queries.ReachableFromA e) with + | a when not (Queries.AD.is_top a) -> + let to_extra addr xs = + match addr with + | Queries.AD.Addr.Addr addr -> AD.of_mval addr :: xs + | _ -> xs + in + Queries.AD.fold to_extra a [] (* Ignore soundness warnings, as invalidation proper will raise them. *) | _ -> [] in From 973edf6c9c6b411ec0a91287015ad369b1407363 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Wed, 23 Aug 2023 15:03:03 +0300 Subject: [PATCH 0500/1312] Use ReachableFromA in useAfterFree --- src/analyses/useAfterFree.ml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index d5d2ba0266..4e28c49771 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -156,11 +156,18 @@ struct if D.is_empty caller_state then [caller_state, caller_state] else ( - let reachable_from_args = List.fold_left (fun acc arg -> Queries.LS.join acc (ctx.ask (ReachableFrom arg))) (Queries.LS.empty ()) args in - if Queries.LS.is_top reachable_from_args || D.is_top caller_state then + let reachable_from_args = List.fold_left (fun acc arg -> Queries.AD.join acc (ctx.ask (ReachableFromA arg))) (Queries.AD.empty ()) args in + if Queries.AD.is_top reachable_from_args || D.is_top caller_state then [caller_state, caller_state] else - let reachable_vars = List.map fst (Queries.LS.elements reachable_from_args) in + let reachable_vars = + let get_vars addr vs = + match addr with + | Queries.AD.Addr.Addr (v,_) -> v :: vs + | _ -> vs + in + Queries.AD.fold get_vars reachable_from_args [] + in let callee_state = D.filter (fun var -> List.mem var reachable_vars) caller_state in [caller_state, callee_state] ) From cf58111767c31dcadd57222dbf89441bbf8ca257 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Wed, 23 Aug 2023 18:51:40 +0300 Subject: [PATCH 0501/1312] Use ReachableFromA in relationAnalysis --- src/analyses/apron/relationAnalysis.apron.ml | 37 ++++++++++++++------ 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index 513663a2cd..d93a96e1b2 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -11,6 +11,7 @@ open Analyses open RelationDomain module M = Messages +module VS = SetDomain.Make (CilType.Varinfo) module SpecFunctor (Priv: RelationPriv.S) (RD: RelationDomain.RD) (PCU: RelationPrecCompareUtil.Util) = struct @@ -271,7 +272,15 @@ struct let any_local_reachable fundec reachable_from_args = let locals = fundec.sformals @ fundec.slocals in let locals_id = List.map (fun v -> v.vid) locals in - Queries.LS.exists (fun (v',_) -> List.mem v'.vid locals_id && RD.Tracked.varinfo_tracked v') reachable_from_args + VS.exists (fun v -> List.mem v.vid locals_id && RD.Tracked.varinfo_tracked v) reachable_from_args + + let reachable_from_args ctx args = + let vs e = + ctx.ask (ReachableFromA e) + |> LockDomain.MayLocksetNoRW.to_var_may + |> VS.of_list + in + List.fold (fun ls e -> VS.join ls (vs e)) (VS.empty ()) args let pass_to_callee fundec any_local_reachable var = (* TODO: currently, we pass all locals of the caller to the callee, provided one of them is reachbale to preserve relationality *) @@ -291,7 +300,6 @@ struct |> List.filter (fun (x, _) -> RD.Tracked.varinfo_tracked x) |> List.map (Tuple2.map1 RV.arg) in - let reachable_from_args = List.fold (fun ls e -> Queries.LS.join ls (ctx.ask (ReachableFrom e))) (Queries.LS.empty ()) args in let arg_vars = List.map fst arg_assigns in let new_rel = RD.add_vars st.rel arg_vars in (* RD.assign_exp_parallel_with new_rel arg_assigns; (* doesn't need to be parallel since exps aren't arg vars directly *) *) @@ -307,6 +315,7 @@ struct ) ) new_rel arg_assigns in + let reachable_from_args = reachable_from_args ctx args in let any_local_reachable = any_local_reachable fundec reachable_from_args in RD.remove_filter_with new_rel (fun var -> match RV.find_metadata var with @@ -369,16 +378,20 @@ struct let combine_env ctx r fe f args fc fun_st (f_ask : Queries.ask) = let st = ctx.local in - let reachable_from_args = List.fold (fun ls e -> Queries.LS.join ls (ctx.ask (ReachableFrom e))) (Queries.LS.empty ()) args in + let reachable_from_args = reachable_from_args ctx args in let fundec = Node.find_fundec ctx.node in if M.tracing then M.tracel "combine" "relation f: %a\n" CilType.Varinfo.pretty f.svar; if M.tracing then M.tracel "combine" "relation formals: %a\n" (d_list "," CilType.Varinfo.pretty) f.sformals; if M.tracing then M.tracel "combine" "relation args: %a\n" (d_list "," d_exp) args; let new_fun_rel = RD.add_vars fun_st.rel (RD.vars st.rel) in let arg_substitutes = + let filter_actuals (x,e) = + RD.Tracked.varinfo_tracked x + && List.for_all (fun v -> not (VS.mem v reachable_from_args)) (Basetype.CilExp.get_vars e) + in GobList.combine_short f.sformals args (* TODO: is it right to ignore missing formals/args? *) (* Do not do replacement for actuals whose value may be modified after the call *) - |> List.filter (fun (x, e) -> RD.Tracked.varinfo_tracked x && List.for_all (fun v -> not (Queries.LS.exists (fun (v',_) -> v'.vid = v.vid) reachable_from_args)) (Basetype.CilExp.get_vars e)) + |> List.filter filter_actuals |> List.map (Tuple2.map1 RV.arg) in (* RD.substitute_exp_parallel_with new_fun_rel arg_substitutes; (* doesn't need to be parallel since exps aren't arg vars directly *) *) @@ -441,13 +454,13 @@ struct match st with | None -> None | Some st -> - let vs = ask.f (Queries.ReachableFrom e) in - if Queries.LS.is_top vs then + let vs = ask.f (Queries.ReachableFromA e) in + if Queries.AD.is_top vs then None else - Some (Queries.LS.join vs st) + Some (Queries.AD.join vs st) in - List.fold_right reachable es (Some (Queries.LS.empty ())) + List.fold_right reachable es (Some (Queries.AD.empty ())) let forget_reachable ctx st es = @@ -460,8 +473,12 @@ struct |> List.filter_map RV.to_cil_varinfo |> List.map Cil.var | Some rs -> - Queries.LS.elements rs - |> List.map Mval.Exp.to_cil + let to_cil addr xs = + match addr with + | Queries.AD.Addr.Addr addr -> (ValueDomain.Addr.Mval.to_cil addr) :: xs + | _ -> xs + in + Queries.AD.fold to_cil rs [] in List.fold_left (fun st lval -> invalidate_one ask ctx st lval From 4e7b21b7d4c411eaf3576438ac599df91a1a2af9 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 24 Aug 2023 12:20:21 +0200 Subject: [PATCH 0502/1312] Try to check and warn only upon dereferences Also slightly change some warning messages --- src/analyses/memOutOfBounds.ml | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index cfab5c8b73..9a03968ca3 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -101,7 +101,17 @@ struct else match lval with | Var _, _ -> () - | Mem e, _ -> check_exp_for_oob_access ctx e + | Mem e, _ -> + begin match e with + | Lval lval -> check_lval_for_oob_access ctx lval + | BinOp (PlusPI as binop, e1, e2, t) + | BinOp (MinusPI as binop, e1, e2, t) + | BinOp (IndexPI as binop, e1, e2, t) -> + check_binop_exp ctx binop e1 e2 t; + check_exp_for_oob_access ctx e1; + check_exp_for_oob_access ctx e2 + | _ -> check_exp_for_oob_access ctx e + end and check_exp_for_oob_access ctx exp = match exp with @@ -117,7 +127,6 @@ struct | UnOp (_, e, _) | CastE (_, e) -> check_exp_for_oob_access ctx e | BinOp (bop, e1, e2, t) -> - check_binop_exp ctx bop e1 e2 t; check_exp_for_oob_access ctx e1; check_exp_for_oob_access ctx e2 | Question (e1, e2, e3, _) -> @@ -141,14 +150,14 @@ struct begin match VDQ.ID.is_top ptr_size, VDQ.ID.is_top offset_size with | true, _ -> AS.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Pointer (%a) size in expression %a not known. Memory out-of-bounds access might occur" d_exp e1 d_exp binopexp + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer %a in expression %a not known. Memory out-of-bounds access might occur" d_exp e1 d_exp binopexp | _, true -> AS.svcomp_may_invalid_deref := true; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Operand value for pointer arithmetic in expression %a not known. Memory out-of-bounds access might occur" d_exp binopexp | false, false -> if ptr_size < offset_size then begin AS.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Pointer size (%a) in expression %a is smaller than offset (%a) for pointer arithmetic. Memory out-of-bounds access must occur" VDQ.ID.pretty ptr_size d_exp binopexp VDQ.ID.pretty offset_size + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer %a in expression %a is smaller than offset %a for pointer arithmetic. Memory out-of-bounds access must occur" VDQ.ID.pretty ptr_size d_exp binopexp VDQ.ID.pretty offset_size end end | _ -> () From 2e4b9f34cd69da48af447b178a438f45fa01efd7 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Fri, 25 Aug 2023 11:25:31 +0300 Subject: [PATCH 0503/1312] Refactor everything until memLeak --- src/analyses/apron/relationAnalysis.apron.ml | 35 +++++++++--------- src/analyses/base.ml | 5 +-- src/analyses/extractPthread.ml | 39 ++++++++++---------- src/analyses/fileUse.ml | 13 +++---- src/analyses/mallocFresh.ml | 11 ++---- src/analyses/malloc_null.ml | 38 +++++++++---------- src/analyses/memLeak.ml | 12 +++--- 7 files changed, 72 insertions(+), 81 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index d93a96e1b2..c096ba8e6c 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -159,11 +159,10 @@ struct ) | (Mem v, NoOffset) -> begin match ask.f (Queries.MayPointToA v) with - | a when Queries.AD.is_top a -> - st - | a -> - let lvals = Queries.AD.to_mval a in - let ass' = List.map (fun lv -> assign_to_global_wrapper ask getg sideg st (ValueDomain.Addr.Mval.to_cil lv) f) lvals in + | ad when Queries.AD.is_top ad -> st + | ad -> + let mvals = Queries.AD.to_mval ad in + let ass' = List.map (fun mval -> assign_to_global_wrapper ask getg sideg st (ValueDomain.Addr.Mval.to_cil mval) f) mvals in List.fold_right D.join ass' (D.bot ()) end (* Ignoring all other assigns *) @@ -275,12 +274,12 @@ struct VS.exists (fun v -> List.mem v.vid locals_id && RD.Tracked.varinfo_tracked v) reachable_from_args let reachable_from_args ctx args = - let vs e = + let to_vs e = ctx.ask (ReachableFromA e) |> LockDomain.MayLocksetNoRW.to_var_may |> VS.of_list in - List.fold (fun ls e -> VS.join ls (vs e)) (VS.empty ()) args + List.fold (fun vs e -> VS.join vs (to_vs e)) (VS.empty ()) args let pass_to_callee fundec any_local_reachable var = (* TODO: currently, we pass all locals of the caller to the callee, provided one of them is reachbale to preserve relationality *) @@ -454,11 +453,11 @@ struct match st with | None -> None | Some st -> - let vs = ask.f (Queries.ReachableFromA e) in - if Queries.AD.is_top vs then + let ad = ask.f (Queries.ReachableFromA e) in + if Queries.AD.is_top ad then None else - Some (Queries.AD.join vs st) + Some (Queries.AD.join ad st) in List.fold_right reachable es (Some (Queries.AD.empty ())) @@ -472,13 +471,13 @@ struct RD.vars st.rel |> List.filter_map RV.to_cil_varinfo |> List.map Cil.var - | Some rs -> - let to_cil addr xs = + | Some ad -> + let to_cil addr rs = match addr with - | Queries.AD.Addr.Addr addr -> (ValueDomain.Addr.Mval.to_cil addr) :: xs - | _ -> xs + | Queries.AD.Addr.Addr mval -> (ValueDomain.Addr.Mval.to_cil mval) :: rs + | _ -> rs in - Queries.AD.fold to_cil rs [] + Queries.AD.fold to_cil ad [] in List.fold_left (fun st lval -> invalidate_one ask ctx st lval @@ -536,9 +535,9 @@ struct | _, _ -> let lvallist e = match ask.f (Queries.MayPointToA e) with - | a when Queries.AD.is_top a -> [] - | a -> - Queries.AD.to_mval a + | ad when Queries.AD.is_top ad -> [] + | ad -> + Queries.AD.to_mval ad |> List.map ValueDomain.Addr.Mval.to_cil in let shallow_addrs = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = false } args in diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 7967b10df2..a4a1262589 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1323,9 +1323,8 @@ struct | Bot -> Queries.Result.bot q (* TODO: remove *) | Address a -> let a' = AD.remove Addr.UnknownPtr a in (* run reachable_vars without unknown just to be safe: TODO why? *) - let xs = reachable_vars (Analyses.ask_of_ctx ctx) [a'] ctx.global ctx.local in - let addrs = List.fold_left (Q.AD.join) (Q.AD.empty ()) xs in - addrs + let addrs = reachable_vars (Analyses.ask_of_ctx ctx) [a'] ctx.global ctx.local in + List.fold_left (Q.AD.join) (Q.AD.empty ()) addrs | _ -> Q.AD.empty () end | Q.ReachableUkTypes e -> begin diff --git a/src/analyses/extractPthread.ml b/src/analyses/extractPthread.ml index 2cba97425d..9c08269058 100644 --- a/src/analyses/extractPthread.ml +++ b/src/analyses/extractPthread.ml @@ -877,15 +877,14 @@ module Spec : Analyses.MCPSpec = struct module ExprEval = struct let eval_ptr ctx exp = - let a = ctx.ask (Queries.MayPointToA exp) in - if (not (Queries.AD.is_top a)) && Queries.AD.cardinal a > 0 then - let a' = - if Queries.AD.mem UnknownPtr a - then (* UNSOUND *) - Queries.AD.remove UnknownPtr a - else a - in - Queries.AD.to_var_may a' + let ad = ctx.ask (Queries.MayPointToA exp) in + if (not (Queries.AD.is_top ad)) && Queries.AD.cardinal ad > 0 then + if Queries.AD.mem UnknownPtr ad + then (* UNSOUND *) + Queries.AD.remove UnknownPtr ad + |> Queries.AD.to_var_may + else + Queries.AD.to_var_may ad else [] @@ -1121,16 +1120,16 @@ module Spec : Analyses.MCPSpec = struct let ad = ctx.ask (Queries.ReachableFromA func) in Queries.AD.filter (function - | Queries.AD.Addr.Addr addr -> - isFunctionType (ValueDomain.Addr.Mval.type_of addr) + | Queries.AD.Addr.Addr mval -> + isFunctionType (ValueDomain.Mval.type_of mval) | _ -> false) ad in let thread_fun = - Queries.AD.fold (fun addr acc -> + Queries.AD.fold (fun addr vars -> match addr with - | Queries.AD.Addr.Addr (v,_) -> v :: acc - | _ -> acc + | Queries.AD.Addr.Addr (v,_) -> v :: vars + | _ -> vars ) funs_ad [] |> List.unique ~eq:(fun a b -> a.vid = b.vid) |> List.hd @@ -1255,12 +1254,12 @@ module Spec : Analyses.MCPSpec = struct let tasks = ctx.global tasks_var in (* TODO: optimize finding *) let tasks_f = - Tasks.filter - (fun (fs, f_d) -> Queries.AD.exists (function - | Queries.AD.Addr.Addr (ls_f, _) -> ls_f = f - | _ -> false) - fs) - tasks + let var_in_ad ad f = Queries.AD.exists (function + | Queries.AD.Addr.Addr (ls_f,_) -> ls_f = f + | _ -> false + ) ad + in + Tasks.filter (fun (ad,_) -> var_in_ad ad f) tasks in let f_d = snd (Tasks.choose tasks_f) in [ { f_d with pred = d.pred } ] diff --git a/src/analyses/fileUse.ml b/src/analyses/fileUse.ml index 06b8d43c40..d4635722b9 100644 --- a/src/analyses/fileUse.ml +++ b/src/analyses/fileUse.ml @@ -24,23 +24,22 @@ struct (* queries *) let query ctx (type a) (q: a Queries.t) = match q with - | Queries.MayPointToA exp -> if M.tracing then M.tracel "file" "query MayPointTo: %a" d_plainexp exp; Queries.Result.top q + | Queries.MayPointToA exp -> if M.tracing then M.tracel "file" "query MayPointToA: %a" d_plainexp exp; Queries.Result.top q | _ -> Queries.Result.top q - let query_lv (ask: Queries.ask) exp = + let query_ad (ask: Queries.ask) exp = match ask.f (Queries.MayPointToA exp) with - | l when not (Queries.AD.is_top l) -> - Queries.AD.elements l + | ad when not (Queries.AD.is_top ad) -> Queries.AD.elements ad | _ -> [] let print_query_lv ?msg:(msg="") ask exp = - let xs = query_lv ask exp in (* MayPointTo -> LValSet *) + let addrs = query_ad ask exp in (* MayPointTo -> LValSet *) let pretty_key = function | Queries.AD.Addr.Addr (v,o) -> Pretty.text (D.string_of_key (v, ValueDomain.Addr.Offs.to_exp o)) | _ -> Pretty.text "" in - if M.tracing then M.tracel "file" "%s MayPointToA %a = [%a]" msg d_exp exp (Pretty.docList ~sep:(Pretty.text ", ") pretty_key) xs + if M.tracing then M.tracel "file" "%s MayPointToA %a = [%a]" msg d_exp exp (Pretty.docList ~sep:(Pretty.text ", ") pretty_key) addrs let eval_fv ask exp: varinfo option = - match query_lv ask exp with + match query_ad ask exp with | [addr] -> Queries.AD.Addr.to_var_may addr | _ -> None diff --git a/src/analyses/mallocFresh.ml b/src/analyses/mallocFresh.ml index a7c1afb35e..3bea7b8c73 100644 --- a/src/analyses/mallocFresh.ml +++ b/src/analyses/mallocFresh.ml @@ -19,15 +19,12 @@ struct let assign_lval (ask: Queries.ask) lval local = match ask.f (MayPointToA (AddrOf lval)) with - | ls when Queries.AD.is_top ls -> - D.empty () - | ls when Queries.AD.exists (function + | ad when Queries.AD.is_top ad -> D.empty () + | ad when Queries.AD.exists (function | Queries.AD.Addr.Addr (v,_) -> not (D.mem v local) && (v.vglob || ThreadEscape.has_escaped ask v) | _ -> false - ) ls -> - D.empty () - | _ -> - local + ) ad -> D.empty () + | _ -> local let assign ctx lval rval = assign_lval (Analyses.ask_of_ctx ctx) lval ctx.local diff --git a/src/analyses/malloc_null.ml b/src/analyses/malloc_null.ml index 2bebd5c0f6..feb1d97a51 100644 --- a/src/analyses/malloc_null.ml +++ b/src/analyses/malloc_null.ml @@ -38,11 +38,11 @@ struct match e with | Lval (Var v, offs) -> begin match a.f (Queries.MayPointToA (mkAddrOf (Var v,offs))) with - | a when not (Queries.AD.is_top a) -> + | ad when not (Queries.AD.is_top ad) -> Queries.AD.iter (function - | Queries.AD.Addr.Addr addr -> warn_lval st addr + | Queries.AD.Addr.Addr mval -> warn_lval st mval | _ -> () - ) a + ) ad | _ -> () end | _ -> () @@ -96,13 +96,13 @@ struct let reachable = let do_exp e = match ask.f (Queries.ReachableFromA e) with - | a when not (Queries.AD.is_top a) -> - let to_extra addr xs = + | ad when not (Queries.AD.is_top ad) -> + let to_extra addr ads = match addr with - | Queries.AD.Addr.Addr addr -> AD.of_mval addr :: xs - | _ -> xs + | Queries.AD.Addr.Addr mval -> AD.of_mval mval :: ads + | _ -> ads in - Queries.AD.fold to_extra a [] + Queries.AD.fold to_extra ad [] (* Ignore soundness warnings, as invalidation proper will raise them. *) | _ -> [] in @@ -119,8 +119,8 @@ struct let get_concrete_lval (ask: Queries.ask) (lval:lval) = match ask.f (Queries.MayPointToA (mkAddrOf lval)) with - | a when Queries.AD.cardinal a = 1 && not (Queries.AD.mem UnknownPtr a) -> - Queries.AD.Addr.to_mval (Queries.AD.choose a) + | ad when Queries.AD.cardinal ad = 1 && not (Queries.AD.mem UnknownPtr ad) -> + Queries.AD.Addr.to_mval (Queries.AD.choose ad) | _ -> None let get_concrete_exp (exp:exp) gl (st:D.t) = @@ -131,13 +131,13 @@ struct let might_be_null (ask: Queries.ask) lv gl st = match ask.f (Queries.MayPointToA (mkAddrOf lv)) with - | a when not (Queries.AD.is_top a) -> + | ad when not (Queries.AD.is_top ad) -> let one_addr_might = function - | Queries.AD.Addr.Addr addr -> - D.exists (fun x -> GobOption.exists (fun x -> is_prefix_of addr x) (Addr.to_mval x)) st + | Queries.AD.Addr.Addr mval -> + D.exists (fun addr -> GobOption.exists (fun x -> is_prefix_of mval x) (Addr.to_mval addr)) st | _ -> false in - Queries.AD.exists one_addr_might a + Queries.AD.exists one_addr_might ad | _ -> false (* @@ -149,8 +149,8 @@ struct warn_deref_exp (Analyses.ask_of_ctx ctx) ctx.local (Lval lval) ; warn_deref_exp (Analyses.ask_of_ctx ctx) ctx.local rval; match get_concrete_exp rval ctx.global ctx.local, get_concrete_lval (Analyses.ask_of_ctx ctx) lval with - | Some rv, Some addr when might_be_null (Analyses.ask_of_ctx ctx) rv ctx.global ctx.local -> - D.add (Addr.of_mval addr) ctx.local + | Some rv, Some mval when might_be_null (Analyses.ask_of_ctx ctx) rv ctx.global ctx.local -> + D.add (Addr.of_mval mval) ctx.local | _ -> ctx.local let branch ctx (exp:exp) (tv:bool) : D.t = @@ -191,7 +191,7 @@ struct match lval, D.mem (return_addr ()) au with | Some lv, true -> begin match get_concrete_lval (Analyses.ask_of_ctx ctx) lv with - | Some addr -> D.add (Addr.of_mval addr) ctx.local + | Some mval -> D.add (Addr.of_mval mval) ctx.local | _ -> ctx.local end | _ -> ctx.local @@ -204,9 +204,9 @@ struct | Malloc _, Some lv -> begin match get_concrete_lval (Analyses.ask_of_ctx ctx) lv with - | Some addr -> + | Some mval -> ctx.split ctx.local [Events.SplitBranch ((Lval lv), true)]; - ctx.split (D.add (Addr.of_mval addr) ctx.local) [Events.SplitBranch ((Lval lv), false)]; + ctx.split (D.add (Addr.of_mval mval) ctx.local) [Events.SplitBranch ((Lval lv), false)]; raise Analyses.Deadcode | _ -> ctx.local end diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index ce0c047d3e..660d9ba591 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -50,14 +50,12 @@ struct end | Free ptr -> begin match ctx.ask (Queries.MayPointToA ptr) with - | a when not (Queries.AD.is_top a) && not (Queries.AD.mem UnknownPtr a) && Queries.AD.cardinal a = 1 -> + | ad when not (Queries.AD.is_top ad) && Queries.AD.cardinal ad = 1 -> (* Note: Need to always set "ana.malloc.unique_address_count" to a value > 0 *) - let unique_pointed_to_heap_vars = (* TODO: no need for fold due to a being singleton *) - Queries.AD.fold (fun addr s -> - match addr with - | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsMultiple v) -> D.add v s - | _ -> s - ) a (D.empty ()) + let unique_pointed_to_heap_vars = + match Queries.AD.choose ad with + | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsMultiple v) -> D.singleton v + | _ -> D.empty () in D.diff state unique_pointed_to_heap_vars | _ -> state From 641de7dfe06d68630a18d320634cd82710b5efd3 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Fri, 25 Aug 2023 16:47:30 +0300 Subject: [PATCH 0504/1312] Refactor everything after memLeak --- src/analyses/accessAnalysis.ml | 16 ++++----- src/analyses/modifiedSinceLongjmp.ml | 16 ++++----- src/analyses/mutexAnalysis.ml | 19 +++++------ src/analyses/mutexEventsAnalysis.ml | 4 +-- src/analyses/poisonVariables.ml | 50 ++++++++++++++-------------- src/analyses/raceAnalysis.ml | 12 +++---- src/analyses/spec.ml | 7 ++-- src/analyses/threadEscape.ml | 18 +++++----- src/analyses/uninit.ml | 12 +++---- src/analyses/useAfterFree.ml | 34 +++++++++---------- src/analyses/varEq.ml | 30 ++++++++--------- src/cdomains/mvalMapDomain.ml | 14 ++++---- src/domains/events.ml | 4 +-- 13 files changed, 117 insertions(+), 119 deletions(-) diff --git a/src/analyses/accessAnalysis.ml b/src/analyses/accessAnalysis.ml index 500a6e1494..cf024afb10 100644 --- a/src/analyses/accessAnalysis.ml +++ b/src/analyses/accessAnalysis.ml @@ -34,8 +34,8 @@ struct let do_access (ctx: (D.t, G.t, C.t, V.t) ctx) (kind:AccessKind.t) (reach:bool) (e:exp) = if M.tracing then M.trace "access" "do_access %a %a %B\n" d_exp e AccessKind.pretty kind reach; let reach_or_mpt: _ Queries.t = if reach then ReachableFromA e else MayPointToA e in - let ls = ctx.ask reach_or_mpt in - ctx.emit (Access {exp=e; lvals=ls; kind; reach}) + let ad = ctx.ask reach_or_mpt in + ctx.emit (Access {exp=e; ad; kind; reach}) (** Three access levels: + [deref=false], [reach=false] - Access [exp] without dereferencing, used for all normal reads and all function call arguments. @@ -137,18 +137,18 @@ struct let event ctx e octx = match e with - | Events.Access {lvals; kind; _} when !collect_local && !AnalysisState.postsolving -> - let events = Queries.AD.fold (fun addr acc -> + | Events.Access {ad; kind; _} when !collect_local && !AnalysisState.postsolving -> + let events = Queries.AD.fold (fun addr es -> match addr with | Queries.AD.Addr.Addr (var, offs) -> let coffs = ValueDomain.Offs.to_cil offs in let access: AccessDomain.Event.t = {var_opt = (Some var); offs_opt = (Some coffs); kind} in - G.add access acc + G.add access es | UnknownPtr -> let access: AccessDomain.Event.t = {var_opt = None; offs_opt = None; kind} in - G.add access acc - | _ -> acc - ) lvals (G.empty ()) + G.add access es + | _ -> es + ) ad (G.empty ()) in ctx.sideg ctx.node events | _ -> diff --git a/src/analyses/modifiedSinceLongjmp.ml b/src/analyses/modifiedSinceLongjmp.ml index 836cf6f827..0375bd3f74 100644 --- a/src/analyses/modifiedSinceLongjmp.ml +++ b/src/analyses/modifiedSinceLongjmp.ml @@ -29,16 +29,16 @@ struct else Queries.LS.fold (fun (v, _) acc -> if is_relevant v then VS.add v acc else acc) ls (VS.empty ()) - let relevants_from_ad ls = + let relevants_from_ad ad = (* TODO: what about AD with both known and unknown pointers? *) - if Queries.AD.is_top ls then + if Queries.AD.is_top ad then VS.top () else - Queries.AD.fold (fun addr acc -> + Queries.AD.fold (fun addr vs -> match addr with - | Queries.AD.Addr.Addr (v, _) -> if is_relevant v then VS.add v acc else acc - | _ -> acc - ) ls (VS.empty ()) + | Queries.AD.Addr.Addr (v,_) -> if is_relevant v then VS.add v vs else vs + | _ -> vs + ) ad (VS.empty ()) (* transfer functions *) let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = @@ -73,8 +73,8 @@ struct let event ctx (e: Events.t) octx = match e with - | Access {lvals; kind = Write; _} -> - add_to_all_defined (relevants_from_ad lvals) ctx.local + | Access {ad; kind = Write; _} -> + add_to_all_defined (relevants_from_ad ad) ctx.local | _ -> ctx.local end diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 7f504badf2..7d8298a0a4 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -293,7 +293,7 @@ struct let event ctx e octx = match e with - | Events.Access {exp; lvals; kind; _} when ThreadFlag.has_ever_been_multi (Analyses.ask_of_ctx ctx) -> (* threadflag query in post-threadspawn ctx *) + | Events.Access {exp; ad; kind; _} when ThreadFlag.has_ever_been_multi (Analyses.ask_of_ctx ctx) -> (* threadflag query in post-threadspawn ctx *) let is_recovered_to_st = not (ThreadFlag.is_currently_multi (Analyses.ask_of_ctx ctx)) in (* must use original (pre-assign, etc) ctx queries *) let old_access var_opt = @@ -327,19 +327,18 @@ struct in let module AD = Queries.AD in let has_escaped g = octx.ask (Queries.MayEscape g) in - let on_lvals ad = - let f addr = - match addr with - | AD.Addr.Addr (g,o) when g.vglob || has_escaped g -> old_access (Some g) + let on_ad ad = + let f = function + | AD.Addr.Addr (g,_) when g.vglob || has_escaped g -> old_access (Some g) | UnknownPtr -> old_access None - | _ -> () + | _ -> () in AD.iter f ad in - begin match lvals with + begin match ad with | ad when not (AD.is_top ad) -> (* the case where the points-to set is non top and does not contain unknown values *) - on_lvals ad + on_ad ad | ad -> (* the case where the points-to set is non top and contains unknown values *) (* now we need to access all fields that might be pointed to: is this correct? *) @@ -354,9 +353,9 @@ struct if Queries.TS.exists f ts then old_access None end; - on_lvals ad + on_ad ad (* | _ -> - old_access None None *) + old_access None None *) (* TODO: what about this case? *) end; ctx.local | _ -> diff --git a/src/analyses/mutexEventsAnalysis.ml b/src/analyses/mutexEventsAnalysis.ml index e5839741b2..2b8ebffc61 100644 --- a/src/analyses/mutexEventsAnalysis.ml +++ b/src/analyses/mutexEventsAnalysis.ml @@ -20,8 +20,8 @@ struct let eval_exp_addr (a: Queries.ask) exp = a.f (Queries.MayPointToA exp) - let lock ctx rw may_fail nonzero_return_when_aquired a lv arg = - match lv with + let lock ctx rw may_fail nonzero_return_when_aquired a lv_opt arg = + match lv_opt with | None -> Queries.AD.iter (fun e -> ctx.split () [Events.Lock (e, rw)] diff --git a/src/analyses/poisonVariables.ml b/src/analyses/poisonVariables.ml index 49bd338538..5f2905ffb1 100644 --- a/src/analyses/poisonVariables.ml +++ b/src/analyses/poisonVariables.ml @@ -15,11 +15,11 @@ struct let context _ _ = () - let check_lval tainted ((v, offset): Queries.LS.elt) = + let check_mval tainted ((v, offset): Queries.LS.elt) = if not v.vglob && VS.mem v tainted then M.warn ~category:(Behavior (Undefined Other)) "Reading poisonous variable %a" CilType.Varinfo.pretty v - let rem_lval tainted ((v, offset): Queries.LS.elt) = match offset with + let rem_mval tainted ((v, offset): Queries.LS.elt) = match offset with | `NoOffset -> VS.remove v tainted | _ -> tainted (* If there is an offset, it is a bit harder to remove, as we don't know where the indeterminate value is *) @@ -38,11 +38,11 @@ struct ) ctx.local ) - let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = + let enter ctx (_:lval option) (_:fundec) (args:exp list) : (D.t * D.t) list = if VS.is_empty ctx.local then [ctx.local,ctx.local] else ( - let reachable_from_args = List.fold (fun ls e -> Queries.AD.join ls (ctx.ask (ReachableFromA e))) (Queries.AD.empty ()) args in + let reachable_from_args = List.fold (fun ad e -> Queries.AD.join ad (ctx.ask (ReachableFromA e))) (Queries.AD.empty ()) args in if Queries.AD.is_top reachable_from_args || VS.is_top ctx.local then [ctx.local, ctx.local] else @@ -82,31 +82,31 @@ struct () ) longjmp_nodes; D.join modified_locals ctx.local - | Access {lvals; kind = Read; _} -> + | Access {ad; kind = Read; _} -> (* TODO: what about AD with both known and unknown pointers? *) - if Queries.AD.is_top lvals then ( - if not (VS.is_empty octx.local) then + begin match ad with + | ad when Queries.AD.is_top ad && not (VS.is_empty octx.local) -> M.warn ~category:(Behavior (Undefined Other)) "reading unknown memory location, may be tainted!" - ) - else ( - Queries.AD.iter (function - (* Use original access state instead of current with removed written vars. *) - | Queries.AD.Addr.Addr (v,o) -> check_lval octx.local (v, ValueDomain.Offs.to_exp o) - | _ -> () - ) lvals - ); + | ad -> + Queries.AD.iter (function + (* Use original access state instead of current with removed written vars. *) + | Queries.AD.Addr.Addr (v,o) -> check_mval octx.local (v, ValueDomain.Offs.to_exp o) + | _ -> () + ) ad + end; ctx.local - | Access {lvals; kind = Write; _} -> + | Access {ad; kind = Write; _} -> (* TODO: what about AD with both known and unknown pointers? *) - if Queries.AD.is_top lvals then - ctx.local - else ( - Queries.AD.fold (fun addr acc -> - match addr with - | Queries.AD.Addr.Addr (v,o) -> rem_lval acc (v, ValueDomain.Offs.to_exp o) - | _ -> acc - ) lvals ctx.local - ) + begin match ad with + | ad when Queries.AD.is_top ad -> + ctx.local + | ad -> + Queries.AD.fold (fun addr vs -> + match addr with + | Queries.AD.Addr.Addr (v,o) -> rem_mval vs (v, ValueDomain.Offs.to_exp o) + | _ -> vs + ) ad ctx.local + end | _ -> ctx.local end diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index d911dafd91..f3301a4659 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -131,7 +131,7 @@ struct let event ctx e octx = match e with - | Events.Access {exp=e; lvals; kind; reach} when ThreadFlag.is_currently_multi (Analyses.ask_of_ctx ctx) -> (* threadflag query in post-threadspawn ctx *) + | Events.Access {exp=e; ad; kind; reach} when ThreadFlag.is_currently_multi (Analyses.ask_of_ctx ctx) -> (* threadflag query in post-threadspawn ctx *) (* must use original (pre-assign, etc) ctx queries *) let conf = 110 in let module AD = Queries.AD in @@ -151,7 +151,7 @@ struct let has_escaped g = octx.ask (Queries.MayEscape g) in (* The following function adds accesses to the lval-set ls -- this is the common case if we have a sound points-to set. *) - let on_lvals ad includes_uk = + let on_ad ad includes_uk = let conf = if reach then conf - 20 else conf in let conf = if includes_uk then conf - 10 else conf in let f addr = @@ -164,10 +164,10 @@ struct in AD.iter f ad in - begin match lvals with + begin match ad with | ad when not (AD.is_top ad) -> (* the case where the points-to set is non top and does not contain unknown values *) - on_lvals ad false + on_ad ad false | ad -> (* the case where the points-to set is non top and contains unknown values *) let includes_uk = ref false in @@ -185,9 +185,9 @@ struct in Queries.TS.iter f ts end; - on_lvals ad !includes_uk + on_ad ad !includes_uk (* | _ -> - add_access (conf - 60) None *) + add_access (conf - 60) None *) (* TODO: what about this case? *) end; ctx.local | _ -> diff --git a/src/analyses/spec.ml b/src/analyses/spec.ml index 568482631c..45a6d99d38 100644 --- a/src/analyses/spec.ml +++ b/src/analyses/spec.ml @@ -203,14 +203,13 @@ struct match q with | _ -> Queries.Result.top q - let query_lv ask exp = + let query_addrs ask exp = match ask (Queries.MayPointToA exp) with - | l when not (Queries.AD.is_top l) -> - Queries.AD.elements l + | ad when not (Queries.AD.is_top ad) -> Queries.AD.elements ad | _ -> [] let eval_fv ask exp: varinfo option = - match query_lv ask exp with + match query_addrs ask exp with | [addr] -> Queries.AD.Addr.to_var_may addr | _ -> None diff --git a/src/analyses/threadEscape.ml b/src/analyses/threadEscape.ml index 3e2b41d903..4e7ec37c8a 100644 --- a/src/analyses/threadEscape.ml +++ b/src/analyses/threadEscape.ml @@ -27,30 +27,30 @@ struct let reachable (ask: Queries.ask) e: D.t = match ask.f (Queries.ReachableFromA e) with - | a when not (Queries.AD.is_top a) -> + | ad when not (Queries.AD.is_top ad) -> let to_extra addr set = match addr with | Queries.AD.Addr.Addr (v,_) -> D.add v set | _ -> set in - Queries.AD.fold to_extra a (D.empty ()) + Queries.AD.fold to_extra ad (D.empty ()) (* Ignore soundness warnings, as invalidation proper will raise them. *) - | a -> - if M.tracing then M.tracel "escape" "reachable %a: %a\n" d_exp e Queries.AD.pretty a; + | ad -> + if M.tracing then M.tracel "escape" "reachable %a: %a\n" d_exp e Queries.AD.pretty ad; D.empty () let mpt (ask: Queries.ask) e: D.t = match ask.f (Queries.MayPointToA e) with - | a when not (AD.is_top a) -> - let to_extra addr set = + | ad when not (AD.is_top ad) -> + let to_extra addr set = match addr with | AD.Addr.Addr (v,_) -> D.add v set | _ -> set in - AD.fold to_extra (AD.remove UnknownPtr a) (D.empty ()) + AD.fold to_extra (AD.remove UnknownPtr ad) (D.empty ()) (* Ignore soundness warnings, as invalidation proper will raise them. *) - | a -> - if M.tracing then M.tracel "escape" "mpt %a: %a\n" d_exp e AD.pretty a; + | ad -> + if M.tracing then M.tracel "escape" "mpt %a: %a\n" d_exp e AD.pretty ad; D.empty () let thread_id ctx = diff --git a/src/analyses/uninit.ml b/src/analyses/uninit.ml index 1f04964d58..20f5667f46 100644 --- a/src/analyses/uninit.ml +++ b/src/analyses/uninit.ml @@ -196,13 +196,13 @@ struct let reachable = let do_exp e = match ask.f (Queries.ReachableFromA e) with - | a when not (Queries.AD.is_top a) -> - let to_extra addr xs = - match addr with - | Queries.AD.Addr.Addr addr -> AD.of_mval addr :: xs - | _ -> xs + | ad when not (Queries.AD.is_top ad) -> + let to_extra ad ads = + match ad with + | Queries.AD.Addr.Addr mval -> AD.of_mval mval :: ads + | _ -> ads in - Queries.AD.fold to_extra a [] + Queries.AD.fold to_extra ad [] (* Ignore soundness warnings, as invalidation proper will raise them. *) | _ -> [] in diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index 4e28c49771..f1fcdd8a7b 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -83,17 +83,17 @@ struct | Mem _ -> mkAddrOf lval (* Take the lval's address if its lhost is of the form *p, where p is a ptr *) in match ctx.ask (Queries.MayPointToA lval_to_query) with - | a when not (Queries.AD.is_top a) -> - let warn_for_heap_var var = - if D.mem var state then - M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "lval (%s) in \"%s\" points to a maybe freed memory region" var.vname transfer_fn_name + | ad when not (Queries.AD.is_top ad) -> + let warn_for_heap_var v = + if D.mem v state then + M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "lval (%s) in \"%s\" points to a maybe freed memory region" v.vname transfer_fn_name in let pointed_to_heap_vars = - Queries.AD.fold (fun addr l -> + Queries.AD.fold (fun addr vars -> match addr with - | Queries.AD.Addr.Addr (var, _) when ctx.ask (Queries.IsHeapVar var) -> var :: l - | _ -> l - ) a [] + | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsHeapVar v) -> v :: vars + | _ -> vars + ) ad [] in List.iter warn_for_heap_var pointed_to_heap_vars; (* Warn for all heap vars that the lval possibly points to *) (* Warn for a potential multi-threaded UAF for all heap vars that the lval possibly points to *) @@ -156,15 +156,15 @@ struct if D.is_empty caller_state then [caller_state, caller_state] else ( - let reachable_from_args = List.fold_left (fun acc arg -> Queries.AD.join acc (ctx.ask (ReachableFromA arg))) (Queries.AD.empty ()) args in + let reachable_from_args = List.fold_left (fun ad arg -> Queries.AD.join ad (ctx.ask (ReachableFromA arg))) (Queries.AD.empty ()) args in if Queries.AD.is_top reachable_from_args || D.is_top caller_state then [caller_state, caller_state] else let reachable_vars = - let get_vars addr vs = + let get_vars addr vars = match addr with - | Queries.AD.Addr.Addr (v,_) -> v :: vs - | _ -> vs + | Queries.AD.Addr.Addr (v,_) -> v :: vars + | _ -> vars in Queries.AD.fold get_vars reachable_from_args [] in @@ -188,13 +188,13 @@ struct match desc.special arglist with | Free ptr -> begin match ctx.ask (Queries.MayPointToA ptr) with - | a when not (Queries.AD.is_top a) -> + | ad when not (Queries.AD.is_top ad) -> let pointed_to_heap_vars = - Queries.AD.fold (fun addr s -> + Queries.AD.fold (fun addr state -> match addr with - | Queries.AD.Addr.Addr (var, _) when ctx.ask (Queries.IsHeapVar var) -> D.add var s - | _ -> s - ) a (D.empty ()) + | Queries.AD.Addr.Addr (var,_) when ctx.ask (Queries.IsHeapVar var) -> D.add var state + | _ -> state + ) ad (D.empty ()) in (* Side-effect the tid that's freeing all the heap vars collected here *) side_effect_mem_free ctx pointed_to_heap_vars (get_current_threadid ctx); diff --git a/src/analyses/varEq.ml b/src/analyses/varEq.ml index edec2f40d4..628faa2ac1 100644 --- a/src/analyses/varEq.ml +++ b/src/analyses/varEq.ml @@ -176,7 +176,7 @@ struct let may_change (ask: Queries.ask) (b:exp) (a:exp) : bool = (*b should be an address of something that changes*) let pt e = ask.f (Queries.MayPointToA e) in - let bls = pt b in + let bad = pt b in let bt = match unrollTypeDeep (Cilfacade.typeOf b) with | TPtr (t,_) -> t @@ -247,7 +247,7 @@ struct | CastE (t,e) -> addrOfExp e | _ -> None in - let lval_is_not_disjoint (v,o) als = + let lval_is_not_disjoint (v,o) aad = let rec oleq o s = match o, s with | `NoOffset, _ -> true @@ -255,21 +255,21 @@ struct | `Index (i1,o), `Index (i2,s) when exp_equal i1 i2 -> oleq o s | _ -> false in - if Queries.AD.is_top als + if Queries.AD.is_top aad then false else Queries.AD.exists (function | Addr (u,s) -> CilType.Varinfo.equal v u && oleq o (Addr.Offs.to_exp s) | _ -> false - ) als + ) aad in - let (als, test) = + let (aad, test) = match addrOfExp a with | None -> (Queries.AD.bot (), false) | Some e -> - let als = pt e in - (als, lval_is_not_disjoint bl als) + let aad = pt e in + (aad, lval_is_not_disjoint bl aad) in - if Queries.AD.is_top als + if Queries.AD.is_top aad then type_may_change_apt a else test || match a with @@ -295,12 +295,12 @@ struct in let r = if Cil.isConstant b then false - else if Queries.AD.is_top bls + else if Queries.AD.is_top bad then ((*Messages.warn ~category:Analyzer "No PT-set: switching to types ";*) type_may_change_apt a ) else Queries.AD.exists (function | Addr (v,o) -> lval_may_change_pt a (v, Addr.Offs.to_exp o) | _ -> false - ) bls + ) bad in (* if r then (Messages.warn ~category:Analyzer ~msg:("Kill " ^sprint 80 (Exp.pretty () a)^" because of "^sprint 80 (Exp.pretty () b)) (); r) @@ -345,11 +345,11 @@ struct Some (v.vglob || (ask.f (Queries.IsMultiple v) || BaseUtil.is_global ask v)) | Lval (Mem e, _) -> begin match ask.f (Queries.MayPointToA e) with - | ls when not (Queries.AD.is_top ls) -> + | ad when not (Queries.AD.is_top ad) -> Some (Queries.AD.exists (function - | Addr (v, _) -> is_global_var ask (Lval (var v)) = Some true + | Addr (v,_) -> is_global_var ask (Lval (var v)) = Some true | _ -> false - ) ls) + ) ad) | _ -> Some true end | CastE (t,e) -> is_global_var ask e @@ -390,8 +390,8 @@ struct let reachables ~deep (ask: Queries.ask) es = let reachable e st = let q = if deep then Queries.ReachableFromA e else Queries.MayPointToA e in - let vs = ask.f q in - Queries.AD.join vs st + let ad = ask.f q in + Queries.AD.join ad st in List.fold_right reachable es (Queries.AD.empty ()) diff --git a/src/cdomains/mvalMapDomain.ml b/src/cdomains/mvalMapDomain.ml index 78357a6d73..28b49de1a5 100644 --- a/src/cdomains/mvalMapDomain.ml +++ b/src/cdomains/mvalMapDomain.ml @@ -281,17 +281,17 @@ struct let keys_from_lval lval (ask: Queries.ask) = (* use MayPointTo query to get all possible pointees of &lval *) (* print_query_lv ctx.ask (AddrOf lval); *) - let query_lv (ask: Queries.ask) exp = match ask.f (Queries.MayPointToA exp) with - | l when not (Queries.AD.is_top l) -> Queries.AD.elements l + let query_addrs (ask: Queries.ask) exp = match ask.f (Queries.MayPointToA exp) with + | ad when not (Queries.AD.is_top ad) -> Queries.AD.elements ad | _ -> [] in let exp = AddrOf lval in - let xs = query_lv ask exp in (* MayPointTo -> LValSet *) - let keys = List.fold (fun l addr -> + let addrs = query_addrs ask exp in (* MayPointTo -> LValSet *) + let keys = List.fold (fun vs addr -> match addr with - | Queries.AD.Addr.Addr (v,o) -> (v, ValueDomain.Offs.to_exp o) :: l - | _ -> l - ) [] xs + | Queries.AD.Addr.Addr (v,o) -> (v, ValueDomain.Offs.to_exp o) :: vs + | _ -> vs + ) [] addrs in let pretty_key k = Pretty.text (string_of_key k) in Messages.debug ~category:Analyzer "MayPointTo %a = [%a]" d_exp exp (Pretty.docList ~sep:(Pretty.text ", ") pretty_key) keys; diff --git a/src/domains/events.ml b/src/domains/events.ml index 41e745ed8a..06561bddbe 100644 --- a/src/domains/events.ml +++ b/src/domains/events.ml @@ -10,7 +10,7 @@ type t = | EnterMultiThreaded | SplitBranch of exp * bool (** Used to simulate old branch-based split. *) | AssignSpawnedThread of lval * ThreadIdDomain.Thread.t (** Assign spawned thread's ID to lval. *) - | Access of {exp: CilType.Exp.t; lvals: Queries.AD.t; kind: AccessKind.t; reach: bool} + | Access of {exp: CilType.Exp.t; ad: Queries.AD.t; kind: AccessKind.t; reach: bool} | Assign of {lval: CilType.Lval.t; exp: CilType.Exp.t} (** Used to simulate old [ctx.assign]. *) (* TODO: unused *) | UpdateExpSplit of exp (** Used by expsplit analysis to evaluate [exp] on post-state. *) | Assert of exp @@ -41,7 +41,7 @@ let pretty () = function | EnterMultiThreaded -> text "EnterMultiThreaded" | SplitBranch (exp, tv) -> dprintf "SplitBranch (%a, %B)" d_exp exp tv | AssignSpawnedThread (lval, tid) -> dprintf "AssignSpawnedThread (%a, %a)" d_lval lval ThreadIdDomain.Thread.pretty tid - | Access {exp; lvals; kind; reach} -> dprintf "Access {exp=%a; lvals=%a; kind=%a; reach=%B}" CilType.Exp.pretty exp Queries.AD.pretty lvals AccessKind.pretty kind reach + | Access {exp; ad; kind; reach} -> dprintf "Access {exp=%a; ad=%a; kind=%a; reach=%B}" CilType.Exp.pretty exp Queries.AD.pretty ad AccessKind.pretty kind reach | Assign {lval; exp} -> dprintf "Assign {lval=%a, exp=%a}" CilType.Lval.pretty lval CilType.Exp.pretty exp | UpdateExpSplit exp -> dprintf "UpdateExpSplit %a" d_exp exp | Assert exp -> dprintf "Assert %a" d_exp exp From f3c3d185c4a275e389f17a237cb117a707ebde87 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Fri, 25 Aug 2023 17:01:55 +0300 Subject: [PATCH 0505/1312] Use MayPointToA and ReachableFromA in taindPartialContexts --- src/analyses/taintPartialContexts.ml | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/analyses/taintPartialContexts.ml b/src/analyses/taintPartialContexts.ml index 76f4af8f9e..453e09b3af 100644 --- a/src/analyses/taintPartialContexts.ml +++ b/src/analyses/taintPartialContexts.ml @@ -6,12 +6,22 @@ open GoblintCil open Analyses +module D = SetDomain.ToppedSet (Mval.Exp) (struct let topname = "All" end) + +let to_mvals ad = + (* TODO: should one handle ad with unknown pointers separately like in (all) other analyses? *) + Queries.AD.fold (fun addr mvals -> + match addr with + | Queries.AD.Addr.Addr (v,o) -> D.add (v, ValueDomain.Offs.to_exp o) mvals + | _ -> mvals + ) ad (D.empty ()) + module Spec = struct include Analyses.IdentitySpec let name () = "taintPartialContexts" - module D = SetDomain.ToppedSet (Mval.Exp) (struct let topname = "All" end) + module D = D module C = Lattice.Unit (* Add Lval or any Lval which it may point to to the set *) @@ -19,7 +29,7 @@ struct let d = ctx.local in (match lval with | (Var v, offs) -> D.add (v, Offset.Exp.of_cil offs) d - | (Mem e, _) -> D.union (ctx.ask (Queries.MayPointTo e)) d + | (Mem e, _) -> D.union (to_mvals (ctx.ask (Queries.MayPointToA e))) d ) (* this analysis is context insensitive*) @@ -84,9 +94,9 @@ struct else deep_addrs in - let d = List.fold_left (fun accD addr -> D.union accD (ctx.ask (Queries.MayPointTo addr))) d shallow_addrs + let d = List.fold_left (fun accD addr -> D.union accD (to_mvals (ctx.ask (Queries.MayPointToA addr)))) d shallow_addrs in - let d = List.fold_left (fun accD addr -> D.union accD (ctx.ask (Queries.ReachableFrom addr))) d deep_addrs + let d = List.fold_left (fun accD addr -> D.union accD (to_mvals (ctx.ask (Queries.ReachableFromA addr)))) d deep_addrs in d From 22da3df00617a328b5c14b8b82456444caf8d602 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Fri, 25 Aug 2023 18:58:56 +0300 Subject: [PATCH 0506/1312] Use MayPointToA for may_point_to in queries Co-authored-by: Simmo Saan --- src/cdomains/valueDomain.ml | 8 ++++---- src/domains/queries.ml | 2 +- src/domains/valueDomainQueries.ml | 3 ++- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/cdomains/valueDomain.ml b/src/cdomains/valueDomain.ml index 20c4f3bf21..83c79a542e 100644 --- a/src/cdomains/valueDomain.ml +++ b/src/cdomains/valueDomain.ml @@ -10,7 +10,7 @@ module M = Messages module BI = IntOps.BigIntOps module MutexAttr = MutexAttrDomain module VDQ = ValueDomainQueries -module LS = VDQ.LS +module AD = VDQ.AD module AddrSetDomain = SetDomain.ToppedSet(Addr)(struct let topname = "All" end) module ArrIdxDomain = IndexDomain @@ -756,9 +756,9 @@ struct match exp, start_of_array_lval with | BinOp(IndexPI, Lval lval, add, _), (Var arr_start_var, NoOffset) when not (contains_pointer add) -> begin match ask.may_point_to (Lval lval) with - | v when LS.cardinal v = 1 && not (LS.is_top v) -> - begin match LS.choose v with - | (var,`Index (i,`NoOffset)) when Cil.isZero (Cil.constFold true i) && CilType.Varinfo.equal var arr_start_var -> + | v when AD.cardinal v = 1 && not (AD.is_top v) -> + begin match AD.choose v with + | AD.Addr.Addr (var,`Index (i,`NoOffset)) when ID.equal_to Z.zero i = `Eq && CilType.Varinfo.equal var arr_start_var -> (* The idea here is that if a must(!) point to arr and we do sth like a[i] we don't want arr to be partitioned according to (arr+i)-&a but according to i instead *) add | _ -> BinOp(MinusPP, exp, StartOf start_of_array_lval, !ptrdiffType) diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 42be039d96..10e9c996da 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -474,7 +474,7 @@ end let to_value_domain_ask (ask: ask) = let eval_int e = ask.f (EvalInt e) in - let may_point_to e = ask.f (MayPointTo e) in + let may_point_to e = ask.f (MayPointToA e) in let is_multiple v = ask.f (IsMultiple v) in { VDQ.eval_int; may_point_to; is_multiple } diff --git a/src/domains/valueDomainQueries.ml b/src/domains/valueDomainQueries.ml index d366e6dda3..8266582ac2 100644 --- a/src/domains/valueDomainQueries.ml +++ b/src/domains/valueDomainQueries.ml @@ -4,6 +4,7 @@ open GoblintCil open BoolDomain module LS = SetDomain.ToppedSet (Mval.Exp) (struct let topname = "All" end) +module AD = PreValueDomain.AD module ID = struct @@ -44,7 +45,7 @@ struct end type eval_int = exp -> ID.t -type may_point_to = exp -> LS.t +type may_point_to = exp -> AD.t type is_multiple = varinfo -> bool (** Subset of queries used by the valuedomain, using a simpler representation. *) From 5cf96d1cf863b21bc258008d5728ff495b7c12d5 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Fri, 25 Aug 2023 19:02:44 +0300 Subject: [PATCH 0507/1312] Remove MayPointTo and ReachableFrom queries with LS --- src/analyses/base.ml | 24 ------------------------ src/domains/queries.ml | 18 ++---------------- 2 files changed, 2 insertions(+), 40 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index a4a1262589..407a3b5965 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1279,16 +1279,6 @@ struct ) | _ -> Queries.Result.top q end - | Q.MayPointTo e -> begin - match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local e with - | Address a -> - let s = addrToLvalSet a in - if AD.mem Addr.UnknownPtr a - then Q.LS.add (dummyFunDec.svar, `NoOffset) s - else s - | Bot -> Queries.Result.bot q (* TODO: remove *) - | _ -> Queries.Result.top q - end | Q.MayPointToA e -> begin match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local e with | Address a -> a @@ -1303,20 +1293,6 @@ struct | Bot -> Queries.Result.bot q (* TODO: remove *) | _ -> Queries.Result.top q end - | Q.ReachableFrom e -> begin - match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local e with - | Top -> Queries.Result.top q - | Bot -> Queries.Result.bot q (* TODO: remove *) - | Address a -> - let a' = AD.remove Addr.UnknownPtr a in (* run reachable_vars without unknown just to be safe *) - let xs = List.map addrToLvalSet (reachable_vars (Analyses.ask_of_ctx ctx) [a'] ctx.global ctx.local) in - let addrs = List.fold_left (Q.LS.join) (Q.LS.empty ()) xs in - if AD.mem Addr.UnknownPtr a then - Q.LS.add (dummyFunDec.svar, `NoOffset) addrs (* add unknown back *) - else - addrs - | _ -> Q.LS.empty () - end | Q.ReachableFromA e -> begin match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local e with | Top -> Queries.Result.top q diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 10e9c996da..6f5838c19b 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -72,9 +72,7 @@ type invariant_context = Invariant.context = { (** GADT for queries with specific result type. *) type _ t = | EqualSet: exp -> ES.t t - | MayPointTo: exp -> LS.t t | MayPointToA: exp -> AD.t t - | ReachableFrom: exp -> LS.t t | ReachableFromA: exp -> AD.t t | ReachableUkTypes: exp -> TS.t t | Regions: exp -> LS.t t @@ -143,9 +141,7 @@ struct (* Cannot group these GADTs... *) | EqualSet _ -> (module ES) | CondVars _ -> (module ES) - | MayPointTo _ -> (module LS) | MayPointToA _ -> (module AD) - | ReachableFrom _ -> (module LS) | ReachableFromA _ -> (module AD) | Regions _ -> (module LS) | MustLockset -> (module LS) @@ -209,9 +205,7 @@ struct (* Cannot group these GADTs... *) | EqualSet _ -> ES.top () | CondVars _ -> ES.top () - | MayPointTo _ -> LS.top () | MayPointToA _ -> AD.top () - | ReachableFrom _ -> LS.top () | ReachableFromA _ -> AD.top () | Regions _ -> LS.top () | MustLockset -> LS.top () @@ -271,10 +265,8 @@ struct (* deriving ord doesn't work for GADTs (t and any_query) so this must be done manually... *) let order = function | Any (EqualSet _) -> 0 - | Any (MayPointTo _) -> 1 - | Any (MayPointToA _) -> 999 - | Any (ReachableFrom _) -> 2 - | Any (ReachableFromA _) -> 666 + | Any (MayPointToA _) -> 1 + | Any (ReachableFromA _) -> 2 | Any (ReachableUkTypes _) -> 3 | Any (Regions _) -> 4 | Any (MayEscape _) -> 5 @@ -329,9 +321,7 @@ struct else match a, b with | Any (EqualSet e1), Any (EqualSet e2) -> CilType.Exp.compare e1 e2 - | Any (MayPointTo e1), Any (MayPointTo e2) -> CilType.Exp.compare e1 e2 | Any (MayPointToA e1), Any (MayPointToA e2) -> CilType.Exp.compare e1 e2 - | Any (ReachableFrom e1), Any (ReachableFrom e2) -> CilType.Exp.compare e1 e2 | Any (ReachableFromA e1), Any (ReachableFromA e2) -> CilType.Exp.compare e1 e2 | Any (ReachableUkTypes e1), Any (ReachableUkTypes e2) -> CilType.Exp.compare e1 e2 | Any (Regions e1), Any (Regions e2) -> CilType.Exp.compare e1 e2 @@ -376,9 +366,7 @@ struct let rec hash_arg = function | Any (EqualSet e) -> CilType.Exp.hash e - | Any (MayPointTo e) -> CilType.Exp.hash e | Any (MayPointToA e) -> CilType.Exp.hash e - | Any (ReachableFrom e) -> CilType.Exp.hash e | Any (ReachableFromA e) -> CilType.Exp.hash e | Any (ReachableUkTypes e) -> CilType.Exp.hash e | Any (Regions e) -> CilType.Exp.hash e @@ -420,9 +408,7 @@ struct let rec pretty () = function | Any (EqualSet e) -> Pretty.dprintf "EqualSet %a" CilType.Exp.pretty e - | Any (MayPointTo e) -> Pretty.dprintf "MayPointTo %a" CilType.Exp.pretty e | Any (MayPointToA e) -> Pretty.dprintf "MayPointToA %a" CilType.Exp.pretty e - | Any (ReachableFrom e) -> Pretty.dprintf "ReachableFrom %a" CilType.Exp.pretty e | Any (ReachableFromA e) -> Pretty.dprintf "ReachableFromA %a" CilType.Exp.pretty e | Any (ReachableUkTypes e) -> Pretty.dprintf "ReachableUkTypes %a" CilType.Exp.pretty e | Any (Regions e) -> Pretty.dprintf "Regions %a" CilType.Exp.pretty e From 9291c8af396cdd6176a5710e3dd7de4810b5588d Mon Sep 17 00:00:00 2001 From: karoliineh Date: Fri, 25 Aug 2023 19:06:39 +0300 Subject: [PATCH 0508/1312] Rename MayPointToA -> MayPointTo --- src/analyses/accessAnalysis.ml | 2 +- src/analyses/apron/relationAnalysis.apron.ml | 6 +++--- src/analyses/base.ml | 2 +- src/analyses/condVars.ml | 2 +- src/analyses/extractPthread.ml | 2 +- src/analyses/fileUse.ml | 6 +++--- src/analyses/mallocFresh.ml | 2 +- src/analyses/malloc_null.ml | 6 +++--- src/analyses/memLeak.ml | 2 +- src/analyses/mutexEventsAnalysis.ml | 2 +- src/analyses/mutexTypeAnalysis.ml | 2 +- src/analyses/pthreadSignals.ml | 2 +- src/analyses/spec.ml | 2 +- src/analyses/taintPartialContexts.ml | 4 ++-- src/analyses/threadEscape.ml | 2 +- src/analyses/uninit.ml | 4 ++-- src/analyses/useAfterFree.ml | 4 ++-- src/analyses/varEq.ml | 8 ++++---- src/cdomains/mvalMapDomain.ml | 2 +- src/domains/queries.ml | 16 ++++++++-------- 20 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/analyses/accessAnalysis.ml b/src/analyses/accessAnalysis.ml index cf024afb10..b13b7bf3ea 100644 --- a/src/analyses/accessAnalysis.ml +++ b/src/analyses/accessAnalysis.ml @@ -33,7 +33,7 @@ struct let do_access (ctx: (D.t, G.t, C.t, V.t) ctx) (kind:AccessKind.t) (reach:bool) (e:exp) = if M.tracing then M.trace "access" "do_access %a %a %B\n" d_exp e AccessKind.pretty kind reach; - let reach_or_mpt: _ Queries.t = if reach then ReachableFromA e else MayPointToA e in + let reach_or_mpt: _ Queries.t = if reach then ReachableFromA e else MayPointTo e in let ad = ctx.ask reach_or_mpt in ctx.emit (Access {exp=e; ad; kind; reach}) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index c096ba8e6c..ef035d3cca 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -158,7 +158,7 @@ struct {st' with rel = rel''} ) | (Mem v, NoOffset) -> - begin match ask.f (Queries.MayPointToA v) with + begin match ask.f (Queries.MayPointTo v) with | ad when Queries.AD.is_top ad -> st | ad -> let mvals = Queries.AD.to_mval ad in @@ -216,7 +216,7 @@ struct | CastE (t,e) -> CastE (t, inner e) | Lval (Var v, off) -> Lval (Var v, off) | Lval (Mem e, NoOffset) -> - begin match ask (Queries.MayPointToA e) with + begin match ask (Queries.MayPointTo e) with | a when not (Queries.AD.is_top a) && (Queries.AD.cardinal a) = 1 -> begin match Queries.AD.Addr.to_mval (Queries.AD.choose a) with | Some mval -> ValueDomain.Addr.Mval.to_cil_exp mval @@ -534,7 +534,7 @@ struct | None -> st) | _, _ -> let lvallist e = - match ask.f (Queries.MayPointToA e) with + match ask.f (Queries.MayPointTo e) with | ad when Queries.AD.is_top ad -> [] | ad -> Queries.AD.to_mval ad diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 407a3b5965..63a9de1113 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1279,7 +1279,7 @@ struct ) | _ -> Queries.Result.top q end - | Q.MayPointToA e -> begin + | Q.MayPointTo e -> begin match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local e with | Address a -> a | Bot -> Queries.Result.bot q (* TODO: remove *) diff --git a/src/analyses/condVars.ml b/src/analyses/condVars.ml index 5f50d3968d..9fdc142f9a 100644 --- a/src/analyses/condVars.ml +++ b/src/analyses/condVars.ml @@ -64,7 +64,7 @@ struct let (>?) = Option.bind let mayPointTo ctx exp = - match ctx.ask (Queries.MayPointToA exp) with + match ctx.ask (Queries.MayPointTo exp) with | a when not (Queries.AD.is_top a) && Queries.AD.cardinal a > 0 -> let a' = if Queries.AD.mem UnknownPtr a then ( M.info ~category:Unsound "mayPointTo: query result for %a contains TOP!" d_exp exp; (* UNSOUND *) diff --git a/src/analyses/extractPthread.ml b/src/analyses/extractPthread.ml index 9c08269058..a4961b8dc9 100644 --- a/src/analyses/extractPthread.ml +++ b/src/analyses/extractPthread.ml @@ -877,7 +877,7 @@ module Spec : Analyses.MCPSpec = struct module ExprEval = struct let eval_ptr ctx exp = - let ad = ctx.ask (Queries.MayPointToA exp) in + let ad = ctx.ask (Queries.MayPointTo exp) in if (not (Queries.AD.is_top ad)) && Queries.AD.cardinal ad > 0 then if Queries.AD.mem UnknownPtr ad then (* UNSOUND *) diff --git a/src/analyses/fileUse.ml b/src/analyses/fileUse.ml index d4635722b9..a9088a4bb2 100644 --- a/src/analyses/fileUse.ml +++ b/src/analyses/fileUse.ml @@ -24,11 +24,11 @@ struct (* queries *) let query ctx (type a) (q: a Queries.t) = match q with - | Queries.MayPointToA exp -> if M.tracing then M.tracel "file" "query MayPointToA: %a" d_plainexp exp; Queries.Result.top q + | Queries.MayPointTo exp -> if M.tracing then M.tracel "file" "query MayPointTo: %a" d_plainexp exp; Queries.Result.top q | _ -> Queries.Result.top q let query_ad (ask: Queries.ask) exp = - match ask.f (Queries.MayPointToA exp) with + match ask.f (Queries.MayPointTo exp) with | ad when not (Queries.AD.is_top ad) -> Queries.AD.elements ad | _ -> [] let print_query_lv ?msg:(msg="") ask exp = @@ -36,7 +36,7 @@ struct let pretty_key = function | Queries.AD.Addr.Addr (v,o) -> Pretty.text (D.string_of_key (v, ValueDomain.Addr.Offs.to_exp o)) | _ -> Pretty.text "" in - if M.tracing then M.tracel "file" "%s MayPointToA %a = [%a]" msg d_exp exp (Pretty.docList ~sep:(Pretty.text ", ") pretty_key) addrs + if M.tracing then M.tracel "file" "%s MayPointTo %a = [%a]" msg d_exp exp (Pretty.docList ~sep:(Pretty.text ", ") pretty_key) addrs let eval_fv ask exp: varinfo option = match query_ad ask exp with diff --git a/src/analyses/mallocFresh.ml b/src/analyses/mallocFresh.ml index 3bea7b8c73..2c2b99a075 100644 --- a/src/analyses/mallocFresh.ml +++ b/src/analyses/mallocFresh.ml @@ -18,7 +18,7 @@ struct let exitstate _ = D.empty () let assign_lval (ask: Queries.ask) lval local = - match ask.f (MayPointToA (AddrOf lval)) with + match ask.f (MayPointTo (AddrOf lval)) with | ad when Queries.AD.is_top ad -> D.empty () | ad when Queries.AD.exists (function | Queries.AD.Addr.Addr (v,_) -> not (D.mem v local) && (v.vglob || ThreadEscape.has_escaped ask v) diff --git a/src/analyses/malloc_null.ml b/src/analyses/malloc_null.ml index feb1d97a51..e211fad98e 100644 --- a/src/analyses/malloc_null.ml +++ b/src/analyses/malloc_null.ml @@ -37,7 +37,7 @@ struct with SetDomain.Unsupported _ -> () end;*) match e with | Lval (Var v, offs) -> - begin match a.f (Queries.MayPointToA (mkAddrOf (Var v,offs))) with + begin match a.f (Queries.MayPointTo (mkAddrOf (Var v,offs))) with | ad when not (Queries.AD.is_top ad) -> Queries.AD.iter (function | Queries.AD.Addr.Addr mval -> warn_lval st mval @@ -118,7 +118,7 @@ struct else D.filter (fun x -> AD.mem x vars) st let get_concrete_lval (ask: Queries.ask) (lval:lval) = - match ask.f (Queries.MayPointToA (mkAddrOf lval)) with + match ask.f (Queries.MayPointTo (mkAddrOf lval)) with | ad when Queries.AD.cardinal ad = 1 && not (Queries.AD.mem UnknownPtr ad) -> Queries.AD.Addr.to_mval (Queries.AD.choose ad) | _ -> None @@ -130,7 +130,7 @@ struct | _ -> None let might_be_null (ask: Queries.ask) lv gl st = - match ask.f (Queries.MayPointToA (mkAddrOf lv)) with + match ask.f (Queries.MayPointTo (mkAddrOf lv)) with | ad when not (Queries.AD.is_top ad) -> let one_addr_might = function | Queries.AD.Addr.Addr mval -> diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index 660d9ba591..0bc2196aff 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -49,7 +49,7 @@ struct | _ -> state end | Free ptr -> - begin match ctx.ask (Queries.MayPointToA ptr) with + begin match ctx.ask (Queries.MayPointTo ptr) with | ad when not (Queries.AD.is_top ad) && Queries.AD.cardinal ad = 1 -> (* Note: Need to always set "ana.malloc.unique_address_count" to a value > 0 *) let unique_pointed_to_heap_vars = diff --git a/src/analyses/mutexEventsAnalysis.ml b/src/analyses/mutexEventsAnalysis.ml index 2b8ebffc61..162527b32b 100644 --- a/src/analyses/mutexEventsAnalysis.ml +++ b/src/analyses/mutexEventsAnalysis.ml @@ -18,7 +18,7 @@ struct include UnitAnalysis.Spec let name () = "mutexEvents" - let eval_exp_addr (a: Queries.ask) exp = a.f (Queries.MayPointToA exp) + let eval_exp_addr (a: Queries.ask) exp = a.f (Queries.MayPointTo exp) let lock ctx rw may_fail nonzero_return_when_aquired a lv_opt arg = match lv_opt with diff --git a/src/analyses/mutexTypeAnalysis.ml b/src/analyses/mutexTypeAnalysis.ml index 815b3fdb67..4d9de5f9c4 100644 --- a/src/analyses/mutexTypeAnalysis.ml +++ b/src/analyses/mutexTypeAnalysis.ml @@ -54,7 +54,7 @@ struct match desc.special arglist with | MutexInit {mutex = mutex; attr = attr} -> let attr = ctx.ask (Queries.EvalMutexAttr attr) in - let mutexes = ctx.ask (Queries.MayPointToA mutex) in + let mutexes = ctx.ask (Queries.MayPointTo mutex) in (* It is correct to iter over these sets here, as mutexes need to be intialized before being used, and an analysis that detects usage before initialization is a different analysis. *) Queries.AD.iter (function addr -> match addr with diff --git a/src/analyses/pthreadSignals.ml b/src/analyses/pthreadSignals.ml index f5d0e2c9d0..d515820514 100644 --- a/src/analyses/pthreadSignals.ml +++ b/src/analyses/pthreadSignals.ml @@ -17,7 +17,7 @@ struct module C = MustSignals module G = SetDomain.ToppedSet (MHP) (struct let topname = "All Threads" end) - let eval_exp_addr (a: Queries.ask) exp = Queries.AD.elements (a.f (Queries.MayPointToA exp)) + let eval_exp_addr (a: Queries.ask) exp = Queries.AD.elements (a.f (Queries.MayPointTo exp)) let possible_vinfos a cv_arg = List.filter_map ValueDomain.Addr.to_var_may (eval_exp_addr a cv_arg) diff --git a/src/analyses/spec.ml b/src/analyses/spec.ml index 45a6d99d38..54ffcd2697 100644 --- a/src/analyses/spec.ml +++ b/src/analyses/spec.ml @@ -204,7 +204,7 @@ struct | _ -> Queries.Result.top q let query_addrs ask exp = - match ask (Queries.MayPointToA exp) with + match ask (Queries.MayPointTo exp) with | ad when not (Queries.AD.is_top ad) -> Queries.AD.elements ad | _ -> [] diff --git a/src/analyses/taintPartialContexts.ml b/src/analyses/taintPartialContexts.ml index 453e09b3af..df0c87f34a 100644 --- a/src/analyses/taintPartialContexts.ml +++ b/src/analyses/taintPartialContexts.ml @@ -29,7 +29,7 @@ struct let d = ctx.local in (match lval with | (Var v, offs) -> D.add (v, Offset.Exp.of_cil offs) d - | (Mem e, _) -> D.union (to_mvals (ctx.ask (Queries.MayPointToA e))) d + | (Mem e, _) -> D.union (to_mvals (ctx.ask (Queries.MayPointTo e))) d ) (* this analysis is context insensitive*) @@ -94,7 +94,7 @@ struct else deep_addrs in - let d = List.fold_left (fun accD addr -> D.union accD (to_mvals (ctx.ask (Queries.MayPointToA addr)))) d shallow_addrs + let d = List.fold_left (fun accD addr -> D.union accD (to_mvals (ctx.ask (Queries.MayPointTo addr)))) d shallow_addrs in let d = List.fold_left (fun accD addr -> D.union accD (to_mvals (ctx.ask (Queries.ReachableFromA addr)))) d deep_addrs in diff --git a/src/analyses/threadEscape.ml b/src/analyses/threadEscape.ml index 4e7ec37c8a..738a2b66ae 100644 --- a/src/analyses/threadEscape.ml +++ b/src/analyses/threadEscape.ml @@ -40,7 +40,7 @@ struct D.empty () let mpt (ask: Queries.ask) e: D.t = - match ask.f (Queries.MayPointToA e) with + match ask.f (Queries.MayPointTo e) with | ad when not (AD.is_top ad) -> let to_extra addr set = match addr with diff --git a/src/analyses/uninit.ml b/src/analyses/uninit.ml index 20f5667f46..3ad3de6afa 100644 --- a/src/analyses/uninit.ml +++ b/src/analyses/uninit.ml @@ -31,7 +31,7 @@ struct (* TODO: Use AddressDomain for queries *) let access_address (ask: Queries.ask) write lv = - match ask.f (Queries.MayPointToA (AddrOf lv)) with + match ask.f (Queries.MayPointTo (AddrOf lv)) with | a when not (Queries.AD.is_top a) -> let to_extra addr xs = match addr with @@ -168,7 +168,7 @@ struct let init_vo (v: varinfo) (ofs: lval_offs) : D.t = List.fold_right remove_if_prefix (get_pfx v `NoOffset ofs v.vtype v.vtype) st in - match a.f (Queries.MayPointToA (AddrOf lv)) with + match a.f (Queries.MayPointTo (AddrOf lv)) with | a when Queries.AD.cardinal a = 1 -> begin match Queries.AD.Addr.to_mval (Queries.AD.choose a) with | Some (var, ofs) -> init_vo var ofs diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index f1fcdd8a7b..fc8b8aba1b 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -82,7 +82,7 @@ struct | Var _ -> Lval lval | Mem _ -> mkAddrOf lval (* Take the lval's address if its lhost is of the form *p, where p is a ptr *) in - match ctx.ask (Queries.MayPointToA lval_to_query) with + match ctx.ask (Queries.MayPointTo lval_to_query) with | ad when not (Queries.AD.is_top ad) -> let warn_for_heap_var v = if D.mem v state then @@ -187,7 +187,7 @@ struct let desc = LibraryFunctions.find f in match desc.special arglist with | Free ptr -> - begin match ctx.ask (Queries.MayPointToA ptr) with + begin match ctx.ask (Queries.MayPointTo ptr) with | ad when not (Queries.AD.is_top ad) -> let pointed_to_heap_vars = Queries.AD.fold (fun addr state -> diff --git a/src/analyses/varEq.ml b/src/analyses/varEq.ml index 628faa2ac1..038ad345d9 100644 --- a/src/analyses/varEq.ml +++ b/src/analyses/varEq.ml @@ -137,7 +137,7 @@ struct (* TODO: why unused? how different from below? *) let may_change_pt ask (b:exp) (a:exp) : bool = - let pt e = ask (Queries.MayPointToA e) in + let pt e = ask (Queries.MayPointTo e) in let rec lval_may_change_pt a bl : bool = let rec may_change_pt_offset o = match o with @@ -175,7 +175,7 @@ struct let may_change (ask: Queries.ask) (b:exp) (a:exp) : bool = (*b should be an address of something that changes*) - let pt e = ask.f (Queries.MayPointToA e) in + let pt e = ask.f (Queries.MayPointTo e) in let bad = pt b in let bt = match unrollTypeDeep (Cilfacade.typeOf b) with @@ -344,7 +344,7 @@ struct | Lval (Var v,_) -> Some (v.vglob || (ask.f (Queries.IsMultiple v) || BaseUtil.is_global ask v)) | Lval (Mem e, _) -> - begin match ask.f (Queries.MayPointToA e) with + begin match ask.f (Queries.MayPointTo e) with | ad when not (Queries.AD.is_top ad) -> Some (Queries.AD.exists (function | Addr (v,_) -> is_global_var ask (Lval (var v)) = Some true @@ -389,7 +389,7 @@ struct (* Give the set of reachables from argument. *) let reachables ~deep (ask: Queries.ask) es = let reachable e st = - let q = if deep then Queries.ReachableFromA e else Queries.MayPointToA e in + let q = if deep then Queries.ReachableFromA e else Queries.MayPointTo e in let ad = ask.f q in Queries.AD.join ad st in diff --git a/src/cdomains/mvalMapDomain.ml b/src/cdomains/mvalMapDomain.ml index 28b49de1a5..d0d2f8da85 100644 --- a/src/cdomains/mvalMapDomain.ml +++ b/src/cdomains/mvalMapDomain.ml @@ -281,7 +281,7 @@ struct let keys_from_lval lval (ask: Queries.ask) = (* use MayPointTo query to get all possible pointees of &lval *) (* print_query_lv ctx.ask (AddrOf lval); *) - let query_addrs (ask: Queries.ask) exp = match ask.f (Queries.MayPointToA exp) with + let query_addrs (ask: Queries.ask) exp = match ask.f (Queries.MayPointTo exp) with | ad when not (Queries.AD.is_top ad) -> Queries.AD.elements ad | _ -> [] in diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 6f5838c19b..3586e304df 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -72,7 +72,7 @@ type invariant_context = Invariant.context = { (** GADT for queries with specific result type. *) type _ t = | EqualSet: exp -> ES.t t - | MayPointToA: exp -> AD.t t + | MayPointTo: exp -> AD.t t | ReachableFromA: exp -> AD.t t | ReachableUkTypes: exp -> TS.t t | Regions: exp -> LS.t t @@ -141,7 +141,7 @@ struct (* Cannot group these GADTs... *) | EqualSet _ -> (module ES) | CondVars _ -> (module ES) - | MayPointToA _ -> (module AD) + | MayPointTo _ -> (module AD) | ReachableFromA _ -> (module AD) | Regions _ -> (module LS) | MustLockset -> (module LS) @@ -205,7 +205,7 @@ struct (* Cannot group these GADTs... *) | EqualSet _ -> ES.top () | CondVars _ -> ES.top () - | MayPointToA _ -> AD.top () + | MayPointTo _ -> AD.top () | ReachableFromA _ -> AD.top () | Regions _ -> LS.top () | MustLockset -> LS.top () @@ -265,7 +265,7 @@ struct (* deriving ord doesn't work for GADTs (t and any_query) so this must be done manually... *) let order = function | Any (EqualSet _) -> 0 - | Any (MayPointToA _) -> 1 + | Any (MayPointTo _) -> 1 | Any (ReachableFromA _) -> 2 | Any (ReachableUkTypes _) -> 3 | Any (Regions _) -> 4 @@ -321,7 +321,7 @@ struct else match a, b with | Any (EqualSet e1), Any (EqualSet e2) -> CilType.Exp.compare e1 e2 - | Any (MayPointToA e1), Any (MayPointToA e2) -> CilType.Exp.compare e1 e2 + | Any (MayPointTo e1), Any (MayPointTo e2) -> CilType.Exp.compare e1 e2 | Any (ReachableFromA e1), Any (ReachableFromA e2) -> CilType.Exp.compare e1 e2 | Any (ReachableUkTypes e1), Any (ReachableUkTypes e2) -> CilType.Exp.compare e1 e2 | Any (Regions e1), Any (Regions e2) -> CilType.Exp.compare e1 e2 @@ -366,7 +366,7 @@ struct let rec hash_arg = function | Any (EqualSet e) -> CilType.Exp.hash e - | Any (MayPointToA e) -> CilType.Exp.hash e + | Any (MayPointTo e) -> CilType.Exp.hash e | Any (ReachableFromA e) -> CilType.Exp.hash e | Any (ReachableUkTypes e) -> CilType.Exp.hash e | Any (Regions e) -> CilType.Exp.hash e @@ -408,7 +408,7 @@ struct let rec pretty () = function | Any (EqualSet e) -> Pretty.dprintf "EqualSet %a" CilType.Exp.pretty e - | Any (MayPointToA e) -> Pretty.dprintf "MayPointToA %a" CilType.Exp.pretty e + | Any (MayPointTo e) -> Pretty.dprintf "MayPointTo %a" CilType.Exp.pretty e | Any (ReachableFromA e) -> Pretty.dprintf "ReachableFromA %a" CilType.Exp.pretty e | Any (ReachableUkTypes e) -> Pretty.dprintf "ReachableUkTypes %a" CilType.Exp.pretty e | Any (Regions e) -> Pretty.dprintf "Regions %a" CilType.Exp.pretty e @@ -460,7 +460,7 @@ end let to_value_domain_ask (ask: ask) = let eval_int e = ask.f (EvalInt e) in - let may_point_to e = ask.f (MayPointToA e) in + let may_point_to e = ask.f (MayPointTo e) in let is_multiple v = ask.f (IsMultiple v) in { VDQ.eval_int; may_point_to; is_multiple } From 43223d09d59f5ae89a1f2f0fde8b009f3e29b9f4 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Fri, 25 Aug 2023 19:09:28 +0300 Subject: [PATCH 0509/1312] Rename ReachableFromA -> ReachableFrom --- src/analyses/accessAnalysis.ml | 2 +- src/analyses/apron/relationAnalysis.apron.ml | 4 ++-- src/analyses/base.ml | 2 +- src/analyses/extractPthread.ml | 2 +- src/analyses/malloc_null.ml | 2 +- src/analyses/poisonVariables.ml | 2 +- src/analyses/taintPartialContexts.ml | 2 +- src/analyses/threadEscape.ml | 2 +- src/analyses/uninit.ml | 2 +- src/analyses/useAfterFree.ml | 2 +- src/analyses/varEq.ml | 2 +- src/domains/queries.ml | 14 +++++++------- 12 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/analyses/accessAnalysis.ml b/src/analyses/accessAnalysis.ml index b13b7bf3ea..e99aefa0e5 100644 --- a/src/analyses/accessAnalysis.ml +++ b/src/analyses/accessAnalysis.ml @@ -33,7 +33,7 @@ struct let do_access (ctx: (D.t, G.t, C.t, V.t) ctx) (kind:AccessKind.t) (reach:bool) (e:exp) = if M.tracing then M.trace "access" "do_access %a %a %B\n" d_exp e AccessKind.pretty kind reach; - let reach_or_mpt: _ Queries.t = if reach then ReachableFromA e else MayPointTo e in + let reach_or_mpt: _ Queries.t = if reach then ReachableFrom e else MayPointTo e in let ad = ctx.ask reach_or_mpt in ctx.emit (Access {exp=e; ad; kind; reach}) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index ef035d3cca..31d697c881 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -275,7 +275,7 @@ struct let reachable_from_args ctx args = let to_vs e = - ctx.ask (ReachableFromA e) + ctx.ask (ReachableFrom e) |> LockDomain.MayLocksetNoRW.to_var_may |> VS.of_list in @@ -453,7 +453,7 @@ struct match st with | None -> None | Some st -> - let ad = ask.f (Queries.ReachableFromA e) in + let ad = ask.f (Queries.ReachableFrom e) in if Queries.AD.is_top ad then None else diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 63a9de1113..e9a0580098 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1293,7 +1293,7 @@ struct | Bot -> Queries.Result.bot q (* TODO: remove *) | _ -> Queries.Result.top q end - | Q.ReachableFromA e -> begin + | Q.ReachableFrom e -> begin match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local e with | Top -> Queries.Result.top q | Bot -> Queries.Result.bot q (* TODO: remove *) diff --git a/src/analyses/extractPthread.ml b/src/analyses/extractPthread.ml index a4961b8dc9..ce8471c662 100644 --- a/src/analyses/extractPthread.ml +++ b/src/analyses/extractPthread.ml @@ -1117,7 +1117,7 @@ module Spec : Analyses.MCPSpec = struct match (LibraryFunctions.find f).special arglist', f.vname, arglist with | ThreadCreate { thread; start_routine = func; _ }, _, _ -> let funs_ad = - let ad = ctx.ask (Queries.ReachableFromA func) in + let ad = ctx.ask (Queries.ReachableFrom func) in Queries.AD.filter (function | Queries.AD.Addr.Addr mval -> diff --git a/src/analyses/malloc_null.ml b/src/analyses/malloc_null.ml index e211fad98e..dca158c973 100644 --- a/src/analyses/malloc_null.ml +++ b/src/analyses/malloc_null.ml @@ -95,7 +95,7 @@ struct let remove_unreachable (ask: Queries.ask) (args: exp list) (st: D.t) : D.t = let reachable = let do_exp e = - match ask.f (Queries.ReachableFromA e) with + match ask.f (Queries.ReachableFrom e) with | ad when not (Queries.AD.is_top ad) -> let to_extra addr ads = match addr with diff --git a/src/analyses/poisonVariables.ml b/src/analyses/poisonVariables.ml index 5f2905ffb1..f69e5b2fbc 100644 --- a/src/analyses/poisonVariables.ml +++ b/src/analyses/poisonVariables.ml @@ -42,7 +42,7 @@ struct if VS.is_empty ctx.local then [ctx.local,ctx.local] else ( - let reachable_from_args = List.fold (fun ad e -> Queries.AD.join ad (ctx.ask (ReachableFromA e))) (Queries.AD.empty ()) args in + let reachable_from_args = List.fold (fun ad e -> Queries.AD.join ad (ctx.ask (ReachableFrom e))) (Queries.AD.empty ()) args in if Queries.AD.is_top reachable_from_args || VS.is_top ctx.local then [ctx.local, ctx.local] else diff --git a/src/analyses/taintPartialContexts.ml b/src/analyses/taintPartialContexts.ml index df0c87f34a..d4ea17d2e0 100644 --- a/src/analyses/taintPartialContexts.ml +++ b/src/analyses/taintPartialContexts.ml @@ -96,7 +96,7 @@ struct in let d = List.fold_left (fun accD addr -> D.union accD (to_mvals (ctx.ask (Queries.MayPointTo addr)))) d shallow_addrs in - let d = List.fold_left (fun accD addr -> D.union accD (to_mvals (ctx.ask (Queries.ReachableFromA addr)))) d deep_addrs + let d = List.fold_left (fun accD addr -> D.union accD (to_mvals (ctx.ask (Queries.ReachableFrom addr)))) d deep_addrs in d diff --git a/src/analyses/threadEscape.ml b/src/analyses/threadEscape.ml index 738a2b66ae..9ed62e7422 100644 --- a/src/analyses/threadEscape.ml +++ b/src/analyses/threadEscape.ml @@ -26,7 +26,7 @@ struct module G = ThreadIdSet let reachable (ask: Queries.ask) e: D.t = - match ask.f (Queries.ReachableFromA e) with + match ask.f (Queries.ReachableFrom e) with | ad when not (Queries.AD.is_top ad) -> let to_extra addr set = match addr with diff --git a/src/analyses/uninit.ml b/src/analyses/uninit.ml index 3ad3de6afa..58fd5fc008 100644 --- a/src/analyses/uninit.ml +++ b/src/analyses/uninit.ml @@ -195,7 +195,7 @@ struct let remove_unreachable (ask: Queries.ask) (args: exp list) (st: D.t) : D.t = let reachable = let do_exp e = - match ask.f (Queries.ReachableFromA e) with + match ask.f (Queries.ReachableFrom e) with | ad when not (Queries.AD.is_top ad) -> let to_extra ad ads = match ad with diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index fc8b8aba1b..f828e17e2b 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -156,7 +156,7 @@ struct if D.is_empty caller_state then [caller_state, caller_state] else ( - let reachable_from_args = List.fold_left (fun ad arg -> Queries.AD.join ad (ctx.ask (ReachableFromA arg))) (Queries.AD.empty ()) args in + let reachable_from_args = List.fold_left (fun ad arg -> Queries.AD.join ad (ctx.ask (ReachableFrom arg))) (Queries.AD.empty ()) args in if Queries.AD.is_top reachable_from_args || D.is_top caller_state then [caller_state, caller_state] else diff --git a/src/analyses/varEq.ml b/src/analyses/varEq.ml index 038ad345d9..77823d99d7 100644 --- a/src/analyses/varEq.ml +++ b/src/analyses/varEq.ml @@ -389,7 +389,7 @@ struct (* Give the set of reachables from argument. *) let reachables ~deep (ask: Queries.ask) es = let reachable e st = - let q = if deep then Queries.ReachableFromA e else Queries.MayPointTo e in + let q = if deep then Queries.ReachableFrom e else Queries.MayPointTo e in let ad = ask.f q in Queries.AD.join ad st in diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 3586e304df..0388ce2995 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -73,7 +73,7 @@ type invariant_context = Invariant.context = { type _ t = | EqualSet: exp -> ES.t t | MayPointTo: exp -> AD.t t - | ReachableFromA: exp -> AD.t t + | ReachableFrom: exp -> AD.t t | ReachableUkTypes: exp -> TS.t t | Regions: exp -> LS.t t | MayEscape: varinfo -> MayBool.t t @@ -142,7 +142,7 @@ struct | EqualSet _ -> (module ES) | CondVars _ -> (module ES) | MayPointTo _ -> (module AD) - | ReachableFromA _ -> (module AD) + | ReachableFrom _ -> (module AD) | Regions _ -> (module LS) | MustLockset -> (module LS) | EvalFunvar _ -> (module LS) @@ -206,7 +206,7 @@ struct | EqualSet _ -> ES.top () | CondVars _ -> ES.top () | MayPointTo _ -> AD.top () - | ReachableFromA _ -> AD.top () + | ReachableFrom _ -> AD.top () | Regions _ -> LS.top () | MustLockset -> LS.top () | EvalFunvar _ -> LS.top () @@ -266,7 +266,7 @@ struct let order = function | Any (EqualSet _) -> 0 | Any (MayPointTo _) -> 1 - | Any (ReachableFromA _) -> 2 + | Any (ReachableFrom _) -> 2 | Any (ReachableUkTypes _) -> 3 | Any (Regions _) -> 4 | Any (MayEscape _) -> 5 @@ -322,7 +322,7 @@ struct match a, b with | Any (EqualSet e1), Any (EqualSet e2) -> CilType.Exp.compare e1 e2 | Any (MayPointTo e1), Any (MayPointTo e2) -> CilType.Exp.compare e1 e2 - | Any (ReachableFromA e1), Any (ReachableFromA e2) -> CilType.Exp.compare e1 e2 + | Any (ReachableFrom e1), Any (ReachableFrom e2) -> CilType.Exp.compare e1 e2 | Any (ReachableUkTypes e1), Any (ReachableUkTypes e2) -> CilType.Exp.compare e1 e2 | Any (Regions e1), Any (Regions e2) -> CilType.Exp.compare e1 e2 | Any (MayEscape vi1), Any (MayEscape vi2) -> CilType.Varinfo.compare vi1 vi2 @@ -367,7 +367,7 @@ struct let rec hash_arg = function | Any (EqualSet e) -> CilType.Exp.hash e | Any (MayPointTo e) -> CilType.Exp.hash e - | Any (ReachableFromA e) -> CilType.Exp.hash e + | Any (ReachableFrom e) -> CilType.Exp.hash e | Any (ReachableUkTypes e) -> CilType.Exp.hash e | Any (Regions e) -> CilType.Exp.hash e | Any (MayEscape vi) -> CilType.Varinfo.hash vi @@ -409,7 +409,7 @@ struct let rec pretty () = function | Any (EqualSet e) -> Pretty.dprintf "EqualSet %a" CilType.Exp.pretty e | Any (MayPointTo e) -> Pretty.dprintf "MayPointTo %a" CilType.Exp.pretty e - | Any (ReachableFromA e) -> Pretty.dprintf "ReachableFromA %a" CilType.Exp.pretty e + | Any (ReachableFrom e) -> Pretty.dprintf "ReachableFrom %a" CilType.Exp.pretty e | Any (ReachableUkTypes e) -> Pretty.dprintf "ReachableUkTypes %a" CilType.Exp.pretty e | Any (Regions e) -> Pretty.dprintf "Regions %a" CilType.Exp.pretty e | Any (MayEscape vi) -> Pretty.dprintf "MayEscape %a" CilType.Varinfo.pretty vi From 866f5172aa486db87986815dd009bef6f5185e70 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Fri, 25 Aug 2023 19:35:42 +0300 Subject: [PATCH 0510/1312] Rename a -> ad, where missed --- src/analyses/apron/relationAnalysis.apron.ml | 4 ++-- src/analyses/condVars.ml | 8 ++++---- src/analyses/uninit.ml | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index 31d697c881..ca60e5dc30 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -217,8 +217,8 @@ struct | Lval (Var v, off) -> Lval (Var v, off) | Lval (Mem e, NoOffset) -> begin match ask (Queries.MayPointTo e) with - | a when not (Queries.AD.is_top a) && (Queries.AD.cardinal a) = 1 -> - begin match Queries.AD.Addr.to_mval (Queries.AD.choose a) with + | ad when not (Queries.AD.is_top ad) && (Queries.AD.cardinal ad) = 1 -> + begin match Queries.AD.Addr.to_mval (Queries.AD.choose ad) with | Some mval -> ValueDomain.Addr.Mval.to_cil_exp mval | None -> Lval (Mem e, NoOffset) end diff --git a/src/analyses/condVars.ml b/src/analyses/condVars.ml index 9fdc142f9a..55627941ba 100644 --- a/src/analyses/condVars.ml +++ b/src/analyses/condVars.ml @@ -65,11 +65,11 @@ struct let mayPointTo ctx exp = match ctx.ask (Queries.MayPointTo exp) with - | a when not (Queries.AD.is_top a) && Queries.AD.cardinal a > 0 -> - let a' = if Queries.AD.mem UnknownPtr a then ( + | ad when not (Queries.AD.is_top ad) && Queries.AD.cardinal ad > 0 -> + let a' = if Queries.AD.mem UnknownPtr ad then ( M.info ~category:Unsound "mayPointTo: query result for %a contains TOP!" d_exp exp; (* UNSOUND *) - Queries.AD.remove UnknownPtr a - ) else a + Queries.AD.remove UnknownPtr ad + ) else ad in List.filter_map (function | ValueDomain.Addr.Addr (v,o) -> Some (v, ValueDomain.Addr.Offs.to_exp o) diff --git a/src/analyses/uninit.ml b/src/analyses/uninit.ml index 58fd5fc008..c14b1034a0 100644 --- a/src/analyses/uninit.ml +++ b/src/analyses/uninit.ml @@ -32,13 +32,13 @@ struct (* TODO: Use AddressDomain for queries *) let access_address (ask: Queries.ask) write lv = match ask.f (Queries.MayPointTo (AddrOf lv)) with - | a when not (Queries.AD.is_top a) -> + | ad when not (Queries.AD.is_top ad) -> let to_extra addr xs = match addr with | Queries.AD.Addr.Addr (v,o) -> (v, o, write) :: xs | _ -> xs in - Queries.AD.fold to_extra a [] + Queries.AD.fold to_extra ad [] | _ -> M.info ~category:Unsound "Access to unknown address could be global"; [] @@ -169,8 +169,8 @@ struct List.fold_right remove_if_prefix (get_pfx v `NoOffset ofs v.vtype v.vtype) st in match a.f (Queries.MayPointTo (AddrOf lv)) with - | a when Queries.AD.cardinal a = 1 -> - begin match Queries.AD.Addr.to_mval (Queries.AD.choose a) with + | ad when Queries.AD.cardinal ad = 1 -> + begin match Queries.AD.Addr.to_mval (Queries.AD.choose ad) with | Some (var, ofs) -> init_vo var ofs | None -> st end From 57549d0ee291fd88a54b3315bc3f5078e2426e72 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Fri, 25 Aug 2023 19:37:55 +0300 Subject: [PATCH 0511/1312] Simplify cardinal > 0 to not is_empty --- src/analyses/condVars.ml | 2 +- src/analyses/extractPthread.ml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/analyses/condVars.ml b/src/analyses/condVars.ml index 55627941ba..04246bf28a 100644 --- a/src/analyses/condVars.ml +++ b/src/analyses/condVars.ml @@ -65,7 +65,7 @@ struct let mayPointTo ctx exp = match ctx.ask (Queries.MayPointTo exp) with - | ad when not (Queries.AD.is_top ad) && Queries.AD.cardinal ad > 0 -> + | ad when not (Queries.AD.is_top ad) && not (Queries.AD.is_empty ad) -> let a' = if Queries.AD.mem UnknownPtr ad then ( M.info ~category:Unsound "mayPointTo: query result for %a contains TOP!" d_exp exp; (* UNSOUND *) Queries.AD.remove UnknownPtr ad diff --git a/src/analyses/extractPthread.ml b/src/analyses/extractPthread.ml index ce8471c662..211b88fd6e 100644 --- a/src/analyses/extractPthread.ml +++ b/src/analyses/extractPthread.ml @@ -878,7 +878,7 @@ module Spec : Analyses.MCPSpec = struct module ExprEval = struct let eval_ptr ctx exp = let ad = ctx.ask (Queries.MayPointTo exp) in - if (not (Queries.AD.is_top ad)) && Queries.AD.cardinal ad > 0 then + if (not (Queries.AD.is_top ad)) && not (Queries.AD.is_empty ad) then if Queries.AD.mem UnknownPtr ad then (* UNSOUND *) Queries.AD.remove UnknownPtr ad From 291d60cd20d2a5ed29c3d76cd1e4cc05b07a562f Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 25 Aug 2023 18:40:20 +0200 Subject: [PATCH 0512/1312] Check for OOB mem access on the address level and only warn on deref --- src/analyses/memOutOfBounds.ml | 195 ++++++++++++++++++++++++++++----- 1 file changed, 166 insertions(+), 29 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 9a03968ca3..4b6c6db43f 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -19,6 +19,36 @@ struct (* HELPER FUNCTIONS *) + let intdom_of_int x = + IntDomain.IntDomTuple.of_int (Cilfacade.ptrdiff_ikind ()) (Z.of_int x) + + let to_index ?typ offs = + let idx_of_int x = + IntDomain.IntDomTuple.of_int (Cilfacade.ptrdiff_ikind ()) (Z.of_int (x / 8)) + in + let rec offset_to_index_offset ?typ offs = match offs with + | `NoOffset -> idx_of_int 0 + | `Field (field, o) -> + let field_as_offset = Field (field, NoOffset) in + let bits_offset, _size = GoblintCil.bitsOffset (TComp (field.fcomp, [])) field_as_offset in + let bits_offset = idx_of_int bits_offset in + let remaining_offset = offset_to_index_offset ~typ:field.ftype o in + IntDomain.IntDomTuple.add bits_offset remaining_offset + | `Index (x, o) -> + let (item_typ, item_size_in_bits) = + match Option.map unrollType typ with + | Some TArray(item_typ, _, _) -> + let item_size_in_bits = bitsSizeOf item_typ in + (Some item_typ, idx_of_int item_size_in_bits) + | _ -> + (None, IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind ()) + in + let bits_offset = IntDomain.IntDomTuple.mul item_size_in_bits x in + let remaining_offset = offset_to_index_offset ?typ:item_typ o in + IntDomain.IntDomTuple.add bits_offset remaining_offset + in + offset_to_index_offset ?typ offs + let rec exp_contains_a_ptr (exp:exp) = match exp with | Const _ @@ -61,6 +91,14 @@ struct let get_size_of_ptr_target ctx ptr = (* Call Queries.BlobSize only if ptr points solely to the heap. Otherwise we get bot *) + (* TODO: + * If the ptr's address has been offset, then Queries.BlobSize will answer with bot. For example: + char *ptr = malloc(10 * sizeof(char)); + ptr++; + printf("%s", *ptr); // => Issues a WARN even though it shouldn't + * However, in this case we're too imprecise, since we're always comparing something with bot + * and thus we always get a warning for an OOB memory access. Should we maybe change Queries.BlobSize again? + *) if points_to_heap_only ctx ptr then ctx.ask (Queries.BlobSize ptr) else @@ -68,16 +106,19 @@ struct | a when not (Queries.LS.is_top a) -> let pts_list = Queries.LS.elements a in let pts_elems_to_sizes (v, _) = - if isArrayType v.vtype then - ctx.ask (Queries.EvalLength ptr) - else - let var_type_size = match Cil.getInteger (sizeOf v.vtype) with - | Some z -> - let ikindOfTypeSize = intKindForValue z true in - VDQ.ID.of_int ikindOfTypeSize z - | None -> VDQ.ID.bot () - in - var_type_size + begin match v.vtype with + | TArray (item_typ, _, _) -> + let item_typ_size_in_bytes = (bitsSizeOf item_typ) / 8 in + let item_typ_size_in_bytes = intdom_of_int item_typ_size_in_bytes in + begin match ctx.ask (Queries.EvalLength ptr) with + | `Lifted arr_len -> `Lifted (IntDomain.IntDomTuple.mul item_typ_size_in_bytes arr_len) + | `Bot -> VDQ.ID.bot () + | `Top -> VDQ.ID.top () + end + | _ -> + let type_size_in_bytes = (bitsSizeOf v.vtype) / 8 in + `Lifted (intdom_of_int type_size_in_bytes) + end in (* Map each points-to-set element to its size *) let pts_sizes = List.map pts_elems_to_sizes pts_list in @@ -93,26 +134,109 @@ struct M.warn "Pointer %a has a points-to-set of top. An invalid memory access might occur" d_exp ptr; VDQ.ID.top () - let eval_ptr_offset_in_binop ctx exp = - ctx.ask (Queries.EvalInt exp) + let get_ptr_deref_type ptr_typ = + match ptr_typ with + | TPtr (t, _) -> Some t + | _ -> None - let rec check_lval_for_oob_access ctx lval = + let size_of_type_in_bytes typ = + let typ_size_in_bytes = (bitsSizeOf typ) / 8 in + intdom_of_int typ_size_in_bytes + + let eval_ptr_offset_in_binop ctx exp ptr_contents_typ = + let eval_offset = ctx.ask (Queries.EvalInt exp) in + let eval_offset = Option.get @@ VDQ.ID.to_int eval_offset in + let eval_offset = VDQ.ID.of_int (Cilfacade.ptrdiff_ikind ()) eval_offset in + let ptr_contents_typ_size_in_bytes = size_of_type_in_bytes ptr_contents_typ in + match eval_offset with + | `Lifted i -> `Lifted (IntDomain.IntDomTuple.mul i ptr_contents_typ_size_in_bytes) + | `Top -> `Top + | `Bot -> `Bot + + let rec offs_to_idx typ offs = + match offs with + | `NoOffset -> intdom_of_int 0 + | `Field (field, o) -> + let field_as_offset = Field (field, NoOffset) in + let bits_offset, _size = GoblintCil.bitsOffset (TComp (field.fcomp, [])) field_as_offset in + let bytes_offset = intdom_of_int (bits_offset / 8) in + let remaining_offset = offs_to_idx field.ftype o in + IntDomain.IntDomTuple.add bytes_offset remaining_offset + | `Index (x, o) -> + let typ_size_in_bytes = size_of_type_in_bytes typ in + let bytes_offset = IntDomain.IntDomTuple.mul typ_size_in_bytes x in + let remaining_offset = offs_to_idx typ o in + IntDomain.IntDomTuple.add bytes_offset remaining_offset + + let rec get_addr_offs ctx ptr = + match ctx.ask (Queries.MayPointTo ptr) with + | a when not (VDQ.LS.is_top a) -> + let ptr_deref_type = get_ptr_deref_type @@ typeOf ptr in + begin match ptr_deref_type with + | Some t -> + begin match VDQ.LS.is_empty a with + | true -> + M.warn "Pointer %a has an empty points-to-set" d_exp ptr; + IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind () + | false -> + (* + Offset should be the same for all elements in the points-to set. + Hence, we can just pick one element and obtain its offset + TODO: Does this make sense? + *) + let (_, o) = VDQ.LS.choose a in + let rec to_int_dom_offs = function + | `NoOffset -> `NoOffset + | `Field (f, o) -> `Field (f, to_int_dom_offs o) + | `Index (i, o) -> `Index (VDQ.ID.unlift (fun x -> x) @@ ctx.ask (Queries.EvalInt i), to_int_dom_offs o) + in + offs_to_idx t (to_int_dom_offs o) + end + | None -> + M.error "Expression %a doesn't have pointer type" d_exp ptr; + IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind () + end + | _ -> + M.warn "Pointer %a has a points-to-set of top. An invalid memory access might occur" d_exp ptr; + IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind () + + and check_lval_for_oob_access ctx lval = if not @@ lval_contains_a_ptr lval then () else match lval with | Var _, _ -> () | Mem e, _ -> begin match e with - | Lval lval -> check_lval_for_oob_access ctx lval - | BinOp (PlusPI as binop, e1, e2, t) - | BinOp (MinusPI as binop, e1, e2, t) - | BinOp (IndexPI as binop, e1, e2, t) -> + | Lval (Var v, _) as lval_exp -> check_no_binop_deref ctx lval_exp + | BinOp (binop, e1, e2, t) when binop = PlusPI || binop = MinusPI || binop = IndexPI -> check_binop_exp ctx binop e1 e2 t; check_exp_for_oob_access ctx e1; check_exp_for_oob_access ctx e2 | _ -> check_exp_for_oob_access ctx e end + and check_no_binop_deref ctx lval_exp = + let behavior = Undefined MemoryOutOfBoundsAccess in + let cwe_number = 823 in + let ptr_size = get_size_of_ptr_target ctx lval_exp in + let addr_offs = get_addr_offs ctx lval_exp in + let ptr_type = typeOf lval_exp in + let ptr_contents_type = get_ptr_deref_type ptr_type in + match ptr_contents_type with + | Some t -> + begin match VDQ.ID.is_top ptr_size with + | true -> + AS.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer %a not known. Memory out-of-bounds access might occur due to pointer arithmetic" d_exp lval_exp + | false -> + let offs = `Lifted addr_offs in + if ptr_size < offs then begin + AS.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer is %a (in bytes). It is offset by %a (in bytes) due to pointer arithmetic. Memory out-of-bounds access must occur" VDQ.ID.pretty ptr_size VDQ.ID.pretty offs + end + end + | _ -> M.error "Expression %a is not a pointer" d_exp lval_exp + and check_exp_for_oob_access ctx exp = match exp with | Const _ @@ -146,19 +270,32 @@ struct | IndexPI | MinusPI -> let ptr_size = get_size_of_ptr_target ctx e1 in - let offset_size = eval_ptr_offset_in_binop ctx e2 in - begin match VDQ.ID.is_top ptr_size, VDQ.ID.is_top offset_size with - | true, _ -> - AS.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer %a in expression %a not known. Memory out-of-bounds access might occur" d_exp e1 d_exp binopexp - | _, true -> - AS.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Operand value for pointer arithmetic in expression %a not known. Memory out-of-bounds access might occur" d_exp binopexp - | false, false -> - if ptr_size < offset_size then begin - AS.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer %a in expression %a is smaller than offset %a for pointer arithmetic. Memory out-of-bounds access must occur" VDQ.ID.pretty ptr_size d_exp binopexp VDQ.ID.pretty offset_size + let addr_offs = get_addr_offs ctx e1 in + let ptr_type = typeOf e1 in + let ptr_contents_type = get_ptr_deref_type ptr_type in + begin match ptr_contents_type with + | Some t -> + let offset_size = eval_ptr_offset_in_binop ctx e2 t in + (* Make sure to add the address offset to the binop offset *) + let offset_size_with_addr_size = match offset_size with + | `Lifted os -> `Lifted (IntDomain.IntDomTuple.add os addr_offs) + | `Top -> `Top + | `Bot -> `Bot + in + begin match VDQ.ID.is_top ptr_size, VDQ.ID.is_top offset_size_with_addr_size with + | true, _ -> + AS.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer %a in expression %a not known. Memory out-of-bounds access might occur" d_exp e1 d_exp binopexp + | _, true -> + AS.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Operand value for pointer arithmetic in expression %a not known. Memory out-of-bounds access might occur" d_exp binopexp + | false, false -> + if ptr_size < offset_size_with_addr_size then begin + AS.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer in expression %a is %a (in bytes). It is offset by %a (in bytes). Memory out-of-bounds access must occur" d_exp binopexp VDQ.ID.pretty ptr_size VDQ.ID.pretty offset_size_with_addr_size + end end + | _ -> M.error "Binary expression %a doesn't have a pointer" d_exp binopexp end | _ -> () From 2a3aaaec12b59236608b7b2e4c81e2fe27f3de36 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 25 Aug 2023 18:40:53 +0200 Subject: [PATCH 0513/1312] Adjust the OOB mem access test case with a loop --- tests/regression/77-mem-oob/03-oob-loop.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/regression/77-mem-oob/03-oob-loop.c b/tests/regression/77-mem-oob/03-oob-loop.c index 502a4cc0e2..4f637d487e 100644 --- a/tests/regression/77-mem-oob/03-oob-loop.c +++ b/tests/regression/77-mem-oob/03-oob-loop.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval +// PARAM: --set ana.activated[+] memOutOfBounds --set exp.unrolling-factor 10 --enable ana.int.interval #include #include @@ -9,10 +9,8 @@ int main(int argc, char const *argv[]) { ptr++; } - //TODO: We cannot currently detect OOB memory accesses happening due to loops like the one above - // => Both lines below can't have WARNs for now - printf("%s", *ptr); //NOWARN - free(ptr); //NOWARN + printf("%s", *ptr); //WARN + free(ptr); //WARN return 0; } From 46bd81f482cc6407eeddd32297676fbc9b21bb79 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 25 Aug 2023 18:46:05 +0200 Subject: [PATCH 0514/1312] Add test case with pointer arithmetic and subsequent derefs --- .../77-mem-oob/04-oob-deref-after-ptr-arith.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 tests/regression/77-mem-oob/04-oob-deref-after-ptr-arith.c diff --git a/tests/regression/77-mem-oob/04-oob-deref-after-ptr-arith.c b/tests/regression/77-mem-oob/04-oob-deref-after-ptr-arith.c new file mode 100644 index 0000000000..5046a00664 --- /dev/null +++ b/tests/regression/77-mem-oob/04-oob-deref-after-ptr-arith.c @@ -0,0 +1,18 @@ +// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval +#include +#include + +int main(int argc, char const *argv[]) { + char *ptr = malloc(5 * sizeof(char)); + + ptr++;//NOWARN + printf("%s", *ptr);//NOWARN + ptr = ptr + 5;//NOWARN + printf("%s", *ptr);//WARN + *(ptr + 1) = 'b';//WARN + *(ptr + 10) = 'c';//WARN + + free(ptr); + + return 0; +} From 37fb4e8173a3e1d1b3b6b404219bc9b681e01721 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 25 Aug 2023 18:51:05 +0200 Subject: [PATCH 0515/1312] Refactor to_int_dom_offs --- src/analyses/memOutOfBounds.ml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 4b6c6db43f..329539ebfb 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -188,7 +188,12 @@ struct let rec to_int_dom_offs = function | `NoOffset -> `NoOffset | `Field (f, o) -> `Field (f, to_int_dom_offs o) - | `Index (i, o) -> `Index (VDQ.ID.unlift (fun x -> x) @@ ctx.ask (Queries.EvalInt i), to_int_dom_offs o) + | `Index (i, o) -> + let exp_as_int_dom = match ctx.ask (Queries.EvalInt i) with + | `Lifted i -> i + | _ -> IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind () + in + `Index (exp_as_int_dom, to_int_dom_offs o) in offs_to_idx t (to_int_dom_offs o) end From ff3e644d81efdd7f5adaadaec31f8cff99b0b77c Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 25 Aug 2023 18:52:41 +0200 Subject: [PATCH 0516/1312] Cover bot and top cases in to_int_dom_offs --- src/analyses/memOutOfBounds.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 329539ebfb..2b4ca3d4c4 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -191,7 +191,8 @@ struct | `Index (i, o) -> let exp_as_int_dom = match ctx.ask (Queries.EvalInt i) with | `Lifted i -> i - | _ -> IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind () + | `Bot -> IntDomain.IntDomTuple.bot_of @@ Cilfacade.ptrdiff_ikind () + | `Top -> IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind () in `Index (exp_as_int_dom, to_int_dom_offs o) in From a054a4f401772ea3318c640d4c0827dbce37e778 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 26 Aug 2023 12:29:39 +0200 Subject: [PATCH 0517/1312] Warn for UAF only in case there's a deref happening --- src/analyses/useAfterFree.ml | 65 +++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index c1f963b466..e88cfc1e51 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -82,38 +82,43 @@ struct end let rec warn_lval_might_contain_freed ?(is_double_free = false) (transfer_fn_name:string) ctx (lval:lval) = - let state = ctx.local in - let undefined_behavior = if is_double_free then Undefined DoubleFree else Undefined UseAfterFree in - let cwe_number = if is_double_free then 415 else 416 in - let rec offset_might_contain_freed offset = - match offset with - | NoOffset -> () - | Field (f, o) -> offset_might_contain_freed o - | Index (e, o) -> warn_exp_might_contain_freed transfer_fn_name ctx e; offset_might_contain_freed o - in - let (lval_host, o) = lval in offset_might_contain_freed o; (* Check the lval's offset *) - let lval_to_query = - match lval_host with - | Var _ -> Lval lval - | Mem _ -> mkAddrOf lval (* Take the lval's address if its lhost is of the form *p, where p is a ptr *) - in - match ctx.ask (Queries.MayPointTo lval_to_query) with - | a when not (Queries.LS.is_top a) && not (Queries.LS.mem (dummyFunDec.svar, `NoOffset) a) -> - let warn_for_heap_var var = - if D.mem var state then begin - AnalysisState.svcomp_may_use_after_free := true; - M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "lval (%s) in \"%s\" points to a maybe freed memory region" var.vname transfer_fn_name - end + match is_double_free, lval with + (* If we're not checking for a double-free and there's no deref happening, then there's no need to check for an invalid deref or an invalid free *) + | false, (Var _, NoOffset) -> () + | _ -> + let state = ctx.local in + let undefined_behavior = if is_double_free then Undefined DoubleFree else Undefined UseAfterFree in + let cwe_number = if is_double_free then 415 else 416 in + let rec offset_might_contain_freed = function + | NoOffset -> () + | Field (f, o) -> offset_might_contain_freed o + | Index (e, o) -> warn_exp_might_contain_freed transfer_fn_name ctx e; offset_might_contain_freed o in - let pointed_to_heap_vars = - Queries.LS.elements a - |> List.map fst - |> List.filter (fun var -> ctx.ask (Queries.IsHeapVar var)) + let (lval_host, o) = lval in offset_might_contain_freed o; (* Check the lval's offset *) + let lval_to_query = + match lval_host with + | Var _ -> Lval lval + | Mem _ -> mkAddrOf lval (* Take the lval's address if its lhost is of the form *p, where p is a ptr *) in - List.iter warn_for_heap_var pointed_to_heap_vars; (* Warn for all heap vars that the lval possibly points to *) - (* Warn for a potential multi-threaded UAF for all heap vars that the lval possibly points to *) - List.iter (fun heap_var -> warn_for_multi_threaded_access ctx heap_var undefined_behavior cwe_number) pointed_to_heap_vars - | _ -> () + begin match ctx.ask (Queries.MayPointTo lval_to_query) with + | a when not (Queries.LS.is_top a) && not (Queries.LS.mem (dummyFunDec.svar, `NoOffset) a) -> + let warn_for_heap_var var = + if D.mem var state then begin + AnalysisState.svcomp_may_use_after_free := true; + M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "lval (%s) in \"%s\" points to a maybe freed memory region" var.vname transfer_fn_name + end + in + let pointed_to_heap_vars = + Queries.LS.elements a + |> List.map fst + |> List.filter (fun var -> ctx.ask (Queries.IsHeapVar var)) + in + (* Warn for all heap vars that the lval possibly points to *) + List.iter warn_for_heap_var pointed_to_heap_vars; + (* Warn for a potential multi-threaded UAF for all heap vars that the lval possibly points to *) + List.iter (fun heap_var -> warn_for_multi_threaded_access ctx heap_var undefined_behavior cwe_number) pointed_to_heap_vars + | _ -> () + end and warn_exp_might_contain_freed ?(is_double_free = false) (transfer_fn_name:string) ctx (exp:exp) = match exp with From 32714309d38154f3af572c8186879cc6eb7da176 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 26 Aug 2023 12:30:01 +0200 Subject: [PATCH 0518/1312] Adapt regression tests to not WARN for UAF if there's no deref --- .../74-use_after_free/04-function-call-uaf.c | 3 ++- tests/regression/74-use_after_free/06-uaf-struct.c | 8 +++++--- tests/regression/74-use_after_free/09-juliet-uaf.c | 8 +++++--- .../74-use_after_free/11-wrapper-funs-uaf.c | 12 +++++++----- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/tests/regression/74-use_after_free/04-function-call-uaf.c b/tests/regression/74-use_after_free/04-function-call-uaf.c index f83f9966b4..d110db9edc 100644 --- a/tests/regression/74-use_after_free/04-function-call-uaf.c +++ b/tests/regression/74-use_after_free/04-function-call-uaf.c @@ -17,7 +17,8 @@ int main() { free(ptr1); free(ptr2); - f(ptr1, ptr2, ptr3); //WARN + // No deref happening in the function call, hence nothing to warn about + f(ptr1, ptr2, ptr3); //NOWARN free(ptr3); //WARN diff --git a/tests/regression/74-use_after_free/06-uaf-struct.c b/tests/regression/74-use_after_free/06-uaf-struct.c index 02c4f3e77a..13eed8e3db 100644 --- a/tests/regression/74-use_after_free/06-uaf-struct.c +++ b/tests/regression/74-use_after_free/06-uaf-struct.c @@ -17,13 +17,15 @@ int main(int argc, char **argv) { char line[128]; while (1) { - printf("[ auth = %p, service = %p ]\n", auth, service); //WARN + // No deref happening here => nothing to warn about + printf("[ auth = %p, service = %p ]\n", auth, service); //NOWARN if (fgets(line, sizeof(line), stdin) == NULL) break; if (strncmp(line, "auth ", 5) == 0) { - auth = malloc(sizeof(auth)); //WARN - memset(auth, 0, sizeof(auth)); //WARN + // No deref happening in either of the 2 lines below => no need to warn + auth = malloc(sizeof(auth)); //NOWARN + memset(auth, 0, sizeof(auth)); //NOWARN if (strlen(line + 5) < 31) { strcpy(auth->name, line + 5); //WARN } diff --git a/tests/regression/74-use_after_free/09-juliet-uaf.c b/tests/regression/74-use_after_free/09-juliet-uaf.c index 5a5bf3ee32..c37ef26aca 100644 --- a/tests/regression/74-use_after_free/09-juliet-uaf.c +++ b/tests/regression/74-use_after_free/09-juliet-uaf.c @@ -21,7 +21,8 @@ static char * helperBad(char * aString) reversedString[i] = '\0'; free(reversedString); - return reversedString; // WARN (Use After Free (CWE-416)) + // No need to warn in the line below, as there's no dereferencing happening + return reversedString; // NOWARN } else { @@ -67,8 +68,9 @@ void CWE416_Use_After_Free__return_freed_ptr_08_bad() if(staticReturnsTrue()) { { - char * reversedString = helperBad("BadSink"); // WARN (Use After Free (CWE-416)) - printf("%s\n", reversedString); // WARN (Use After Free (CWE-416)) + // No need to warn in the two lines below, since there's no dereferencing of the freed memory + char * reversedString = helperBad("BadSink"); // NOWARN + printf("%s\n", reversedString); // NOWARN } } } diff --git a/tests/regression/74-use_after_free/11-wrapper-funs-uaf.c b/tests/regression/74-use_after_free/11-wrapper-funs-uaf.c index 3ed540b53d..6ae79e0062 100644 --- a/tests/regression/74-use_after_free/11-wrapper-funs-uaf.c +++ b/tests/regression/74-use_after_free/11-wrapper-funs-uaf.c @@ -27,12 +27,14 @@ int main(int argc, char const *argv[]) { my_free2(p); *(p + 42) = 'c'; //WARN - printf("%s", p); //WARN - - char *p2 = p; //WARN + // No dereferencing in the line below => no need to warn + printf("%s", p); //NOWARN - my_free2(p); //WARN - my_free2(p2); //WARN + // No dereferencing happening in the lines below => no need to warn for an invalid-deref + // Also no need to warn for an invalid-free, as the call to free is within these functions and they're not the "free" function itself + char *p2 = p; //NOWARN + my_free2(p); //NOWARN + my_free2(p2); //NOWARN return 0; } From d49db71fee1a9ffc125cf373eaa0bf2dab0baa58 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 26 Aug 2023 14:38:23 +0200 Subject: [PATCH 0519/1312] Clean up and use proper global vars for SV-COMP --- src/analyses/useAfterFree.ml | 21 +++++++++++++-------- src/framework/analysisState.ml | 4 ++-- src/witness/witness.ml | 2 +- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index e88cfc1e51..6e2ca3b04f 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -1,6 +1,5 @@ (** An analysis for the detection of use-after-free vulnerabilities ([useAfterFree]). *) -open GobConfig open GoblintCil open Analyses open MessageCategory @@ -25,13 +24,19 @@ struct (* HELPER FUNCTIONS *) + let set_global_svcomp_var is_double_free = + if is_double_free then + AnalysisState.svcomp_may_invalid_free := true + else + AnalysisState.svcomp_may_invalid_deref := true + let get_current_threadid ctx = ctx.ask Queries.CurrentThreadId let get_joined_threads ctx = ctx.ask Queries.MustJoinedThreads - let warn_for_multi_threaded_access ctx (heap_var:varinfo) behavior cwe_number = + let warn_for_multi_threaded_access ctx ?(is_double_free = false) (heap_var:varinfo) behavior cwe_number = let freeing_threads = ctx.global heap_var in (* If we're single-threaded or there are no threads freeing the memory, we have nothing to WARN about *) if ctx.ask (Queries.MustBeSingleThreaded { since_start = true }) || G.is_empty freeing_threads then () @@ -59,23 +64,23 @@ struct | `Lifted current -> let possibly_started = G.exists (possibly_started current) freeing_threads in if possibly_started then begin - AnalysisState.svcomp_may_use_after_free := true; + set_global_svcomp_var is_double_free; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "There's a thread that's been started in parallel with the memory-freeing threads for heap variable %a. Use-After-Free might occur" CilType.Varinfo.pretty heap_var end else begin let current_is_unique = ThreadId.Thread.is_unique current in let any_equal_current threads = G.exists (equal_current current) threads in if not current_is_unique && any_equal_current freeing_threads then begin - AnalysisState.svcomp_may_use_after_free := true; + set_global_svcomp_var is_double_free; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Current thread is not unique and a Use-After-Free might occur for heap variable %a" CilType.Varinfo.pretty heap_var end else if D.mem heap_var ctx.local then begin - AnalysisState.svcomp_may_use_after_free := true; + set_global_svcomp_var is_double_free; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Use-After-Free might occur in current unique thread %a for heap variable %a" ThreadIdDomain.FlagConfiguredTID.pretty current CilType.Varinfo.pretty heap_var end end | `Top -> - AnalysisState.svcomp_may_use_after_free := true; + set_global_svcomp_var is_double_free; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "CurrentThreadId is top. A Use-After-Free might occur for heap variable %a" CilType.Varinfo.pretty heap_var | `Bot -> M.warn ~category:MessageCategory.Analyzer "CurrentThreadId is bottom" @@ -104,7 +109,7 @@ struct | a when not (Queries.LS.is_top a) && not (Queries.LS.mem (dummyFunDec.svar, `NoOffset) a) -> let warn_for_heap_var var = if D.mem var state then begin - AnalysisState.svcomp_may_use_after_free := true; + set_global_svcomp_var is_double_free; M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "lval (%s) in \"%s\" points to a maybe freed memory region" var.vname transfer_fn_name end in @@ -116,7 +121,7 @@ struct (* Warn for all heap vars that the lval possibly points to *) List.iter warn_for_heap_var pointed_to_heap_vars; (* Warn for a potential multi-threaded UAF for all heap vars that the lval possibly points to *) - List.iter (fun heap_var -> warn_for_multi_threaded_access ctx heap_var undefined_behavior cwe_number) pointed_to_heap_vars + List.iter (fun heap_var -> warn_for_multi_threaded_access ctx ~is_double_free heap_var undefined_behavior cwe_number) pointed_to_heap_vars | _ -> () end diff --git a/src/framework/analysisState.ml b/src/framework/analysisState.ml index e987c414f2..ca619d4dfb 100644 --- a/src/framework/analysisState.ml +++ b/src/framework/analysisState.ml @@ -7,8 +7,8 @@ let should_warn = ref false (** Whether signed overflow or underflow happened *) let svcomp_may_overflow = ref false -(** Whether a Use-After-Free (UAF) happened *) -let svcomp_may_use_after_free = ref false +(** Whether an invalid free happened *) +let svcomp_may_invalid_free = ref false (** Whether an invalid pointer dereference happened *) let svcomp_may_invalid_deref = ref false diff --git a/src/witness/witness.ml b/src/witness/witness.ml index 797541c606..8a77a10bc7 100644 --- a/src/witness/witness.ml +++ b/src/witness/witness.ml @@ -479,7 +479,7 @@ struct let next _ = [] end in - if not !AnalysisState.svcomp_may_use_after_free then + if not !AnalysisState.svcomp_may_invalid_free then let module TaskResult = struct module Arg = Arg From 034b0ab90198a60311485dc290b434386c41fd21 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 27 Aug 2023 13:49:07 +0200 Subject: [PATCH 0520/1312] Use proper bug names when warning for multi-threaded programs --- src/analyses/useAfterFree.ml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index 6e2ca3b04f..b680bc5369 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -60,28 +60,29 @@ struct | `Top -> true | `Bot -> false in + let bug_name = if is_double_free then "Double Free" else "Use After Free" in match get_current_threadid ctx with | `Lifted current -> let possibly_started = G.exists (possibly_started current) freeing_threads in if possibly_started then begin set_global_svcomp_var is_double_free; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "There's a thread that's been started in parallel with the memory-freeing threads for heap variable %a. Use-After-Free might occur" CilType.Varinfo.pretty heap_var + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "There's a thread that's been started in parallel with the memory-freeing threads for heap variable %a. %s might occur" CilType.Varinfo.pretty heap_var bug_name end else begin let current_is_unique = ThreadId.Thread.is_unique current in let any_equal_current threads = G.exists (equal_current current) threads in if not current_is_unique && any_equal_current freeing_threads then begin set_global_svcomp_var is_double_free; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Current thread is not unique and a Use-After-Free might occur for heap variable %a" CilType.Varinfo.pretty heap_var + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Current thread is not unique and a %s might occur for heap variable %a" bug_name CilType.Varinfo.pretty heap_var end else if D.mem heap_var ctx.local then begin set_global_svcomp_var is_double_free; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Use-After-Free might occur in current unique thread %a for heap variable %a" ThreadIdDomain.FlagConfiguredTID.pretty current CilType.Varinfo.pretty heap_var + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "%s might occur in current unique thread %a for heap variable %a" bug_name ThreadIdDomain.FlagConfiguredTID.pretty current CilType.Varinfo.pretty heap_var end end | `Top -> set_global_svcomp_var is_double_free; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "CurrentThreadId is top. A Use-After-Free might occur for heap variable %a" CilType.Varinfo.pretty heap_var + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "CurrentThreadId is top. %s might occur for heap variable %a" bug_name CilType.Varinfo.pretty heap_var | `Bot -> M.warn ~category:MessageCategory.Analyzer "CurrentThreadId is bottom" end From 68e1b7111bbf57a734a3ff9e1abce0e405e5f935 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 27 Aug 2023 14:00:17 +0200 Subject: [PATCH 0521/1312] Add a valid-memsafety.prp file --- tests/sv-comp/valid-memsafety.prp | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 tests/sv-comp/valid-memsafety.prp diff --git a/tests/sv-comp/valid-memsafety.prp b/tests/sv-comp/valid-memsafety.prp new file mode 100644 index 0000000000..06a87f5a37 --- /dev/null +++ b/tests/sv-comp/valid-memsafety.prp @@ -0,0 +1,4 @@ +CHECK( init(main()), LTL(G valid-free) ) +CHECK( init(main()), LTL(G valid-deref) ) +CHECK( init(main()), LTL(G valid-memtrack) ) + From 3cbc8e7db66d5373249eaac2b64b2a9f10a8aef3 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Mon, 28 Aug 2023 13:43:58 +0300 Subject: [PATCH 0522/1312] Remove done TODO from uninit --- src/analyses/uninit.ml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/analyses/uninit.ml b/src/analyses/uninit.ml index c14b1034a0..9bd8db2fe5 100644 --- a/src/analyses/uninit.ml +++ b/src/analyses/uninit.ml @@ -29,7 +29,6 @@ struct let threadspawn ctx lval f args fctx = ctx.local let exitstate v : D.t = D.empty () - (* TODO: Use AddressDomain for queries *) let access_address (ask: Queries.ask) write lv = match ask.f (Queries.MayPointTo (AddrOf lv)) with | ad when not (Queries.AD.is_top ad) -> From 6bfb97b845fe544d3f6c0aebec1ea8da4aecadf9 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Mon, 28 Aug 2023 14:42:32 +0300 Subject: [PATCH 0523/1312] Use AddressDomain for queries in base --- src/analyses/base.ml | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index e9a0580098..50729de29e 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -611,16 +611,6 @@ struct %> f (ContextUtil.should_keep ~isAttr:GobContext ~keepOption:"ana.base.context.interval" ~removeAttr:"base.no-interval" ~keepAttr:"base.interval" fd) drop_interval %> f (ContextUtil.should_keep ~isAttr:GobContext ~keepOption:"ana.base.context.interval_set" ~removeAttr:"base.no-interval_set" ~keepAttr:"base.interval_set" fd) drop_intervalSet - (* TODO: Use AddressDomain for queries *) - let convertToQueryLval = function - | ValueDomain.AD.Addr.Addr (v,o) -> [v, Addr.Offs.to_exp o] - | _ -> [] - - let addrToLvalSet a = - let add x y = Q.LS.add y x in - try - AD.fold (fun e c -> List.fold_left add c (convertToQueryLval e)) a (Q.LS.empty ()) - with SetDomain.Unsupported _ -> Q.LS.top () let reachable_top_pointers_types ctx (ps: AD.t) : Queries.TS.t = let module TS = Queries.TS in @@ -1261,14 +1251,15 @@ struct (* ignore @@ printf "BlobSize %a MayPointTo %a\n" d_plainexp e VD.pretty p; *) match p with | Address a -> - let s = addrToLvalSet a in let has_offset = function | `NoOffset -> false | `Field _ | `Index _ -> true in (* If there's a non-heap var or an offset in the lval set, we answer with bottom *) - if ValueDomainQueries.LS.exists (fun (v, o) -> (not @@ ctx.ask (Queries.IsHeapVar v)) || has_offset o) s then + if Q.AD.exists (function + | Q.AD.Addr.Addr (v, o) -> (not @@ ctx.ask (Queries.IsHeapVar v)) || has_offset o + | _ -> false) a then Queries.Result.bot q else ( let r = get ~full:true (Analyses.ask_of_ctx ctx) ctx.global ctx.local a None in @@ -1328,7 +1319,8 @@ struct (* ignore @@ printf "EvalStr Address: %a -> %s (must %i, may %i)\n" d_plainexp e (VD.short 80 (Address a)) (List.length @@ AD.to_var_must a) (List.length @@ AD.to_var_may a); *) begin match unrollType (Cilfacade.typeOf e) with | TPtr(TInt(IChar, _), _) -> - let lval = Mval.Exp.to_cil @@ Q.LS.choose @@ addrToLvalSet a in + let (v,o) = List.hd (AD.to_mval a) in + let lval = Mval.Exp.to_cil @@ (v, Addr.Offs.to_exp o) in (try `Lifted (Bytes.to_string (Hashtbl.find char_array lval)) with Not_found -> Queries.Result.top q) | _ -> (* what about ISChar and IUChar? *) @@ -2006,12 +1998,15 @@ struct invalidate ~deep:true ~ctx (Analyses.ask_of_ctx ctx) gs st' deep_addrs let check_free_of_non_heap_mem ctx special_fn ptr = + let has_non_heap_var = Q.AD.exists (function + | Q.AD.Addr.Addr (v,_) -> not (ctx.ask (Q.IsHeapVar v)) + | _ -> false) + in match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local ptr with | Address a -> - let points_to_set = addrToLvalSet a in - if Q.LS.is_top points_to_set then + if Q.AD.is_top a then M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Points-to set for pointer %a in function %s is top. Potential free of non-dynamically allocated memory may occur" d_exp ptr special_fn.vname - else if (Q.LS.exists (fun (v, _) -> not (ctx.ask (Q.IsHeapVar v))) points_to_set) || (AD.mem Addr.UnknownPtr a) then + else if has_non_heap_var a then M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Free of non-dynamically allocated memory in function %s for pointer %a" special_fn.vname d_exp ptr | _ -> M.warn ~category:MessageCategory.Analyzer "Pointer %a in function %s doesn't evaluate to a valid address." d_exp ptr special_fn.vname From 593822588ee466c72ae93aee485bf52074878b4f Mon Sep 17 00:00:00 2001 From: karoliineh Date: Mon, 28 Aug 2023 15:02:08 +0300 Subject: [PATCH 0524/1312] Remove unneccessary module calls before AD and Addr in base --- src/analyses/base.ml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 50729de29e..4183261ddb 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -108,7 +108,7 @@ struct | (info,(value:VD.t))::xs -> match value with | Address t when hasAttribute "goblint_array_domain" info.vattr -> - let possibleVars = List.to_seq (PreValueDomain.AD.to_var_may t) in + let possibleVars = List.to_seq (AD.to_var_may t) in Seq.fold_left (fun map arr -> VarMap.add arr (info.vattr) map) (pointedArrayMap xs) @@ Seq.filter (fun info -> isArrayType info.vtype) possibleVars | _ -> pointedArrayMap xs in @@ -345,7 +345,7 @@ struct if AD.is_definite x && AD.is_definite y then let ax = AD.choose x in let ay = AD.choose y in - let handle_address_is_multiple addr = begin match AD.Addr.to_var addr with + let handle_address_is_multiple addr = begin match Addr.to_var addr with | Some v when a.f (Q.IsMultiple v) -> if M.tracing then M.tracel "addr" "IsMultiple %a\n" CilType.Varinfo.pretty v; None @@ -353,7 +353,7 @@ struct Some true end in - match AD.Addr.semantic_equal ax ay with + match Addr.semantic_equal ax ay with | Some true -> if M.tracing then M.tracel "addr" "semantic_equal %a %a\n" AD.pretty x AD.pretty y; handle_address_is_multiple ax @@ -591,7 +591,7 @@ struct | Struct n -> Struct (ValueDomain.Structs.map replace_val n) | Union (f,v) -> Union (f,replace_val v) | Blob (n,s,o) -> Blob (replace_val n,s,o) - | Address x -> Address (ValueDomain.AD.map ValueDomain.Addr.top_indices x) + | Address x -> Address (AD.map ValueDomain.Addr.top_indices x) | x -> x in CPA.map replace_val st @@ -1257,8 +1257,8 @@ struct | `Index _ -> true in (* If there's a non-heap var or an offset in the lval set, we answer with bottom *) - if Q.AD.exists (function - | Q.AD.Addr.Addr (v, o) -> (not @@ ctx.ask (Queries.IsHeapVar v)) || has_offset o + if AD.exists (function + | Addr (v,o) -> (not @@ ctx.ask (Queries.IsHeapVar v)) || has_offset o | _ -> false) a then Queries.Result.bot q else ( @@ -1291,8 +1291,8 @@ struct | Address a -> let a' = AD.remove Addr.UnknownPtr a in (* run reachable_vars without unknown just to be safe: TODO why? *) let addrs = reachable_vars (Analyses.ask_of_ctx ctx) [a'] ctx.global ctx.local in - List.fold_left (Q.AD.join) (Q.AD.empty ()) addrs - | _ -> Q.AD.empty () + List.fold_left (AD.join) (AD.empty ()) addrs + | _ -> AD.empty () end | Q.ReachableUkTypes e -> begin match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local e with @@ -1998,13 +1998,13 @@ struct invalidate ~deep:true ~ctx (Analyses.ask_of_ctx ctx) gs st' deep_addrs let check_free_of_non_heap_mem ctx special_fn ptr = - let has_non_heap_var = Q.AD.exists (function - | Q.AD.Addr.Addr (v,_) -> not (ctx.ask (Q.IsHeapVar v)) + let has_non_heap_var = AD.exists (function + | Addr (v,_) -> not (ctx.ask (Q.IsHeapVar v)) | _ -> false) in match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local ptr with | Address a -> - if Q.AD.is_top a then + if AD.is_top a then M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Points-to set for pointer %a in function %s is top. Potential free of non-dynamically allocated memory may occur" d_exp ptr special_fn.vname else if has_non_heap_var a then M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Free of non-dynamically allocated memory in function %s for pointer %a" special_fn.vname d_exp ptr From cd95e8d45dbcaf68adacd3508cc5938eec4d500c Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Mon, 28 Aug 2023 22:17:59 +0200 Subject: [PATCH 0525/1312] Try to not warn for threads joined before free() --- src/analyses/useAfterFree.ml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index b680bc5369..94d996249d 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -45,11 +45,11 @@ struct match tid with | `Lifted tid -> let created_threads = ctx.ask Queries.CreatedThreads in - (* Discard joined threads, as they're supposed to be joined before the point of freeing the memory *) - let threads = ConcDomain.MustThreadSet.diff created_threads joined_threads in - let not_started = MHP.definitely_not_started (current, threads) tid in + let not_started = MHP.definitely_not_started (current, created_threads) tid in let possibly_started = not not_started in - possibly_started + (* If [current] is possibly running together with [tid], but is also joined before the free() in [tid], then no need to WARN *) + let current_joined_before_free = ConcDomain.MustThreadSet.mem current joined_threads in + possibly_started && not current_joined_before_free | `Top -> true | `Bot -> false in From ba4d301aede5ad76f7175f00a4ca82c09adeddc4 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Mon, 28 Aug 2023 22:18:35 +0200 Subject: [PATCH 0526/1312] Enable threadJoins for all multi-threaded test cases --- tests/regression/74-use_after_free/12-multi-threaded-uaf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression/74-use_after_free/12-multi-threaded-uaf.c b/tests/regression/74-use_after_free/12-multi-threaded-uaf.c index 0c647eff76..f6d11ae098 100644 --- a/tests/regression/74-use_after_free/12-multi-threaded-uaf.c +++ b/tests/regression/74-use_after_free/12-multi-threaded-uaf.c @@ -1,4 +1,4 @@ -//PARAM: --set ana.activated[+] useAfterFree +//PARAM: --set ana.activated[+] useAfterFree --set ana.activated[+] threadJoins #include #include #include From 66f0c9da414be2dc0d989a9dd22db35978bf00ef Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Mon, 28 Aug 2023 23:25:53 +0200 Subject: [PATCH 0527/1312] Add checks for implicit dereferencing of pointers --- src/analyses/useAfterFree.ml | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index 94d996249d..e8a0d99925 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -87,10 +87,10 @@ struct M.warn ~category:MessageCategory.Analyzer "CurrentThreadId is bottom" end - let rec warn_lval_might_contain_freed ?(is_double_free = false) (transfer_fn_name:string) ctx (lval:lval) = - match is_double_free, lval with + let rec warn_lval_might_contain_freed ?(is_implicitly_derefed = false) ?(is_double_free = false) (transfer_fn_name:string) ctx (lval:lval) = + match is_implicitly_derefed, is_double_free, lval with (* If we're not checking for a double-free and there's no deref happening, then there's no need to check for an invalid deref or an invalid free *) - | false, (Var _, NoOffset) -> () + | false, false, (Var _, NoOffset) -> () | _ -> let state = ctx.local in let undefined_behavior = if is_double_free then Undefined DoubleFree else Undefined UseAfterFree in @@ -126,7 +126,7 @@ struct | _ -> () end - and warn_exp_might_contain_freed ?(is_double_free = false) (transfer_fn_name:string) ctx (exp:exp) = + and warn_exp_might_contain_freed ?(is_implicitly_derefed = false) ?(is_double_free = false) (transfer_fn_name:string) ctx (exp:exp) = match exp with (* Base recursion cases *) | Const _ @@ -140,18 +140,18 @@ struct | SizeOfE e | AlignOfE e | UnOp (_, e, _) - | CastE (_, e) -> warn_exp_might_contain_freed ~is_double_free transfer_fn_name ctx e + | CastE (_, e) -> warn_exp_might_contain_freed ~is_implicitly_derefed ~is_double_free transfer_fn_name ctx e | BinOp (_, e1, e2, _) -> - warn_exp_might_contain_freed ~is_double_free transfer_fn_name ctx e1; - warn_exp_might_contain_freed ~is_double_free transfer_fn_name ctx e2 + warn_exp_might_contain_freed ~is_implicitly_derefed ~is_double_free transfer_fn_name ctx e1; + warn_exp_might_contain_freed ~is_implicitly_derefed ~is_double_free transfer_fn_name ctx e2 | Question (e1, e2, e3, _) -> - warn_exp_might_contain_freed ~is_double_free transfer_fn_name ctx e1; - warn_exp_might_contain_freed ~is_double_free transfer_fn_name ctx e2; - warn_exp_might_contain_freed ~is_double_free transfer_fn_name ctx e3 + warn_exp_might_contain_freed ~is_implicitly_derefed ~is_double_free transfer_fn_name ctx e1; + warn_exp_might_contain_freed ~is_implicitly_derefed ~is_double_free transfer_fn_name ctx e2; + warn_exp_might_contain_freed ~is_implicitly_derefed ~is_double_free transfer_fn_name ctx e3 (* Lval cases (need [warn_lval_might_contain_freed] for them) *) | Lval lval | StartOf lval - | AddrOf lval -> warn_lval_might_contain_freed ~is_double_free transfer_fn_name ctx lval + | AddrOf lval -> warn_lval_might_contain_freed ~is_implicitly_derefed ~is_double_free transfer_fn_name ctx lval let side_effect_mem_free ctx freed_heap_vars threadid joined_threads = let side_effect_globals_to_heap_var heap_var = @@ -210,9 +210,16 @@ struct let special ctx (lval:lval option) (f:varinfo) (arglist:exp list) : D.t = let state = ctx.local in - Option.iter (fun x -> warn_lval_might_contain_freed ("special: " ^ f.vname) ctx x) lval; - List.iter (fun arg -> warn_exp_might_contain_freed ~is_double_free:(f.vname = "free") ("special: " ^ f.vname) ctx arg) arglist; let desc = LibraryFunctions.find f in + let is_arg_implicitly_derefed arg = + let read_shallow_args = LibraryDesc.Accesses.find desc.accs { kind = Read; deep = false } arglist in + let read_deep_args = LibraryDesc.Accesses.find desc.accs { kind = Read; deep = true } arglist in + let write_shallow_args = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = false } arglist in + let write_deep_args = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = true } arglist in + List.mem arg read_shallow_args || List.mem arg read_deep_args || List.mem arg write_shallow_args || List.mem arg write_deep_args + in + Option.iter (fun x -> warn_lval_might_contain_freed ("special: " ^ f.vname) ctx x) lval; + List.iter (fun arg -> warn_exp_might_contain_freed ~is_implicitly_derefed:(is_arg_implicitly_derefed arg) ~is_double_free:(f.vname = "free") ("special: " ^ f.vname) ctx arg) arglist; match desc.special arglist with | Free ptr -> begin match ctx.ask (Queries.MayPointTo ptr) with From 54646364ce12217a8e64c300a20548698c646aac Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Mon, 28 Aug 2023 23:26:14 +0200 Subject: [PATCH 0528/1312] Accommodate implicit dereferencing in regression tests --- tests/regression/74-use_after_free/06-uaf-struct.c | 9 +++++---- tests/regression/74-use_after_free/09-juliet-uaf.c | 5 +++-- tests/regression/74-use_after_free/11-wrapper-funs-uaf.c | 4 ++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/tests/regression/74-use_after_free/06-uaf-struct.c b/tests/regression/74-use_after_free/06-uaf-struct.c index 13eed8e3db..fa3ffc7b56 100644 --- a/tests/regression/74-use_after_free/06-uaf-struct.c +++ b/tests/regression/74-use_after_free/06-uaf-struct.c @@ -17,15 +17,16 @@ int main(int argc, char **argv) { char line[128]; while (1) { - // No deref happening here => nothing to warn about - printf("[ auth = %p, service = %p ]\n", auth, service); //NOWARN + // printf() is considered an implicit deref => need to warn here + printf("[ auth = %p, service = %p ]\n", auth, service); //WARN if (fgets(line, sizeof(line), stdin) == NULL) break; if (strncmp(line, "auth ", 5) == 0) { - // No deref happening in either of the 2 lines below => no need to warn + // No deref happening in the line below => no need to warn auth = malloc(sizeof(auth)); //NOWARN - memset(auth, 0, sizeof(auth)); //NOWARN + // memset() is considered an implicit deref => need to warn + memset(auth, 0, sizeof(auth)); //WARN if (strlen(line + 5) < 31) { strcpy(auth->name, line + 5); //WARN } diff --git a/tests/regression/74-use_after_free/09-juliet-uaf.c b/tests/regression/74-use_after_free/09-juliet-uaf.c index c37ef26aca..e1a88508a6 100644 --- a/tests/regression/74-use_after_free/09-juliet-uaf.c +++ b/tests/regression/74-use_after_free/09-juliet-uaf.c @@ -68,9 +68,10 @@ void CWE416_Use_After_Free__return_freed_ptr_08_bad() if(staticReturnsTrue()) { { - // No need to warn in the two lines below, since there's no dereferencing of the freed memory + // No need to warn in the line below, since there's no dereferencing of the freed memory char * reversedString = helperBad("BadSink"); // NOWARN - printf("%s\n", reversedString); // NOWARN + // printf() is considered an implicit deref => need to warn here + printf("%s\n", reversedString); // WARN } } } diff --git a/tests/regression/74-use_after_free/11-wrapper-funs-uaf.c b/tests/regression/74-use_after_free/11-wrapper-funs-uaf.c index 6ae79e0062..cc6539eff2 100644 --- a/tests/regression/74-use_after_free/11-wrapper-funs-uaf.c +++ b/tests/regression/74-use_after_free/11-wrapper-funs-uaf.c @@ -27,8 +27,8 @@ int main(int argc, char const *argv[]) { my_free2(p); *(p + 42) = 'c'; //WARN - // No dereferencing in the line below => no need to warn - printf("%s", p); //NOWARN + // printf() is considered an implicit deref => need to warn + printf("%s", p); //WARN // No dereferencing happening in the lines below => no need to warn for an invalid-deref // Also no need to warn for an invalid-free, as the call to free is within these functions and they're not the "free" function itself From 049dc5853368f7b604aeb1a17aa449788d66ee72 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Mon, 28 Aug 2023 23:32:26 +0200 Subject: [PATCH 0529/1312] Remove redundant intersection when side-effecting --- src/analyses/useAfterFree.ml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index e8a0d99925..9af1b8ca7a 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -156,11 +156,7 @@ struct let side_effect_mem_free ctx freed_heap_vars threadid joined_threads = let side_effect_globals_to_heap_var heap_var = let current_globals = ctx.global heap_var in - let joined_threads_to_add = match G.find_opt threadid current_globals with - | Some threads -> ConcDomain.ThreadSet.inter joined_threads threads - | None -> joined_threads - in - let globals_to_side_effect = G.add threadid joined_threads_to_add current_globals in + let globals_to_side_effect = G.add threadid joined_threads current_globals in ctx.sideg heap_var globals_to_side_effect in D.iter side_effect_globals_to_heap_var freed_heap_vars From dc0df887e22adc34ff93f170f337ddcc7b9aff19 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 29 Aug 2023 12:27:57 +0300 Subject: [PATCH 0530/1312] Add MustLocksetA and MustProtectedVarsA to queries --- src/domains/queries.ml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 0388ce2995..8680103d7a 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -81,6 +81,7 @@ type _ t = | MayBePublicWithout: maybepublicwithout -> MayBool.t t | MustBeProtectedBy: mustbeprotectedby -> MustBool.t t | MustLockset: LS.t t + | MustLocksetA: AD.t t | MustBeAtomic: MustBool.t t | MustBeSingleThreaded: {since_start: bool} -> MustBool.t t | MustBeUniqueThread: MustBool.t t @@ -115,6 +116,7 @@ type _ t = | MustJoinedThreads: ConcDomain.MustThreadSet.t t | ThreadsJoinedCleanly: MustBool.t t | MustProtectedVars: mustprotectedvars -> LS.t t + | MustProtectedVarsA: mustprotectedvars -> AD.t t | Invariant: invariant_context -> Invariant.t t | InvariantGlobal: Obj.t -> Invariant.t t (** Argument must be of corresponding [Spec.V.t]. *) | WarnGlobal: Obj.t -> Unit.t t (** Argument must be of corresponding [Spec.V.t]. *) @@ -145,6 +147,7 @@ struct | ReachableFrom _ -> (module AD) | Regions _ -> (module LS) | MustLockset -> (module LS) + | MustLocksetA -> (module AD) | EvalFunvar _ -> (module LS) | ReachableUkTypes _ -> (module TS) | MayEscape _ -> (module MayBool) @@ -180,6 +183,7 @@ struct | MustJoinedThreads -> (module ConcDomain.MustThreadSet) | ThreadsJoinedCleanly -> (module MustBool) | MustProtectedVars _ -> (module LS) + | MustProtectedVarsA _ -> (module AD) | Invariant _ -> (module Invariant) | InvariantGlobal _ -> (module Invariant) | WarnGlobal _ -> (module Unit) @@ -209,6 +213,7 @@ struct | ReachableFrom _ -> AD.top () | Regions _ -> LS.top () | MustLockset -> LS.top () + | MustLocksetA -> AD.top () | EvalFunvar _ -> LS.top () | ReachableUkTypes _ -> TS.top () | MayEscape _ -> MayBool.top () @@ -244,6 +249,7 @@ struct | MustJoinedThreads -> ConcDomain.MustThreadSet.top () | ThreadsJoinedCleanly -> MustBool.top () | MustProtectedVars _ -> LS.top () + | MustProtectedVarsA _ -> AD.top () | Invariant _ -> Invariant.top () | InvariantGlobal _ -> Invariant.top () | WarnGlobal _ -> Unit.top () @@ -274,6 +280,7 @@ struct | Any (MayBePublicWithout _) -> 8 | Any (MustBeProtectedBy _) -> 9 | Any MustLockset -> 10 + | Any MustLocksetA -> 1010 | Any MustBeAtomic -> 11 | Any (MustBeSingleThreaded _)-> 12 | Any MustBeUniqueThread -> 13 @@ -299,6 +306,7 @@ struct | Any (IterSysVars _) -> 37 | Any (InvariantGlobal _) -> 38 | Any (MustProtectedVars _) -> 39 + | Any (MustProtectedVarsA _) -> 3939 | Any MayAccessed -> 40 | Any MayBeTainted -> 41 | Any (PathQuery _) -> 42 @@ -356,6 +364,7 @@ struct | Any (IterSysVars (vq1, vf1)), Any (IterSysVars (vq2, vf2)) -> VarQuery.compare vq1 vq2 (* not comparing fs *) | Any (MutexType m1), Any (MutexType m2) -> Mval.Unit.compare m1 m2 | Any (MustProtectedVars m1), Any (MustProtectedVars m2) -> compare_mustprotectedvars m1 m2 + | Any (MustProtectedVarsA m1), Any (MustProtectedVarsA m2) -> compare_mustprotectedvars m1 m2 | Any (MayBeModifiedSinceSetjmp e1), Any (MayBeModifiedSinceSetjmp e2) -> JmpBufDomain.BufferEntry.compare e1 e2 | Any (MustBeSingleThreaded {since_start=s1;}), Any (MustBeSingleThreaded {since_start=s2;}) -> Stdlib.compare s1 s2 | Any (TmpSpecial lv1), Any (TmpSpecial lv2) -> Mval.Exp.compare lv1 lv2 @@ -395,6 +404,7 @@ struct | Any (MutexType m) -> Mval.Unit.hash m | Any (InvariantGlobal vi) -> Hashtbl.hash vi | Any (MustProtectedVars m) -> hash_mustprotectedvars m + | Any (MustProtectedVarsA m) -> hash_mustprotectedvars m | Any (MayBeModifiedSinceSetjmp e) -> JmpBufDomain.BufferEntry.hash e | Any (MustBeSingleThreaded {since_start}) -> Hashtbl.hash since_start | Any (TmpSpecial lv) -> Mval.Exp.hash lv @@ -417,6 +427,7 @@ struct | Any (MayBePublicWithout x) -> Pretty.dprintf "MayBePublicWithout _" | Any (MustBeProtectedBy x) -> Pretty.dprintf "MustBeProtectedBy _" | Any MustLockset -> Pretty.dprintf "MustLockset" + | Any MustLocksetA -> Pretty.dprintf "MustLocksetA" | Any MustBeAtomic -> Pretty.dprintf "MustBeAtomic" | Any (MustBeSingleThreaded {since_start}) -> Pretty.dprintf "MustBeSingleThreaded since_start=%b" since_start | Any MustBeUniqueThread -> Pretty.dprintf "MustBeUniqueThread" @@ -445,6 +456,7 @@ struct | Any MustJoinedThreads -> Pretty.dprintf "MustJoinedThreads" | Any ThreadsJoinedCleanly -> Pretty.dprintf "ThreadsJoinedCleanly" | Any (MustProtectedVars m) -> Pretty.dprintf "MustProtectedVars _" + | Any (MustProtectedVarsA m) -> Pretty.dprintf "MustProtectedVarsA _" | Any (Invariant i) -> Pretty.dprintf "Invariant _" | Any (WarnGlobal vi) -> Pretty.dprintf "WarnGlobal _" | Any (IterSysVars _) -> Pretty.dprintf "IterSysVars _" From 326b8e66d7ed97d21ce894bf0a32612487038815 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 29 Aug 2023 12:28:42 +0300 Subject: [PATCH 0531/1312] Use MustLocksetA and MustProtectedVarsA in mutexAnalysis --- src/analyses/base.ml | 1 + src/analyses/commonPriv.ml | 19 +++++++++---------- src/analyses/mutexAnalysis.ml | 10 ++-------- 3 files changed, 12 insertions(+), 18 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 4183261ddb..d7c7d4429b 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2542,6 +2542,7 @@ struct | MayBePublicWithout _ | MustBeProtectedBy _ | MustLockset + | MustLocksetA | MustBeAtomic | MustBeSingleThreaded _ | MustBeUniqueThread diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index 1b92cb320d..4aca250ba5 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -61,12 +61,13 @@ struct let protected_vars (ask: Q.ask): varinfo list = let module VS = Set.Make (CilType.Varinfo) in - Q.LS.fold (fun (v, _) acc -> - let m = ValueDomain.Addr.of_var v in (* TODO: don't ignore offsets *) - Q.LS.fold (fun l acc -> - VS.add (fst l) acc (* always `NoOffset from mutex analysis *) - ) (ask.f (Q.MustProtectedVars {mutex = m; write = true})) acc - ) (ask.f Q.MustLockset) VS.empty + Q.AD.fold (fun m acc -> + Q.AD.fold (fun l acc -> + match l with + | Q.AD.Addr.Addr (v,_) -> VS.add v acc (* always `NoOffset from mutex analysis *) + | _ -> acc + ) (ask.f (Q.MustProtectedVarsA {mutex = m; write = true})) acc + ) (ask.f Q.MustLocksetA) VS.empty |> VS.elements end @@ -126,10 +127,8 @@ struct if !AnalysisState.global_initialization then Lockset.empty () else - let ls = ask.f Queries.MustLockset in - Q.LS.fold (fun (var, offs) acc -> - Lockset.add (Lock.of_mval (var, Lock.Offs.of_exp offs)) acc - ) ls (Lockset.empty ()) + let ad = ask.f Queries.MustLocksetA in + Q.AD.fold (fun mls acc -> Lockset.add mls acc) ad (Lockset.empty ()) (* TODO: reversed SetDomain.Hoare *) module MinLocksets = HoareDomain.Set_LiftTop (MustLockset) (struct let topname = "All locksets" end) (* reverse Lockset because Hoare keeps maximal, but we need minimal *) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 7d8298a0a4..bc03216ba6 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -231,15 +231,9 @@ struct true else *) Mutexes.leq mutex_lockset protecting - | Queries.MustLockset -> + | Queries.MustLocksetA -> let held_locks = Lockset.export_locks (Lockset.filter snd ls) in - let ls = Mutexes.fold (fun addr ls -> - match Addr.to_mval addr with - | Some (var, offs) -> Queries.LS.add (var, Addr.Offs.to_exp offs) ls - | None -> ls - ) held_locks (Queries.LS.empty ()) - in - ls + Mutexes.fold (fun addr ls -> Queries.AD.add addr ls) held_locks (Queries.AD.empty ()) | Queries.MustBeAtomic -> let held_locks = Lockset.export_locks (Lockset.filter snd ls) in Mutexes.mem (LockDomain.Addr.of_var LF.verifier_atomic_var) held_locks From 2bf93536925839af61816451656586208012f763 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 29 Aug 2023 14:44:53 +0300 Subject: [PATCH 0532/1312] Use MustProtectedVarsA in mutexAnalysis --- src/analyses/mutexAnalysis.ml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index bc03216ba6..9bde708566 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -242,6 +242,11 @@ struct VarSet.fold (fun v acc -> Queries.LS.add (v, `NoOffset) acc ) protected (Queries.LS.empty ()) + | Queries.MustProtectedVarsA {mutex = m; write} -> + let protected = GProtected.get ~write Strong (G.protected (ctx.global (V.protected m))) in + VarSet.fold (fun v acc -> + Queries.AD.join (Queries.AD.of_var v) acc + ) protected (Queries.AD.empty ()) | Queries.IterSysVars (Global g, f) -> f (Obj.repr (V.protecting g)) (* TODO: something about V.protected? *) | WarnGlobal g -> From 65567a603f6b3fb8c7c869fa1bbc6f8a23d69691 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 29 Aug 2023 14:50:29 +0300 Subject: [PATCH 0533/1312] Remove MustLockset and MustProtectedVars from queries --- src/analyses/base.ml | 1 - src/analyses/mutexAnalysis.ml | 5 ----- src/domains/queries.ml | 16 ++-------------- 3 files changed, 2 insertions(+), 20 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index d7c7d4429b..a7330459f0 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2541,7 +2541,6 @@ struct | MayBePublic _ | MayBePublicWithout _ | MustBeProtectedBy _ - | MustLockset | MustLocksetA | MustBeAtomic | MustBeSingleThreaded _ diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 9bde708566..55172c48eb 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -237,11 +237,6 @@ struct | Queries.MustBeAtomic -> let held_locks = Lockset.export_locks (Lockset.filter snd ls) in Mutexes.mem (LockDomain.Addr.of_var LF.verifier_atomic_var) held_locks - | Queries.MustProtectedVars {mutex = m; write} -> - let protected = GProtected.get ~write Strong (G.protected (ctx.global (V.protected m))) in - VarSet.fold (fun v acc -> - Queries.LS.add (v, `NoOffset) acc - ) protected (Queries.LS.empty ()) | Queries.MustProtectedVarsA {mutex = m; write} -> let protected = GProtected.get ~write Strong (G.protected (ctx.global (V.protected m))) in VarSet.fold (fun v acc -> diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 8680103d7a..a160e6dd7b 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -80,7 +80,6 @@ type _ t = | MayBePublic: maybepublic -> MayBool.t t (* old behavior with write=false *) | MayBePublicWithout: maybepublicwithout -> MayBool.t t | MustBeProtectedBy: mustbeprotectedby -> MustBool.t t - | MustLockset: LS.t t | MustLocksetA: AD.t t | MustBeAtomic: MustBool.t t | MustBeSingleThreaded: {since_start: bool} -> MustBool.t t @@ -115,7 +114,6 @@ type _ t = | CreatedThreads: ConcDomain.ThreadSet.t t | MustJoinedThreads: ConcDomain.MustThreadSet.t t | ThreadsJoinedCleanly: MustBool.t t - | MustProtectedVars: mustprotectedvars -> LS.t t | MustProtectedVarsA: mustprotectedvars -> AD.t t | Invariant: invariant_context -> Invariant.t t | InvariantGlobal: Obj.t -> Invariant.t t (** Argument must be of corresponding [Spec.V.t]. *) @@ -146,7 +144,6 @@ struct | MayPointTo _ -> (module AD) | ReachableFrom _ -> (module AD) | Regions _ -> (module LS) - | MustLockset -> (module LS) | MustLocksetA -> (module AD) | EvalFunvar _ -> (module LS) | ReachableUkTypes _ -> (module TS) @@ -182,7 +179,6 @@ struct | CreatedThreads -> (module ConcDomain.ThreadSet) | MustJoinedThreads -> (module ConcDomain.MustThreadSet) | ThreadsJoinedCleanly -> (module MustBool) - | MustProtectedVars _ -> (module LS) | MustProtectedVarsA _ -> (module AD) | Invariant _ -> (module Invariant) | InvariantGlobal _ -> (module Invariant) @@ -212,7 +208,6 @@ struct | MayPointTo _ -> AD.top () | ReachableFrom _ -> AD.top () | Regions _ -> LS.top () - | MustLockset -> LS.top () | MustLocksetA -> AD.top () | EvalFunvar _ -> LS.top () | ReachableUkTypes _ -> TS.top () @@ -248,7 +243,6 @@ struct | CreatedThreads -> ConcDomain.ThreadSet.top () | MustJoinedThreads -> ConcDomain.MustThreadSet.top () | ThreadsJoinedCleanly -> MustBool.top () - | MustProtectedVars _ -> LS.top () | MustProtectedVarsA _ -> AD.top () | Invariant _ -> Invariant.top () | InvariantGlobal _ -> Invariant.top () @@ -279,8 +273,7 @@ struct | Any (MayBePublic _) -> 7 | Any (MayBePublicWithout _) -> 8 | Any (MustBeProtectedBy _) -> 9 - | Any MustLockset -> 10 - | Any MustLocksetA -> 1010 + | Any MustLocksetA -> 10 | Any MustBeAtomic -> 11 | Any (MustBeSingleThreaded _)-> 12 | Any MustBeUniqueThread -> 13 @@ -305,8 +298,7 @@ struct | Any (Invariant _) -> 36 | Any (IterSysVars _) -> 37 | Any (InvariantGlobal _) -> 38 - | Any (MustProtectedVars _) -> 39 - | Any (MustProtectedVarsA _) -> 3939 + | Any (MustProtectedVarsA _) -> 39 | Any MayAccessed -> 40 | Any MayBeTainted -> 41 | Any (PathQuery _) -> 42 @@ -363,7 +355,6 @@ struct | Any (InvariantGlobal vi1), Any (InvariantGlobal vi2) -> Stdlib.compare (Hashtbl.hash vi1) (Hashtbl.hash vi2) | Any (IterSysVars (vq1, vf1)), Any (IterSysVars (vq2, vf2)) -> VarQuery.compare vq1 vq2 (* not comparing fs *) | Any (MutexType m1), Any (MutexType m2) -> Mval.Unit.compare m1 m2 - | Any (MustProtectedVars m1), Any (MustProtectedVars m2) -> compare_mustprotectedvars m1 m2 | Any (MustProtectedVarsA m1), Any (MustProtectedVarsA m2) -> compare_mustprotectedvars m1 m2 | Any (MayBeModifiedSinceSetjmp e1), Any (MayBeModifiedSinceSetjmp e2) -> JmpBufDomain.BufferEntry.compare e1 e2 | Any (MustBeSingleThreaded {since_start=s1;}), Any (MustBeSingleThreaded {since_start=s2;}) -> Stdlib.compare s1 s2 @@ -403,7 +394,6 @@ struct | Any (Invariant i) -> hash_invariant_context i | Any (MutexType m) -> Mval.Unit.hash m | Any (InvariantGlobal vi) -> Hashtbl.hash vi - | Any (MustProtectedVars m) -> hash_mustprotectedvars m | Any (MustProtectedVarsA m) -> hash_mustprotectedvars m | Any (MayBeModifiedSinceSetjmp e) -> JmpBufDomain.BufferEntry.hash e | Any (MustBeSingleThreaded {since_start}) -> Hashtbl.hash since_start @@ -426,7 +416,6 @@ struct | Any (MayBePublic x) -> Pretty.dprintf "MayBePublic _" | Any (MayBePublicWithout x) -> Pretty.dprintf "MayBePublicWithout _" | Any (MustBeProtectedBy x) -> Pretty.dprintf "MustBeProtectedBy _" - | Any MustLockset -> Pretty.dprintf "MustLockset" | Any MustLocksetA -> Pretty.dprintf "MustLocksetA" | Any MustBeAtomic -> Pretty.dprintf "MustBeAtomic" | Any (MustBeSingleThreaded {since_start}) -> Pretty.dprintf "MustBeSingleThreaded since_start=%b" since_start @@ -455,7 +444,6 @@ struct | Any CreatedThreads -> Pretty.dprintf "CreatedThreads" | Any MustJoinedThreads -> Pretty.dprintf "MustJoinedThreads" | Any ThreadsJoinedCleanly -> Pretty.dprintf "ThreadsJoinedCleanly" - | Any (MustProtectedVars m) -> Pretty.dprintf "MustProtectedVars _" | Any (MustProtectedVarsA m) -> Pretty.dprintf "MustProtectedVarsA _" | Any (Invariant i) -> Pretty.dprintf "Invariant _" | Any (WarnGlobal vi) -> Pretty.dprintf "WarnGlobal _" From 4bf3680a0889e97f7e1cd845241139ccc95d144a Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 29 Aug 2023 14:55:02 +0300 Subject: [PATCH 0534/1312] Rename MustLocksetA -> MustLockset, MustProtectedVarsA -> MustProtectedVars --- src/analyses/base.ml | 2 +- src/analyses/commonPriv.ml | 6 +++--- src/analyses/mutexAnalysis.ml | 4 ++-- src/domains/queries.ml | 24 ++++++++++++------------ 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index a7330459f0..4183261ddb 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2541,7 +2541,7 @@ struct | MayBePublic _ | MayBePublicWithout _ | MustBeProtectedBy _ - | MustLocksetA + | MustLockset | MustBeAtomic | MustBeSingleThreaded _ | MustBeUniqueThread diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index 4aca250ba5..199fae98b0 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -66,8 +66,8 @@ struct match l with | Q.AD.Addr.Addr (v,_) -> VS.add v acc (* always `NoOffset from mutex analysis *) | _ -> acc - ) (ask.f (Q.MustProtectedVarsA {mutex = m; write = true})) acc - ) (ask.f Q.MustLocksetA) VS.empty + ) (ask.f (Q.MustProtectedVars {mutex = m; write = true})) acc + ) (ask.f Q.MustLockset) VS.empty |> VS.elements end @@ -127,7 +127,7 @@ struct if !AnalysisState.global_initialization then Lockset.empty () else - let ad = ask.f Queries.MustLocksetA in + let ad = ask.f Queries.MustLockset in Q.AD.fold (fun mls acc -> Lockset.add mls acc) ad (Lockset.empty ()) (* TODO: reversed SetDomain.Hoare *) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 55172c48eb..9cc019db76 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -231,13 +231,13 @@ struct true else *) Mutexes.leq mutex_lockset protecting - | Queries.MustLocksetA -> + | Queries.MustLockset -> let held_locks = Lockset.export_locks (Lockset.filter snd ls) in Mutexes.fold (fun addr ls -> Queries.AD.add addr ls) held_locks (Queries.AD.empty ()) | Queries.MustBeAtomic -> let held_locks = Lockset.export_locks (Lockset.filter snd ls) in Mutexes.mem (LockDomain.Addr.of_var LF.verifier_atomic_var) held_locks - | Queries.MustProtectedVarsA {mutex = m; write} -> + | Queries.MustProtectedVars {mutex = m; write} -> let protected = GProtected.get ~write Strong (G.protected (ctx.global (V.protected m))) in VarSet.fold (fun v acc -> Queries.AD.join (Queries.AD.of_var v) acc diff --git a/src/domains/queries.ml b/src/domains/queries.ml index a160e6dd7b..b10d1b673d 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -80,7 +80,7 @@ type _ t = | MayBePublic: maybepublic -> MayBool.t t (* old behavior with write=false *) | MayBePublicWithout: maybepublicwithout -> MayBool.t t | MustBeProtectedBy: mustbeprotectedby -> MustBool.t t - | MustLocksetA: AD.t t + | MustLockset: AD.t t | MustBeAtomic: MustBool.t t | MustBeSingleThreaded: {since_start: bool} -> MustBool.t t | MustBeUniqueThread: MustBool.t t @@ -114,7 +114,7 @@ type _ t = | CreatedThreads: ConcDomain.ThreadSet.t t | MustJoinedThreads: ConcDomain.MustThreadSet.t t | ThreadsJoinedCleanly: MustBool.t t - | MustProtectedVarsA: mustprotectedvars -> AD.t t + | MustProtectedVars: mustprotectedvars -> AD.t t | Invariant: invariant_context -> Invariant.t t | InvariantGlobal: Obj.t -> Invariant.t t (** Argument must be of corresponding [Spec.V.t]. *) | WarnGlobal: Obj.t -> Unit.t t (** Argument must be of corresponding [Spec.V.t]. *) @@ -144,7 +144,7 @@ struct | MayPointTo _ -> (module AD) | ReachableFrom _ -> (module AD) | Regions _ -> (module LS) - | MustLocksetA -> (module AD) + | MustLockset -> (module AD) | EvalFunvar _ -> (module LS) | ReachableUkTypes _ -> (module TS) | MayEscape _ -> (module MayBool) @@ -179,7 +179,7 @@ struct | CreatedThreads -> (module ConcDomain.ThreadSet) | MustJoinedThreads -> (module ConcDomain.MustThreadSet) | ThreadsJoinedCleanly -> (module MustBool) - | MustProtectedVarsA _ -> (module AD) + | MustProtectedVars _ -> (module AD) | Invariant _ -> (module Invariant) | InvariantGlobal _ -> (module Invariant) | WarnGlobal _ -> (module Unit) @@ -208,7 +208,7 @@ struct | MayPointTo _ -> AD.top () | ReachableFrom _ -> AD.top () | Regions _ -> LS.top () - | MustLocksetA -> AD.top () + | MustLockset -> AD.top () | EvalFunvar _ -> LS.top () | ReachableUkTypes _ -> TS.top () | MayEscape _ -> MayBool.top () @@ -243,7 +243,7 @@ struct | CreatedThreads -> ConcDomain.ThreadSet.top () | MustJoinedThreads -> ConcDomain.MustThreadSet.top () | ThreadsJoinedCleanly -> MustBool.top () - | MustProtectedVarsA _ -> AD.top () + | MustProtectedVars _ -> AD.top () | Invariant _ -> Invariant.top () | InvariantGlobal _ -> Invariant.top () | WarnGlobal _ -> Unit.top () @@ -273,7 +273,7 @@ struct | Any (MayBePublic _) -> 7 | Any (MayBePublicWithout _) -> 8 | Any (MustBeProtectedBy _) -> 9 - | Any MustLocksetA -> 10 + | Any MustLockset -> 10 | Any MustBeAtomic -> 11 | Any (MustBeSingleThreaded _)-> 12 | Any MustBeUniqueThread -> 13 @@ -298,7 +298,7 @@ struct | Any (Invariant _) -> 36 | Any (IterSysVars _) -> 37 | Any (InvariantGlobal _) -> 38 - | Any (MustProtectedVarsA _) -> 39 + | Any (MustProtectedVars _) -> 39 | Any MayAccessed -> 40 | Any MayBeTainted -> 41 | Any (PathQuery _) -> 42 @@ -355,7 +355,7 @@ struct | Any (InvariantGlobal vi1), Any (InvariantGlobal vi2) -> Stdlib.compare (Hashtbl.hash vi1) (Hashtbl.hash vi2) | Any (IterSysVars (vq1, vf1)), Any (IterSysVars (vq2, vf2)) -> VarQuery.compare vq1 vq2 (* not comparing fs *) | Any (MutexType m1), Any (MutexType m2) -> Mval.Unit.compare m1 m2 - | Any (MustProtectedVarsA m1), Any (MustProtectedVarsA m2) -> compare_mustprotectedvars m1 m2 + | Any (MustProtectedVars m1), Any (MustProtectedVars m2) -> compare_mustprotectedvars m1 m2 | Any (MayBeModifiedSinceSetjmp e1), Any (MayBeModifiedSinceSetjmp e2) -> JmpBufDomain.BufferEntry.compare e1 e2 | Any (MustBeSingleThreaded {since_start=s1;}), Any (MustBeSingleThreaded {since_start=s2;}) -> Stdlib.compare s1 s2 | Any (TmpSpecial lv1), Any (TmpSpecial lv2) -> Mval.Exp.compare lv1 lv2 @@ -394,7 +394,7 @@ struct | Any (Invariant i) -> hash_invariant_context i | Any (MutexType m) -> Mval.Unit.hash m | Any (InvariantGlobal vi) -> Hashtbl.hash vi - | Any (MustProtectedVarsA m) -> hash_mustprotectedvars m + | Any (MustProtectedVars m) -> hash_mustprotectedvars m | Any (MayBeModifiedSinceSetjmp e) -> JmpBufDomain.BufferEntry.hash e | Any (MustBeSingleThreaded {since_start}) -> Hashtbl.hash since_start | Any (TmpSpecial lv) -> Mval.Exp.hash lv @@ -416,7 +416,7 @@ struct | Any (MayBePublic x) -> Pretty.dprintf "MayBePublic _" | Any (MayBePublicWithout x) -> Pretty.dprintf "MayBePublicWithout _" | Any (MustBeProtectedBy x) -> Pretty.dprintf "MustBeProtectedBy _" - | Any MustLocksetA -> Pretty.dprintf "MustLocksetA" + | Any MustLockset -> Pretty.dprintf "MustLockset" | Any MustBeAtomic -> Pretty.dprintf "MustBeAtomic" | Any (MustBeSingleThreaded {since_start}) -> Pretty.dprintf "MustBeSingleThreaded since_start=%b" since_start | Any MustBeUniqueThread -> Pretty.dprintf "MustBeUniqueThread" @@ -444,7 +444,7 @@ struct | Any CreatedThreads -> Pretty.dprintf "CreatedThreads" | Any MustJoinedThreads -> Pretty.dprintf "MustJoinedThreads" | Any ThreadsJoinedCleanly -> Pretty.dprintf "ThreadsJoinedCleanly" - | Any (MustProtectedVarsA m) -> Pretty.dprintf "MustProtectedVarsA _" + | Any (MustProtectedVars m) -> Pretty.dprintf "MustProtectedVars _" | Any (Invariant i) -> Pretty.dprintf "Invariant _" | Any (WarnGlobal vi) -> Pretty.dprintf "WarnGlobal _" | Any (IterSysVars _) -> Pretty.dprintf "IterSysVars _" From 586a5959a6ba4090f25088c000c94a2a710611c6 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 29 Aug 2023 15:00:20 +0300 Subject: [PATCH 0535/1312] Add EvalFunvarA to queries --- src/domains/queries.ml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/domains/queries.ml b/src/domains/queries.ml index b10d1b673d..1f17b198b6 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -88,6 +88,7 @@ type _ t = | ThreadCreateIndexedNode: ThreadNodeLattice.t t | MayBeThreadReturn: MayBool.t t | EvalFunvar: exp -> LS.t t + | EvalFunvarA: exp -> AD.t t | EvalInt: exp -> ID.t t | EvalStr: exp -> SD.t t | EvalLength: exp -> ID.t t (* length of an array or string *) @@ -146,6 +147,7 @@ struct | Regions _ -> (module LS) | MustLockset -> (module AD) | EvalFunvar _ -> (module LS) + | EvalFunvarA _ -> (module AD) | ReachableUkTypes _ -> (module TS) | MayEscape _ -> (module MayBool) | MayBePublic _ -> (module MayBool) @@ -210,6 +212,7 @@ struct | Regions _ -> LS.top () | MustLockset -> AD.top () | EvalFunvar _ -> LS.top () + | EvalFunvarA _ -> AD.top () | ReachableUkTypes _ -> TS.top () | MayEscape _ -> MayBool.top () | MayBePublic _ -> MayBool.top () @@ -280,6 +283,7 @@ struct | Any CurrentThreadId -> 14 | Any MayBeThreadReturn -> 15 | Any (EvalFunvar _) -> 16 + | Any (EvalFunvarA _) -> 1616 | Any (EvalInt _) -> 17 | Any (EvalStr _) -> 18 | Any (EvalLength _) -> 19 @@ -330,6 +334,7 @@ struct | Any (MayBePublicWithout x1), Any (MayBePublicWithout x2) -> compare_maybepublicwithout x1 x2 | Any (MustBeProtectedBy x1), Any (MustBeProtectedBy x2) -> compare_mustbeprotectedby x1 x2 | Any (EvalFunvar e1), Any (EvalFunvar e2) -> CilType.Exp.compare e1 e2 + | Any (EvalFunvarA e1), Any (EvalFunvarA e2) -> CilType.Exp.compare e1 e2 | Any (EvalInt e1), Any (EvalInt e2) -> CilType.Exp.compare e1 e2 | Any (EvalStr e1), Any (EvalStr e2) -> CilType.Exp.compare e1 e2 | Any (EvalLength e1), Any (EvalLength e2) -> CilType.Exp.compare e1 e2 @@ -375,6 +380,7 @@ struct | Any (MayBePublicWithout x) -> hash_maybepublicwithout x | Any (MustBeProtectedBy x) -> hash_mustbeprotectedby x | Any (EvalFunvar e) -> CilType.Exp.hash e + | Any (EvalFunvarA e) -> CilType.Exp.hash e | Any (EvalInt e) -> CilType.Exp.hash e | Any (EvalStr e) -> CilType.Exp.hash e | Any (EvalLength e) -> CilType.Exp.hash e @@ -424,6 +430,7 @@ struct | Any ThreadCreateIndexedNode -> Pretty.dprintf "ThreadCreateIndexedNode" | Any MayBeThreadReturn -> Pretty.dprintf "MayBeThreadReturn" | Any (EvalFunvar e) -> Pretty.dprintf "EvalFunvar %a" CilType.Exp.pretty e + | Any (EvalFunvarA e) -> Pretty.dprintf "EvalFunvarA %a" CilType.Exp.pretty e | Any (EvalInt e) -> Pretty.dprintf "EvalInt %a" CilType.Exp.pretty e | Any (EvalStr e) -> Pretty.dprintf "EvalStr %a" CilType.Exp.pretty e | Any (EvalLength e) -> Pretty.dprintf "EvalLength %a" CilType.Exp.pretty e From f15b41fb6fe467c4b4ef113306bea23008d830a5 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 29 Aug 2023 15:08:10 +0300 Subject: [PATCH 0536/1312] Use EvalFunvarA in base --- src/analyses/base.ml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 4183261ddb..2272a953ca 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1207,6 +1207,11 @@ struct let fs = eval_funvar ctx e in List.fold_left (fun xs v -> Q.LS.add (v,`NoOffset) xs) (Q.LS.empty ()) fs end + | Q.EvalFunvarA e -> + begin + let fs = eval_funvar ctx e in + List.fold_left (fun ad v -> Q.AD.join (Q.AD.of_var v) ad) (Q.AD.empty ()) fs + end | Q.EvalJumpBuf e -> begin match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local e with | Address jmp_buf -> From bdd90b4c4f7cffa3130b126d1a7b746ee1d9d3d4 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 29 Aug 2023 15:35:27 +0300 Subject: [PATCH 0537/1312] Use EvalFunvarA in constraints --- src/framework/constraints.ml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 740d1f85a9..9bbfef9c89 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -299,12 +299,12 @@ struct let query ctx (type a) (q: a Queries.t): a Queries.result = match q with - | Queries.EvalFunvar e -> + | Queries.EvalFunvarA e -> let (d,l) = ctx.local in if leq0 l then - Queries.LS.empty () + Queries.AD.empty () else - query' ctx (Queries.EvalFunvar e) + query' ctx (Queries.EvalFunvarA e) | q -> query' ctx q end @@ -754,8 +754,8 @@ struct [v] | _ -> (* Depends on base for query. *) - let ls = ctx.ask (Queries.EvalFunvar e) in - Queries.LS.fold (fun ((x,_)) xs -> x::xs) ls [] + let ad = ctx.ask (Queries.EvalFunvarA e) in + List.filter_map (fun addr -> Queries.AD.Addr.to_var addr) (Queries.AD.elements ad) in let one_function f = match f.vtype with From e9e53a71080bcbca164906cd8bef1a1d420c5345 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 29 Aug 2023 15:39:07 +0300 Subject: [PATCH 0538/1312] Remove EvalFunvar queries --- src/analyses/base.ml | 5 ----- src/domains/queries.ml | 9 +-------- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 2272a953ca..c272bf1a63 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1202,11 +1202,6 @@ struct let query ctx (type a) (q: a Q.t): a Q.result = match q with - | Q.EvalFunvar e -> - begin - let fs = eval_funvar ctx e in - List.fold_left (fun xs v -> Q.LS.add (v,`NoOffset) xs) (Q.LS.empty ()) fs - end | Q.EvalFunvarA e -> begin let fs = eval_funvar ctx e in diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 1f17b198b6..648a0c561e 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -87,7 +87,6 @@ type _ t = | CurrentThreadId: ThreadIdDomain.ThreadLifted.t t | ThreadCreateIndexedNode: ThreadNodeLattice.t t | MayBeThreadReturn: MayBool.t t - | EvalFunvar: exp -> LS.t t | EvalFunvarA: exp -> AD.t t | EvalInt: exp -> ID.t t | EvalStr: exp -> SD.t t @@ -146,7 +145,6 @@ struct | ReachableFrom _ -> (module AD) | Regions _ -> (module LS) | MustLockset -> (module AD) - | EvalFunvar _ -> (module LS) | EvalFunvarA _ -> (module AD) | ReachableUkTypes _ -> (module TS) | MayEscape _ -> (module MayBool) @@ -211,7 +209,6 @@ struct | ReachableFrom _ -> AD.top () | Regions _ -> LS.top () | MustLockset -> AD.top () - | EvalFunvar _ -> LS.top () | EvalFunvarA _ -> AD.top () | ReachableUkTypes _ -> TS.top () | MayEscape _ -> MayBool.top () @@ -282,8 +279,7 @@ struct | Any MustBeUniqueThread -> 13 | Any CurrentThreadId -> 14 | Any MayBeThreadReturn -> 15 - | Any (EvalFunvar _) -> 16 - | Any (EvalFunvarA _) -> 1616 + | Any (EvalFunvarA _) -> 16 | Any (EvalInt _) -> 17 | Any (EvalStr _) -> 18 | Any (EvalLength _) -> 19 @@ -333,7 +329,6 @@ struct | Any (MayBePublic x1), Any (MayBePublic x2) -> compare_maybepublic x1 x2 | Any (MayBePublicWithout x1), Any (MayBePublicWithout x2) -> compare_maybepublicwithout x1 x2 | Any (MustBeProtectedBy x1), Any (MustBeProtectedBy x2) -> compare_mustbeprotectedby x1 x2 - | Any (EvalFunvar e1), Any (EvalFunvar e2) -> CilType.Exp.compare e1 e2 | Any (EvalFunvarA e1), Any (EvalFunvarA e2) -> CilType.Exp.compare e1 e2 | Any (EvalInt e1), Any (EvalInt e2) -> CilType.Exp.compare e1 e2 | Any (EvalStr e1), Any (EvalStr e2) -> CilType.Exp.compare e1 e2 @@ -379,7 +374,6 @@ struct | Any (MayBePublic x) -> hash_maybepublic x | Any (MayBePublicWithout x) -> hash_maybepublicwithout x | Any (MustBeProtectedBy x) -> hash_mustbeprotectedby x - | Any (EvalFunvar e) -> CilType.Exp.hash e | Any (EvalFunvarA e) -> CilType.Exp.hash e | Any (EvalInt e) -> CilType.Exp.hash e | Any (EvalStr e) -> CilType.Exp.hash e @@ -429,7 +423,6 @@ struct | Any CurrentThreadId -> Pretty.dprintf "CurrentThreadId" | Any ThreadCreateIndexedNode -> Pretty.dprintf "ThreadCreateIndexedNode" | Any MayBeThreadReturn -> Pretty.dprintf "MayBeThreadReturn" - | Any (EvalFunvar e) -> Pretty.dprintf "EvalFunvar %a" CilType.Exp.pretty e | Any (EvalFunvarA e) -> Pretty.dprintf "EvalFunvarA %a" CilType.Exp.pretty e | Any (EvalInt e) -> Pretty.dprintf "EvalInt %a" CilType.Exp.pretty e | Any (EvalStr e) -> Pretty.dprintf "EvalStr %a" CilType.Exp.pretty e From 9fef5946d61b009ea31d2090870ed73a5b8755de Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 29 Aug 2023 15:39:46 +0300 Subject: [PATCH 0539/1312] Rename EvalFunvarA -> EvalFunvar --- src/analyses/base.ml | 2 +- src/domains/queries.ml | 14 +++++++------- src/framework/constraints.ml | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index c272bf1a63..5dfbe3c64a 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1202,7 +1202,7 @@ struct let query ctx (type a) (q: a Q.t): a Q.result = match q with - | Q.EvalFunvarA e -> + | Q.EvalFunvar e -> begin let fs = eval_funvar ctx e in List.fold_left (fun ad v -> Q.AD.join (Q.AD.of_var v) ad) (Q.AD.empty ()) fs diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 648a0c561e..8626fb9d11 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -87,7 +87,7 @@ type _ t = | CurrentThreadId: ThreadIdDomain.ThreadLifted.t t | ThreadCreateIndexedNode: ThreadNodeLattice.t t | MayBeThreadReturn: MayBool.t t - | EvalFunvarA: exp -> AD.t t + | EvalFunvar: exp -> AD.t t | EvalInt: exp -> ID.t t | EvalStr: exp -> SD.t t | EvalLength: exp -> ID.t t (* length of an array or string *) @@ -145,7 +145,7 @@ struct | ReachableFrom _ -> (module AD) | Regions _ -> (module LS) | MustLockset -> (module AD) - | EvalFunvarA _ -> (module AD) + | EvalFunvar _ -> (module AD) | ReachableUkTypes _ -> (module TS) | MayEscape _ -> (module MayBool) | MayBePublic _ -> (module MayBool) @@ -209,7 +209,7 @@ struct | ReachableFrom _ -> AD.top () | Regions _ -> LS.top () | MustLockset -> AD.top () - | EvalFunvarA _ -> AD.top () + | EvalFunvar _ -> AD.top () | ReachableUkTypes _ -> TS.top () | MayEscape _ -> MayBool.top () | MayBePublic _ -> MayBool.top () @@ -279,7 +279,7 @@ struct | Any MustBeUniqueThread -> 13 | Any CurrentThreadId -> 14 | Any MayBeThreadReturn -> 15 - | Any (EvalFunvarA _) -> 16 + | Any (EvalFunvar _) -> 16 | Any (EvalInt _) -> 17 | Any (EvalStr _) -> 18 | Any (EvalLength _) -> 19 @@ -329,7 +329,7 @@ struct | Any (MayBePublic x1), Any (MayBePublic x2) -> compare_maybepublic x1 x2 | Any (MayBePublicWithout x1), Any (MayBePublicWithout x2) -> compare_maybepublicwithout x1 x2 | Any (MustBeProtectedBy x1), Any (MustBeProtectedBy x2) -> compare_mustbeprotectedby x1 x2 - | Any (EvalFunvarA e1), Any (EvalFunvarA e2) -> CilType.Exp.compare e1 e2 + | Any (EvalFunvar e1), Any (EvalFunvar e2) -> CilType.Exp.compare e1 e2 | Any (EvalInt e1), Any (EvalInt e2) -> CilType.Exp.compare e1 e2 | Any (EvalStr e1), Any (EvalStr e2) -> CilType.Exp.compare e1 e2 | Any (EvalLength e1), Any (EvalLength e2) -> CilType.Exp.compare e1 e2 @@ -374,7 +374,7 @@ struct | Any (MayBePublic x) -> hash_maybepublic x | Any (MayBePublicWithout x) -> hash_maybepublicwithout x | Any (MustBeProtectedBy x) -> hash_mustbeprotectedby x - | Any (EvalFunvarA e) -> CilType.Exp.hash e + | Any (EvalFunvar e) -> CilType.Exp.hash e | Any (EvalInt e) -> CilType.Exp.hash e | Any (EvalStr e) -> CilType.Exp.hash e | Any (EvalLength e) -> CilType.Exp.hash e @@ -423,7 +423,7 @@ struct | Any CurrentThreadId -> Pretty.dprintf "CurrentThreadId" | Any ThreadCreateIndexedNode -> Pretty.dprintf "ThreadCreateIndexedNode" | Any MayBeThreadReturn -> Pretty.dprintf "MayBeThreadReturn" - | Any (EvalFunvarA e) -> Pretty.dprintf "EvalFunvarA %a" CilType.Exp.pretty e + | Any (EvalFunvar e) -> Pretty.dprintf "EvalFunvar %a" CilType.Exp.pretty e | Any (EvalInt e) -> Pretty.dprintf "EvalInt %a" CilType.Exp.pretty e | Any (EvalStr e) -> Pretty.dprintf "EvalStr %a" CilType.Exp.pretty e | Any (EvalLength e) -> Pretty.dprintf "EvalLength %a" CilType.Exp.pretty e diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 9bbfef9c89..3ea62a2f4c 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -299,12 +299,12 @@ struct let query ctx (type a) (q: a Queries.t): a Queries.result = match q with - | Queries.EvalFunvarA e -> + | Queries.EvalFunvar e -> let (d,l) = ctx.local in if leq0 l then Queries.AD.empty () else - query' ctx (Queries.EvalFunvarA e) + query' ctx (Queries.EvalFunvar e) | q -> query' ctx q end @@ -754,7 +754,7 @@ struct [v] | _ -> (* Depends on base for query. *) - let ad = ctx.ask (Queries.EvalFunvarA e) in + let ad = ctx.ask (Queries.EvalFunvar e) in List.filter_map (fun addr -> Queries.AD.Addr.to_var addr) (Queries.AD.elements ad) in let one_function f = From 8e42121dd7a75bdc44568defb29e44dc5cec66a4 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 29 Aug 2023 15:45:30 +0300 Subject: [PATCH 0540/1312] Add MayBeTaintedA to queries --- src/domains/queries.ml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 8626fb9d11..2cefb3f318 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -121,6 +121,7 @@ type _ t = | IterSysVars: VarQuery.t * Obj.t VarQuery.f -> Unit.t t (** [iter_vars] for [Constraints.FromSpec]. [Obj.t] represents [Spec.V.t]. *) | MayAccessed: AccessDomain.EventSet.t t | MayBeTainted: LS.t t + | MayBeTaintedA: AD.t t | MayBeModifiedSinceSetjmp: JmpBufDomain.BufferEntry.t -> VS.t t | TmpSpecial: Mval.Exp.t -> ML.t t @@ -186,6 +187,7 @@ struct | IterSysVars _ -> (module Unit) | MayAccessed -> (module AccessDomain.EventSet) | MayBeTainted -> (module LS) + | MayBeTaintedA -> (module AD) | MayBeModifiedSinceSetjmp _ -> (module VS) | TmpSpecial _ -> (module ML) @@ -250,6 +252,7 @@ struct | IterSysVars _ -> Unit.top () | MayAccessed -> AccessDomain.EventSet.top () | MayBeTainted -> LS.top () + | MayBeTaintedA -> AD.top () | MayBeModifiedSinceSetjmp _ -> VS.top () | TmpSpecial _ -> ML.top () end @@ -301,6 +304,7 @@ struct | Any (MustProtectedVars _) -> 39 | Any MayAccessed -> 40 | Any MayBeTainted -> 41 + | Any MayBeTaintedA -> 4141 | Any (PathQuery _) -> 42 | Any DYojson -> 43 | Any (EvalValue _) -> 44 @@ -453,6 +457,7 @@ struct | Any (EvalMutexAttr a) -> Pretty.dprintf "EvalMutexAttr _" | Any MayAccessed -> Pretty.dprintf "MayAccessed" | Any MayBeTainted -> Pretty.dprintf "MayBeTainted" + | Any MayBeTaintedA -> Pretty.dprintf "MayBeTaintedA" | Any DYojson -> Pretty.dprintf "DYojson" | Any MayBeModifiedSinceSetjmp buf -> Pretty.dprintf "MayBeModifiedSinceSetjmp %a" JmpBufDomain.BufferEntry.pretty buf | Any (TmpSpecial lv) -> Pretty.dprintf "TmpSpecial %a" Mval.Exp.pretty lv From 392834b054524c2eeac202de7b7ffce375717be3 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 29 Aug 2023 17:01:14 +0300 Subject: [PATCH 0541/1312] Use MayBeTaintedA in base --- src/analyses/base.ml | 69 ++++++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 31 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 5dfbe3c64a..6f0f3a099a 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2386,34 +2386,38 @@ struct in if get_bool "sem.noreturn.dead_code" && Cil.hasAttribute "noreturn" f.vattr then raise Deadcode else st - let combine_st ctx (local_st : store) (fun_st : store) (tainted_lvs : Q.LS.t) : store = + let combine_st ctx (local_st : store) (fun_st : store) (tainted_lvs : AD.t) : store = let ask = (Analyses.ask_of_ctx ctx) in - Q.LS.fold (fun (v, o) st -> - if CPA.mem v fun_st.cpa then - let lval = Mval.Exp.to_cil (v,o) in - let address = eval_lv ask ctx.global st lval in - let lval_type = (AD.type_of address) in - if M.tracing then M.trace "taintPC" "updating %a; type: %a\n" Mval.Exp.pretty (v, o) d_type lval_type; - match (CPA.find_opt v (fun_st.cpa)), lval_type with - | None, _ -> st - (* partitioned arrays cannot be copied by individual lvalues, so if tainted just copy the whole callee value for the array variable *) - | Some (Array a), _ when (CArrays.domain_of_t a) = PartitionedDomain -> {st with cpa = CPA.add v (Array a) st.cpa} - (* "get" returned "unknown" when applied to a void type, so special case void types. This caused problems with some sv-comps (e.g. regtest 64 11) *) - | Some voidVal, TVoid _ -> {st with cpa = CPA.add v voidVal st.cpa} - | _, _ -> begin - let new_val = get ask ctx.global fun_st address None in - if M.tracing then M.trace "taintPC" "update val: %a\n\n" VD.pretty new_val; - let st' = set_savetop ~ctx ask ctx.global st address lval_type new_val in - let partDep = Dep.find_opt v fun_st.deps in - match partDep with - | None -> st' - (* if a var partitions an array, all cpa-info for arrays it may partition are added from callee to caller *) - | Some deps -> {st' with cpa = (Dep.VarSet.fold (fun v accCPA -> let val_opt = CPA.find_opt v fun_st.cpa in - match val_opt with - | None -> accCPA - | Some new_val -> CPA.add v new_val accCPA ) deps st'.cpa)} - end - else st) tainted_lvs local_st + AD.fold (fun addr st -> + match addr with + | Addr.Addr (v,o) -> + if CPA.mem v fun_st.cpa then + let lval = Addr.Mval.to_cil (v,o) in + let address = eval_lv ask ctx.global st lval in + let lval_type = Addr.type_of addr in + if M.tracing then M.trace "taintPC" "updating %a; type: %a\n" Addr.Mval.pretty (v,o) d_type lval_type; + match (CPA.find_opt v (fun_st.cpa)), lval_type with + | None, _ -> st + (* partitioned arrays cannot be copied by individual lvalues, so if tainted just copy the whole callee value for the array variable *) + | Some (Array a), _ when (CArrays.domain_of_t a) = PartitionedDomain -> {st with cpa = CPA.add v (Array a) st.cpa} + (* "get" returned "unknown" when applied to a void type, so special case void types. This caused problems with some sv-comps (e.g. regtest 64 11) *) + | Some voidVal, TVoid _ -> {st with cpa = CPA.add v voidVal st.cpa} + | _, _ -> begin + let new_val = get ask ctx.global fun_st address None in + if M.tracing then M.trace "taintPC" "update val: %a\n\n" VD.pretty new_val; + let st' = set_savetop ~ctx ask ctx.global st address lval_type new_val in + let partDep = Dep.find_opt v fun_st.deps in + match partDep with + | None -> st' + (* if a var partitions an array, all cpa-info for arrays it may partition are added from callee to caller *) + | Some deps -> {st' with cpa = (Dep.VarSet.fold (fun v accCPA -> let val_opt = CPA.find_opt v fun_st.cpa in + match val_opt with + | None -> accCPA + | Some new_val -> CPA.add v new_val accCPA ) deps st'.cpa)} + end + else st + | _ -> st + ) tainted_lvs local_st let combine_env ctx lval fexp f args fc au (f_ask: Queries.ask) = let combine_one (st: D.t) (fun_st: D.t) = @@ -2427,10 +2431,10 @@ struct (* Remove the return value as this is dealt with separately. *) let cpa_noreturn = CPA.remove (return_varinfo ()) fun_st.cpa in let ask = (Analyses.ask_of_ctx ctx) in - let tainted = f_ask.f Q.MayBeTainted in - if M.tracing then M.trace "taintPC" "combine for %s in base: tainted: %a\n" f.svar.vname Q.LS.pretty tainted; + let tainted = f_ask.f Q.MayBeTaintedA in + if M.tracing then M.trace "taintPC" "combine for %s in base: tainted: %a\n" f.svar.vname AD.pretty tainted; if M.tracing then M.trace "taintPC" "combine base:\ncaller: %a\ncallee: %a\n" CPA.pretty st.cpa CPA.pretty fun_st.cpa; - if Q.LS.is_top tainted then + if AD.is_top tainted then let cpa_local = CPA.filter (fun x _ -> not (is_global ask x)) st.cpa in let cpa' = CPA.fold CPA.add cpa_noreturn cpa_local in (* add cpa_noreturn to cpa_local *) if M.tracing then M.trace "taintPC" "combined: %a\n" CPA.pretty cpa'; @@ -2445,7 +2449,10 @@ struct let cpa_caller' = CPA.fold CPA.add cpa_new cpa_caller in if M.tracing then M.trace "taintPC" "cpa_caller': %a\n" CPA.pretty cpa_caller'; (* remove lvals from the tainted set that correspond to variables for which we just added a new mapping from the callee*) - let tainted = Q.LS.filter (fun (v, _) -> not (CPA.mem v cpa_new)) tainted in + let tainted = AD.filter (function + | Addr.Addr (v,_) -> not (CPA.mem v cpa_new) + | _ -> false + ) tainted in let st_combined = combine_st ctx {st with cpa = cpa_caller'} fun_st tainted in if M.tracing then M.trace "taintPC" "combined: %a\n" CPA.pretty st_combined.cpa; { fun_st with cpa = st_combined.cpa } From 224615f63b07448ba45afb79f42c2d8284d4b968 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 29 Aug 2023 20:19:38 +0300 Subject: [PATCH 0542/1312] Use MayBeTaintedA --- src/analyses/apron/relationAnalysis.apron.ml | 2 +- src/analyses/condVars.ml | 2 +- src/analyses/modifiedSinceLongjmp.ml | 10 +++-- src/analyses/poisonVariables.ml | 18 +++++---- src/analyses/taintPartialContexts.ml | 42 ++++++++------------ src/analyses/varEq.ml | 11 +++-- src/cdomains/addressDomain.ml | 2 + 7 files changed, 46 insertions(+), 41 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index ca60e5dc30..64b367be68 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -407,7 +407,7 @@ struct let arg_vars = f.sformals |> List.filter (RD.Tracked.varinfo_tracked) |> List.map RV.arg in if M.tracing then M.tracel "combine" "relation remove vars: %a\n" (docList (fun v -> Pretty.text (RD.Var.to_string v))) arg_vars; RD.remove_vars_with new_fun_rel arg_vars; (* fine to remove arg vars that also exist in caller because unify from new_rel adds them back with proper constraints *) - let tainted = f_ask.f Queries.MayBeTainted in + let tainted = f_ask.f Queries.MayBeTaintedA in let tainted_vars = TaintPartialContexts.conv_varset tainted in let new_rel = RD.keep_filter st.rel (fun var -> match RV.find_metadata var with diff --git a/src/analyses/condVars.ml b/src/analyses/condVars.ml index 04246bf28a..282f5bf020 100644 --- a/src/analyses/condVars.ml +++ b/src/analyses/condVars.ml @@ -146,7 +146,7 @@ struct (* combine caller's state with globals from callee *) (* TODO (precision): globals with only global vars are kept, the rest is lost -> collect which globals are assigned to *) (* D.merge (fun k s1 s2 -> match s2 with Some ss2 when (fst k).vglob && D.only_global_exprs ss2 -> s2 | _ when (fst k).vglob -> None | _ -> s1) ctx.local au *) - let tainted = TaintPartialContexts.conv_varset (f_ask.f Queries.MayBeTainted) in + let tainted = TaintPartialContexts.conv_varset (f_ask.f Queries.MayBeTaintedA) in D.only_untainted ctx.local tainted (* tainted globals might have changed... *) let combine_assign ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) (f_ask: Queries.ask) : D.t = diff --git a/src/analyses/modifiedSinceLongjmp.ml b/src/analyses/modifiedSinceLongjmp.ml index 0375bd3f74..3cc10196fd 100644 --- a/src/analyses/modifiedSinceLongjmp.ml +++ b/src/analyses/modifiedSinceLongjmp.ml @@ -24,10 +24,14 @@ struct not v.vglob (* *) && not (BaseUtil.is_volatile v) && v.vstorage <> Static let relevants_from_ls ls = - if Queries.LS.is_top ls then + if Queries.AD.is_top ls then VS.top () else - Queries.LS.fold (fun (v, _) acc -> if is_relevant v then VS.add v acc else acc) ls (VS.empty ()) + Queries.AD.fold (fun addr acc -> + match addr with + | Queries.AD.Addr.Addr (v, _) when is_relevant v -> VS.add v acc + | _ -> acc + ) ls (VS.empty ()) let relevants_from_ad ad = (* TODO: what about AD with both known and unknown pointers? *) @@ -45,7 +49,7 @@ struct [ctx.local, D.bot ()] (* enter with bot as opposed to IdentitySpec *) let combine_env ctx lval fexp f args fc au (f_ask: Queries.ask) = - let taintedcallee = relevants_from_ls (f_ask.f Queries.MayBeTainted) in + let taintedcallee = relevants_from_ls (f_ask.f Queries.MayBeTaintedA) in add_to_all_defined taintedcallee ctx.local let combine_assign ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) (f_ask:Queries.ask) : D.t = diff --git a/src/analyses/poisonVariables.ml b/src/analyses/poisonVariables.ml index f69e5b2fbc..ddbb6a5a40 100644 --- a/src/analyses/poisonVariables.ml +++ b/src/analyses/poisonVariables.ml @@ -15,12 +15,16 @@ struct let context _ _ = () - let check_mval tainted ((v, offset): Queries.LS.elt) = - if not v.vglob && VS.mem v tainted then - M.warn ~category:(Behavior (Undefined Other)) "Reading poisonous variable %a" CilType.Varinfo.pretty v + let check_mval tainted (addr: Queries.AD.elt) = + match addr with + | Queries.AD.Addr.Addr (v,_) -> + if not v.vglob && VS.mem v tainted then + M.warn ~category:(Behavior (Undefined Other)) "Reading poisonous variable %a" CilType.Varinfo.pretty v + | _ -> () - let rem_mval tainted ((v, offset): Queries.LS.elt) = match offset with - | `NoOffset -> VS.remove v tainted + let rem_mval tainted (addr: Queries.AD.elt) = + match addr with + | Queries.AD.Addr.Addr (v,`NoOffset) -> VS.remove v tainted | _ -> tainted (* If there is an offset, it is a bit harder to remove, as we don't know where the indeterminate value is *) @@ -90,7 +94,7 @@ struct | ad -> Queries.AD.iter (function (* Use original access state instead of current with removed written vars. *) - | Queries.AD.Addr.Addr (v,o) -> check_mval octx.local (v, ValueDomain.Offs.to_exp o) + | Queries.AD.Addr.Addr (v,o) -> check_mval octx.local (Queries.AD.Addr.Addr (v,o)) | _ -> () ) ad end; @@ -103,7 +107,7 @@ struct | ad -> Queries.AD.fold (fun addr vs -> match addr with - | Queries.AD.Addr.Addr (v,o) -> rem_mval vs (v, ValueDomain.Offs.to_exp o) + | Queries.AD.Addr.Addr _ -> rem_mval vs addr | _ -> vs ) ad ctx.local end diff --git a/src/analyses/taintPartialContexts.ml b/src/analyses/taintPartialContexts.ml index d4ea17d2e0..39183ef087 100644 --- a/src/analyses/taintPartialContexts.ml +++ b/src/analyses/taintPartialContexts.ml @@ -6,31 +6,19 @@ open GoblintCil open Analyses -module D = SetDomain.ToppedSet (Mval.Exp) (struct let topname = "All" end) - -let to_mvals ad = - (* TODO: should one handle ad with unknown pointers separately like in (all) other analyses? *) - Queries.AD.fold (fun addr mvals -> - match addr with - | Queries.AD.Addr.Addr (v,o) -> D.add (v, ValueDomain.Offs.to_exp o) mvals - | _ -> mvals - ) ad (D.empty ()) +module AD = ValueDomain.AD module Spec = struct include Analyses.IdentitySpec let name () = "taintPartialContexts" - module D = D + module D = AD module C = Lattice.Unit (* Add Lval or any Lval which it may point to to the set *) let taint_lval ctx (lval:lval) : D.t = - let d = ctx.local in - (match lval with - | (Var v, offs) -> D.add (v, Offset.Exp.of_cil offs) d - | (Mem e, _) -> D.union (to_mvals (ctx.ask (Queries.MayPointTo e))) d - ) + D.union (ctx.ask (Queries.MayPointTo (AddrOf lval))) ctx.local (* this analysis is context insensitive*) let context _ _ = () @@ -45,14 +33,12 @@ struct let d_return = if D.is_top d then d - else ( + else let locals = f.sformals @ f.slocals in - D.filter (fun (v, _) -> - not (List.exists (fun local -> - CilType.Varinfo.equal v local && not (ctx.ask (Queries.IsMultiple local)) - ) locals) + D.filter (function + | AD.Addr.Addr (v,_) -> not (List.exists (fun local -> CilType.Varinfo.equal v local && not (ctx.ask (Queries.IsMultiple local))) locals) + | _ -> false ) d - ) in if M.tracing then M.trace "taintPC" "returning from %s: tainted vars: %a\n without locals: %a\n" f.svar.vname D.pretty d D.pretty d_return; d_return @@ -94,9 +80,10 @@ struct else deep_addrs in - let d = List.fold_left (fun accD addr -> D.union accD (to_mvals (ctx.ask (Queries.MayPointTo addr)))) d shallow_addrs + (* TODO: should one handle ad with unknown pointers separately like in (all) other analyses? *) + let d = List.fold_left (fun accD addr -> D.union accD (ctx.ask (Queries.MayPointTo addr))) d shallow_addrs in - let d = List.fold_left (fun accD addr -> D.union accD (to_mvals (ctx.ask (Queries.ReachableFrom addr)))) d deep_addrs + let d = List.fold_left (fun accD addr -> D.union accD (ctx.ask (Queries.ReachableFrom addr))) d deep_addrs in d @@ -111,7 +98,7 @@ struct let query ctx (type a) (q: a Queries.t) : a Queries.result = match q with - | MayBeTainted -> (ctx.local : Queries.LS.t) + | MayBeTaintedA -> (ctx.local : Queries.AD.t) | _ -> Queries.Result.top q end @@ -122,5 +109,8 @@ let _ = module VS = SetDomain.ToppedSet(Basetype.Variables) (struct let topname = "All" end) (* Convert Lval set to (less precise) Varinfo set. *) -let conv_varset (lval_set : Spec.D.t) : VS.t = - if Spec.D.is_top lval_set then VS.top () else VS.of_list (List.map (fun (v, _) -> v) (Spec.D.elements lval_set)) +let conv_varset (addr_set : Spec.D.t) : VS.t = + if Spec.D.is_top addr_set then + VS.top () + else + VS.of_list (List.filter_map (fun addr -> Spec.D.Addr.to_var_may addr) (Spec.D.elements addr_set)) diff --git a/src/analyses/varEq.ml b/src/analyses/varEq.ml index 77823d99d7..3bfd188aba 100644 --- a/src/analyses/varEq.ml +++ b/src/analyses/varEq.ml @@ -433,14 +433,19 @@ struct | false -> [ctx.local,nst] let combine_env ctx lval fexp f args fc au (f_ask: Queries.ask) = - let tainted = f_ask.f Queries.MayBeTainted in + let tainted = f_ask.f Queries.MayBeTaintedA in let d_local = (* if we are multithreaded, we run the risk, that some mutex protected variables got unlocked, so in this case caller state goes to top TODO: !!Unsound, this analysis does not handle this case -> regtest 63 08!! *) - if Queries.LS.is_top tainted || not (ctx.ask (Queries.MustBeSingleThreaded {since_start = true})) then + if Queries.AD.is_top tainted || not (ctx.ask (Queries.MustBeSingleThreaded {since_start = true})) then D.top () else - let taint_exp = Queries.ES.of_list (List.map Mval.Exp.to_cil_exp (Queries.LS.elements tainted)) in + let taint_exp = + Queries.AD.elements tainted + |> List.filter_map Addr.to_mval + |> List.map Addr.Mval.to_cil_exp + |> Queries.ES.of_list + in D.filter (fun exp -> not (Queries.ES.mem exp taint_exp)) ctx.local in let d = D.meet au d_local in diff --git a/src/cdomains/addressDomain.ml b/src/cdomains/addressDomain.ml index 9f6ee56cbf..5981caf9ea 100644 --- a/src/cdomains/addressDomain.ml +++ b/src/cdomains/addressDomain.ml @@ -440,4 +440,6 @@ struct let r = narrow x y in if M.tracing then M.traceu "ad" "-> %a\n" pretty r; r + + let filter f ad = fold (fun addr ad -> if f addr then add addr ad else ad) ad (empty ()) end From 26151efbaf3d2cb107a69209ca5414c96d9b1cd2 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 29 Aug 2023 20:21:09 +0300 Subject: [PATCH 0543/1312] Remove MayBeTainted from queries --- src/domains/queries.ml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 2cefb3f318..65c915e472 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -120,7 +120,6 @@ type _ t = | WarnGlobal: Obj.t -> Unit.t t (** Argument must be of corresponding [Spec.V.t]. *) | IterSysVars: VarQuery.t * Obj.t VarQuery.f -> Unit.t t (** [iter_vars] for [Constraints.FromSpec]. [Obj.t] represents [Spec.V.t]. *) | MayAccessed: AccessDomain.EventSet.t t - | MayBeTainted: LS.t t | MayBeTaintedA: AD.t t | MayBeModifiedSinceSetjmp: JmpBufDomain.BufferEntry.t -> VS.t t | TmpSpecial: Mval.Exp.t -> ML.t t @@ -186,7 +185,6 @@ struct | WarnGlobal _ -> (module Unit) | IterSysVars _ -> (module Unit) | MayAccessed -> (module AccessDomain.EventSet) - | MayBeTainted -> (module LS) | MayBeTaintedA -> (module AD) | MayBeModifiedSinceSetjmp _ -> (module VS) | TmpSpecial _ -> (module ML) @@ -251,7 +249,6 @@ struct | WarnGlobal _ -> Unit.top () | IterSysVars _ -> Unit.top () | MayAccessed -> AccessDomain.EventSet.top () - | MayBeTainted -> LS.top () | MayBeTaintedA -> AD.top () | MayBeModifiedSinceSetjmp _ -> VS.top () | TmpSpecial _ -> ML.top () @@ -303,8 +300,7 @@ struct | Any (InvariantGlobal _) -> 38 | Any (MustProtectedVars _) -> 39 | Any MayAccessed -> 40 - | Any MayBeTainted -> 41 - | Any MayBeTaintedA -> 4141 + | Any MayBeTaintedA -> 41 | Any (PathQuery _) -> 42 | Any DYojson -> 43 | Any (EvalValue _) -> 44 @@ -456,7 +452,6 @@ struct | Any (MutexType (v,o)) -> Pretty.dprintf "MutexType _" | Any (EvalMutexAttr a) -> Pretty.dprintf "EvalMutexAttr _" | Any MayAccessed -> Pretty.dprintf "MayAccessed" - | Any MayBeTainted -> Pretty.dprintf "MayBeTainted" | Any MayBeTaintedA -> Pretty.dprintf "MayBeTaintedA" | Any DYojson -> Pretty.dprintf "DYojson" | Any MayBeModifiedSinceSetjmp buf -> Pretty.dprintf "MayBeModifiedSinceSetjmp %a" JmpBufDomain.BufferEntry.pretty buf From 0d3af6cfeb416e039a0058670be588bcf40a74bc Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 29 Aug 2023 20:23:02 +0300 Subject: [PATCH 0544/1312] Rename MayBeTaintedA -> MayBeTainted --- src/analyses/apron/relationAnalysis.apron.ml | 2 +- src/analyses/base.ml | 2 +- src/analyses/condVars.ml | 2 +- src/analyses/modifiedSinceLongjmp.ml | 2 +- src/analyses/taintPartialContexts.ml | 2 +- src/analyses/varEq.ml | 2 +- src/domains/queries.ml | 10 +++++----- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index 64b367be68..ca60e5dc30 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -407,7 +407,7 @@ struct let arg_vars = f.sformals |> List.filter (RD.Tracked.varinfo_tracked) |> List.map RV.arg in if M.tracing then M.tracel "combine" "relation remove vars: %a\n" (docList (fun v -> Pretty.text (RD.Var.to_string v))) arg_vars; RD.remove_vars_with new_fun_rel arg_vars; (* fine to remove arg vars that also exist in caller because unify from new_rel adds them back with proper constraints *) - let tainted = f_ask.f Queries.MayBeTaintedA in + let tainted = f_ask.f Queries.MayBeTainted in let tainted_vars = TaintPartialContexts.conv_varset tainted in let new_rel = RD.keep_filter st.rel (fun var -> match RV.find_metadata var with diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 6f0f3a099a..24596f5072 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2431,7 +2431,7 @@ struct (* Remove the return value as this is dealt with separately. *) let cpa_noreturn = CPA.remove (return_varinfo ()) fun_st.cpa in let ask = (Analyses.ask_of_ctx ctx) in - let tainted = f_ask.f Q.MayBeTaintedA in + let tainted = f_ask.f Q.MayBeTainted in if M.tracing then M.trace "taintPC" "combine for %s in base: tainted: %a\n" f.svar.vname AD.pretty tainted; if M.tracing then M.trace "taintPC" "combine base:\ncaller: %a\ncallee: %a\n" CPA.pretty st.cpa CPA.pretty fun_st.cpa; if AD.is_top tainted then diff --git a/src/analyses/condVars.ml b/src/analyses/condVars.ml index 282f5bf020..04246bf28a 100644 --- a/src/analyses/condVars.ml +++ b/src/analyses/condVars.ml @@ -146,7 +146,7 @@ struct (* combine caller's state with globals from callee *) (* TODO (precision): globals with only global vars are kept, the rest is lost -> collect which globals are assigned to *) (* D.merge (fun k s1 s2 -> match s2 with Some ss2 when (fst k).vglob && D.only_global_exprs ss2 -> s2 | _ when (fst k).vglob -> None | _ -> s1) ctx.local au *) - let tainted = TaintPartialContexts.conv_varset (f_ask.f Queries.MayBeTaintedA) in + let tainted = TaintPartialContexts.conv_varset (f_ask.f Queries.MayBeTainted) in D.only_untainted ctx.local tainted (* tainted globals might have changed... *) let combine_assign ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) (f_ask: Queries.ask) : D.t = diff --git a/src/analyses/modifiedSinceLongjmp.ml b/src/analyses/modifiedSinceLongjmp.ml index 3cc10196fd..fafbe54840 100644 --- a/src/analyses/modifiedSinceLongjmp.ml +++ b/src/analyses/modifiedSinceLongjmp.ml @@ -49,7 +49,7 @@ struct [ctx.local, D.bot ()] (* enter with bot as opposed to IdentitySpec *) let combine_env ctx lval fexp f args fc au (f_ask: Queries.ask) = - let taintedcallee = relevants_from_ls (f_ask.f Queries.MayBeTaintedA) in + let taintedcallee = relevants_from_ls (f_ask.f Queries.MayBeTainted) in add_to_all_defined taintedcallee ctx.local let combine_assign ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) (f_ask:Queries.ask) : D.t = diff --git a/src/analyses/taintPartialContexts.ml b/src/analyses/taintPartialContexts.ml index 39183ef087..660766667e 100644 --- a/src/analyses/taintPartialContexts.ml +++ b/src/analyses/taintPartialContexts.ml @@ -98,7 +98,7 @@ struct let query ctx (type a) (q: a Queries.t) : a Queries.result = match q with - | MayBeTaintedA -> (ctx.local : Queries.AD.t) + | MayBeTainted -> (ctx.local : Queries.AD.t) | _ -> Queries.Result.top q end diff --git a/src/analyses/varEq.ml b/src/analyses/varEq.ml index 3bfd188aba..1159ff8b1b 100644 --- a/src/analyses/varEq.ml +++ b/src/analyses/varEq.ml @@ -433,7 +433,7 @@ struct | false -> [ctx.local,nst] let combine_env ctx lval fexp f args fc au (f_ask: Queries.ask) = - let tainted = f_ask.f Queries.MayBeTaintedA in + let tainted = f_ask.f Queries.MayBeTainted in let d_local = (* if we are multithreaded, we run the risk, that some mutex protected variables got unlocked, so in this case caller state goes to top TODO: !!Unsound, this analysis does not handle this case -> regtest 63 08!! *) diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 65c915e472..e51ee90f68 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -120,7 +120,7 @@ type _ t = | WarnGlobal: Obj.t -> Unit.t t (** Argument must be of corresponding [Spec.V.t]. *) | IterSysVars: VarQuery.t * Obj.t VarQuery.f -> Unit.t t (** [iter_vars] for [Constraints.FromSpec]. [Obj.t] represents [Spec.V.t]. *) | MayAccessed: AccessDomain.EventSet.t t - | MayBeTaintedA: AD.t t + | MayBeTainted: AD.t t | MayBeModifiedSinceSetjmp: JmpBufDomain.BufferEntry.t -> VS.t t | TmpSpecial: Mval.Exp.t -> ML.t t @@ -185,7 +185,7 @@ struct | WarnGlobal _ -> (module Unit) | IterSysVars _ -> (module Unit) | MayAccessed -> (module AccessDomain.EventSet) - | MayBeTaintedA -> (module AD) + | MayBeTainted -> (module AD) | MayBeModifiedSinceSetjmp _ -> (module VS) | TmpSpecial _ -> (module ML) @@ -249,7 +249,7 @@ struct | WarnGlobal _ -> Unit.top () | IterSysVars _ -> Unit.top () | MayAccessed -> AccessDomain.EventSet.top () - | MayBeTaintedA -> AD.top () + | MayBeTainted -> AD.top () | MayBeModifiedSinceSetjmp _ -> VS.top () | TmpSpecial _ -> ML.top () end @@ -300,7 +300,7 @@ struct | Any (InvariantGlobal _) -> 38 | Any (MustProtectedVars _) -> 39 | Any MayAccessed -> 40 - | Any MayBeTaintedA -> 41 + | Any MayBeTainted -> 41 | Any (PathQuery _) -> 42 | Any DYojson -> 43 | Any (EvalValue _) -> 44 @@ -452,7 +452,7 @@ struct | Any (MutexType (v,o)) -> Pretty.dprintf "MutexType _" | Any (EvalMutexAttr a) -> Pretty.dprintf "EvalMutexAttr _" | Any MayAccessed -> Pretty.dprintf "MayAccessed" - | Any MayBeTaintedA -> Pretty.dprintf "MayBeTaintedA" + | Any MayBeTainted -> Pretty.dprintf "MayBeTainted" | Any DYojson -> Pretty.dprintf "DYojson" | Any MayBeModifiedSinceSetjmp buf -> Pretty.dprintf "MayBeModifiedSinceSetjmp %a" JmpBufDomain.BufferEntry.pretty buf | Any (TmpSpecial lv) -> Pretty.dprintf "TmpSpecial %a" Mval.Exp.pretty lv From e1f8319a7e72aab0e4ec57a11a3998105448fc4d Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 30 Aug 2023 17:54:10 +0200 Subject: [PATCH 0545/1312] Set sv-comp flag for upjumping gotos --- src/framework/control.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/framework/control.ml b/src/framework/control.ml index 8185dc6053..32e0a8c038 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -145,6 +145,7 @@ struct (fun fi _ -> let fundec_live = live fi fname in if ( not (BatISet.is_empty fundec_live)) then ( + AnalysisState.svcomp_may_not_terminate := true; let msgs = [(Pretty.dprintf "The program might not terminate! (Upjumping Goto)", From aceffa897f7294261db65e2531661c64239a0376 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 31 Aug 2023 16:48:35 +0200 Subject: [PATCH 0546/1312] Allow Queries.BlobSize to be asked for the size from the start address The idea is to ignore address offsets and thus getting bot from Queries.BlobSize --- src/analyses/base.ml | 14 ++++++++++++-- src/domains/queries.ml | 10 ++++++---- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 824fde18d9..b50fed9c50 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1256,16 +1256,26 @@ struct end | Q.EvalValue e -> eval_rv (Analyses.ask_of_ctx ctx) ctx.global ctx.local e - | Q.BlobSize e -> begin + | Q.BlobSize (e, from_base_addr) -> begin let p = eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local e in (* ignore @@ printf "BlobSize %a MayPointTo %a\n" d_plainexp e VD.pretty p; *) match p with | Address a -> let s = addrToLvalSet a in (* If there's a non-heap var or an offset in the lval set, we answer with bottom *) - if ValueDomainQueries.LS.exists (fun (v, o) -> (not @@ ctx.ask (Queries.IsHeapVar v)) || o <> `NoOffset) s then + (* If we're asking for the BlobSize from the base address, then don't check for offsets => we want to avoid getting bot *) + if ValueDomainQueries.LS.exists (fun (v, o) -> (not @@ ctx.ask (Queries.IsHeapVar v)) || (if not from_base_addr then o <> `NoOffset else false)) s then Queries.Result.bot q else ( + (* If we need the BlobSize from the base address, then remove any offsets *) + let a = + if from_base_addr then + ValueDomainQueries.LS.elements s + |> List.map (fun (v, _) -> Addr.of_var v) + |> AD.of_list + else + a + in let r = get ~full:true (Analyses.ask_of_ctx ctx) ctx.global ctx.local a None in (* ignore @@ printf "BlobSize %a = %a\n" d_plainexp e VD.pretty r; *) (match r with diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 214fcf1384..1735a047ea 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -91,7 +91,9 @@ type _ t = | EvalStr: exp -> SD.t t | EvalLength: exp -> ID.t t (* length of an array or string *) | EvalValue: exp -> VD.t t - | BlobSize: exp -> ID.t t (* size of a dynamically allocated `Blob pointed to by exp *) + | BlobSize: exp * bool -> ID.t t + (* Size of a dynamically allocated `Blob pointed to by exp. *) + (* If the second component is set to true, then address offsets are discarded and the size of the `Blob is asked for the base address. *) | CondVars: exp -> ES.t t | PartAccess: access -> Obj.t t (** Only queried by access and deadlock analysis. [Obj.t] represents [MCPAccess.A.t], needed to break dependency cycle. *) | IterPrevVars: iterprevvar -> Unit.t t @@ -334,7 +336,7 @@ struct | Any (EvalLength e1), Any (EvalLength e2) -> CilType.Exp.compare e1 e2 | Any (EvalMutexAttr e1), Any (EvalMutexAttr e2) -> CilType.Exp.compare e1 e2 | Any (EvalValue e1), Any (EvalValue e2) -> CilType.Exp.compare e1 e2 - | Any (BlobSize e1), Any (BlobSize e2) -> CilType.Exp.compare e1 e2 + | Any (BlobSize (e1, _)), Any (BlobSize (e2, _)) -> CilType.Exp.compare e1 e2 | Any (CondVars e1), Any (CondVars e2) -> CilType.Exp.compare e1 e2 | Any (PartAccess p1), Any (PartAccess p2) -> compare_access p1 p2 | Any (IterPrevVars ip1), Any (IterPrevVars ip2) -> compare_iterprevvar ip1 ip2 @@ -379,7 +381,7 @@ struct | Any (EvalLength e) -> CilType.Exp.hash e | Any (EvalMutexAttr e) -> CilType.Exp.hash e | Any (EvalValue e) -> CilType.Exp.hash e - | Any (BlobSize e) -> CilType.Exp.hash e + | Any (BlobSize (e, from_base_addr)) -> CilType.Exp.hash e + Hashtbl.hash from_base_addr | Any (CondVars e) -> CilType.Exp.hash e | Any (PartAccess p) -> hash_access p | Any (IterPrevVars i) -> 0 @@ -427,7 +429,7 @@ struct | Any (EvalStr e) -> Pretty.dprintf "EvalStr %a" CilType.Exp.pretty e | Any (EvalLength e) -> Pretty.dprintf "EvalLength %a" CilType.Exp.pretty e | Any (EvalValue e) -> Pretty.dprintf "EvalValue %a" CilType.Exp.pretty e - | Any (BlobSize e) -> Pretty.dprintf "BlobSize %a" CilType.Exp.pretty e + | Any (BlobSize (e, from_base_addr)) -> Pretty.dprintf "BlobSize %a (from base address: %b)" CilType.Exp.pretty e from_base_addr | Any (CondVars e) -> Pretty.dprintf "CondVars %a" CilType.Exp.pretty e | Any (PartAccess p) -> Pretty.dprintf "PartAccess _" | Any (IterPrevVars i) -> Pretty.dprintf "IterPrevVars _" From 7fb671947da29c5f1d6a67b9a1b91d5e8fd3db05 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 31 Aug 2023 16:49:32 +0200 Subject: [PATCH 0547/1312] Use Queries.BlobSize for getting blob sizes without address offsets --- src/analyses/memOutOfBounds.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 2b4ca3d4c4..e81b097054 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -100,7 +100,8 @@ struct * and thus we always get a warning for an OOB memory access. Should we maybe change Queries.BlobSize again? *) if points_to_heap_only ctx ptr then - ctx.ask (Queries.BlobSize ptr) + (* Ask for BlobSize from the base address (the second component being set to true) in order to avoid BlobSize giving us bot *) + ctx.ask (Queries.BlobSize (ptr, true)) else match ctx.ask (Queries.MayPointTo ptr) with | a when not (Queries.LS.is_top a) -> From 0e126df6b638d755af1e36ba4b50bc1b67935eb9 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 31 Aug 2023 16:51:29 +0200 Subject: [PATCH 0548/1312] Clean up unnecessary comments --- src/analyses/memOutOfBounds.ml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index e81b097054..acf6eb7660 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -90,15 +90,6 @@ struct | _ -> false let get_size_of_ptr_target ctx ptr = - (* Call Queries.BlobSize only if ptr points solely to the heap. Otherwise we get bot *) - (* TODO: - * If the ptr's address has been offset, then Queries.BlobSize will answer with bot. For example: - char *ptr = malloc(10 * sizeof(char)); - ptr++; - printf("%s", *ptr); // => Issues a WARN even though it shouldn't - * However, in this case we're too imprecise, since we're always comparing something with bot - * and thus we always get a warning for an OOB memory access. Should we maybe change Queries.BlobSize again? - *) if points_to_heap_only ctx ptr then (* Ask for BlobSize from the base address (the second component being set to true) in order to avoid BlobSize giving us bot *) ctx.ask (Queries.BlobSize (ptr, true)) From 293d3e7d02bd4a8a52db31611bb4f0ebe4e1ffc5 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 31 Aug 2023 17:22:20 +0200 Subject: [PATCH 0549/1312] Add check for implicit pointer dereferences in special functions Also remove a resolved TODO comment --- src/analyses/memOutOfBounds.ml | 44 +++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index acf6eb7660..0e93adb295 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -174,7 +174,6 @@ struct (* Offset should be the same for all elements in the points-to set. Hence, we can just pick one element and obtain its offset - TODO: Does this make sense? *) let (_, o) = VDQ.LS.choose a in let rec to_int_dom_offs = function @@ -198,19 +197,22 @@ struct M.warn "Pointer %a has a points-to-set of top. An invalid memory access might occur" d_exp ptr; IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind () - and check_lval_for_oob_access ctx lval = + and check_lval_for_oob_access ctx ?(is_implicitly_derefed = false) lval = if not @@ lval_contains_a_ptr lval then () else - match lval with - | Var _, _ -> () - | Mem e, _ -> + (* If the lval doesn't indicate an explicit dereference, we still need to check for an implicit dereference *) + (* An implicit dereference is, e.g., printf("%p", ptr), where ptr is a pointer *) + match lval, is_implicitly_derefed with + | (Var _, _), false -> () + | (Var v, _), true -> check_no_binop_deref ctx (Lval lval) + | (Mem e, _), _ -> begin match e with | Lval (Var v, _) as lval_exp -> check_no_binop_deref ctx lval_exp | BinOp (binop, e1, e2, t) when binop = PlusPI || binop = MinusPI || binop = IndexPI -> check_binop_exp ctx binop e1 e2 t; - check_exp_for_oob_access ctx e1; - check_exp_for_oob_access ctx e2 - | _ -> check_exp_for_oob_access ctx e + check_exp_for_oob_access ctx ~is_implicitly_derefed e1; + check_exp_for_oob_access ctx ~is_implicitly_derefed e2 + | _ -> check_exp_for_oob_access ctx ~is_implicitly_derefed e end and check_no_binop_deref ctx lval_exp = @@ -235,7 +237,7 @@ struct end | _ -> M.error "Expression %a is not a pointer" d_exp lval_exp - and check_exp_for_oob_access ctx exp = + and check_exp_for_oob_access ctx ?(is_implicitly_derefed = false) exp = match exp with | Const _ | SizeOf _ @@ -247,17 +249,17 @@ struct | SizeOfE e | AlignOfE e | UnOp (_, e, _) - | CastE (_, e) -> check_exp_for_oob_access ctx e + | CastE (_, e) -> check_exp_for_oob_access ctx ~is_implicitly_derefed e | BinOp (bop, e1, e2, t) -> - check_exp_for_oob_access ctx e1; - check_exp_for_oob_access ctx e2 + check_exp_for_oob_access ctx ~is_implicitly_derefed e1; + check_exp_for_oob_access ctx ~is_implicitly_derefed e2 | Question (e1, e2, e3, _) -> - check_exp_for_oob_access ctx e1; - check_exp_for_oob_access ctx e2; - check_exp_for_oob_access ctx e3 + check_exp_for_oob_access ctx ~is_implicitly_derefed e1; + check_exp_for_oob_access ctx ~is_implicitly_derefed e2; + check_exp_for_oob_access ctx ~is_implicitly_derefed e3 | Lval lval | StartOf lval - | AddrOf lval -> check_lval_for_oob_access ctx lval + | AddrOf lval -> check_lval_for_oob_access ctx ~is_implicitly_derefed lval and check_binop_exp ctx binop e1 e2 t = let binopexp = BinOp (binop, e1, e2, t) in @@ -314,8 +316,16 @@ struct ctx.local let special ctx (lval:lval option) (f:varinfo) (arglist:exp list) : D.t = + let desc = LibraryFunctions.find f in + let is_arg_implicitly_derefed arg = + let read_shallow_args = LibraryDesc.Accesses.find desc.accs { kind = Read; deep = false } arglist in + let read_deep_args = LibraryDesc.Accesses.find desc.accs { kind = Read; deep = true } arglist in + let write_shallow_args = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = false } arglist in + let write_deep_args = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = true } arglist in + List.mem arg read_shallow_args || List.mem arg read_deep_args || List.mem arg write_shallow_args || List.mem arg write_deep_args + in Option.iter (fun x -> check_lval_for_oob_access ctx x) lval; - List.iter (fun arg -> check_exp_for_oob_access ctx arg) arglist; + List.iter (fun arg -> check_exp_for_oob_access ctx ~is_implicitly_derefed:(is_arg_implicitly_derefed arg) arg) arglist; ctx.local let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = From e4b349a61545e00c53f2c4267a496f959b2e83fa Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 31 Aug 2023 17:23:03 +0200 Subject: [PATCH 0550/1312] Add a test case with implicit dereferences --- .../77-mem-oob/05-oob-implicit-deref.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 tests/regression/77-mem-oob/05-oob-implicit-deref.c diff --git a/tests/regression/77-mem-oob/05-oob-implicit-deref.c b/tests/regression/77-mem-oob/05-oob-implicit-deref.c new file mode 100644 index 0000000000..088493d3b9 --- /dev/null +++ b/tests/regression/77-mem-oob/05-oob-implicit-deref.c @@ -0,0 +1,19 @@ +// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval +#include +#include +#include + +int main(int argc, char const *argv[]) { + int *ptr = malloc(4 * sizeof(int)); + + // Both lines below are considered derefs => no need to warn, since ptr is pointing within its bounds + memset(ptr, 0, 4 * sizeof(int)); //NOWARN + printf("%p", (void *) ptr); //NOWARN + ptr = ptr + 10; // ptr no longer points within its allocated bounds + + // Each of both lines below should now receive a WARN + memset(ptr, 0, 4 * sizeof(int)); //WARN + printf("%p", (void *) ptr); //WARN + + return 0; +} From b55afa6a6fdafab6f9a9b3c4502d3d2e2d15e9d9 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 1 Sep 2023 13:05:30 +0300 Subject: [PATCH 0551/1312] Fix semgrep 1.38 compatibility --- .github/workflows/semgrep.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/semgrep.yml b/.github/workflows/semgrep.yml index acd696e597..b46713d510 100644 --- a/.github/workflows/semgrep.yml +++ b/.github/workflows/semgrep.yml @@ -19,7 +19,7 @@ jobs: uses: actions/checkout@v3 - name: Run semgrep - run: semgrep scan --sarif --output=semgrep.sarif + run: semgrep scan --config .semgrep/ --sarif > semgrep.sarif - name: Upload SARIF file to GitHub Advanced Security Dashboard uses: github/codeql-action/upload-sarif@v2 From b832f31cb647ac0f632bbc335a89817564b140dd Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Fri, 1 Sep 2023 15:42:17 +0200 Subject: [PATCH 0552/1312] Increase indentation in switch-case in update_suite.rb. --- scripts/update_suite.rb | 42 ++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/scripts/update_suite.rb b/scripts/update_suite.rb index 313b6b75f9..e91b1d116a 100755 --- a/scripts/update_suite.rb +++ b/scripts/update_suite.rb @@ -151,27 +151,27 @@ def collect_warnings ranking = ["other", "warn", "goto", "fundec", "loop", "term", "nonterm", "race", "norace", "deadlock", "nodeadlock", "success", "fail", "unknown"] thiswarn = case obj - when /\(conf\. \d+\)/ then "race" - when /Deadlock/ then "deadlock" - when /lock (before|after):/ then "deadlock" - when /Assertion .* will fail/ then "fail" - when /Assertion .* will succeed/ then "success" - when /Assertion .* is unknown/ then "unknown" - when /invariant confirmed/ then "success" - when /invariant unconfirmed/ then "unknown" - when /invariant refuted/ then "fail" - when /^\[Warning\]/ then "warn" - when /^\[Error\]/ then "warn" - when /^\[Info\]/ then "warn" - when /^\[Success\]/ then "success" - when /(Upjumping Goto)/ then "goto" - when /(Fundec \w+ is contained in a call graph cycle)/ then "fundec" - when /(Loop analysis)/ then "loop" - when /\[Debug\]/ then next # debug "warnings" shouldn't count as other warnings (against NOWARN) - when /^ on line \d+ $/ then next # dead line warnings shouldn't count (used for unreachability with NOWARN) - when /^ on lines \d+..\d+ $/ then next # dead line warnings shouldn't count (used for unreachability with NOWARN) - else "other" - end + when /\(conf\. \d+\)/ then "race" + when /Deadlock/ then "deadlock" + when /lock (before|after):/ then "deadlock" + when /Assertion .* will fail/ then "fail" + when /Assertion .* will succeed/ then "success" + when /Assertion .* is unknown/ then "unknown" + when /invariant confirmed/ then "success" + when /invariant unconfirmed/ then "unknown" + when /invariant refuted/ then "fail" + when /^\[Warning\]/ then "warn" + when /^\[Error\]/ then "warn" + when /^\[Info\]/ then "warn" + when /^\[Success\]/ then "success" + when /(Upjumping Goto)/ then "goto" + when /(Fundec \w+ is contained in a call graph cycle)/ then "fundec" + when /(Loop analysis)/ then "loop" + when /\[Debug\]/ then next # debug "warnings" shouldn't count as other warnings (against NOWARN) + when /^ on line \d+ $/ then next # dead line warnings shouldn't count (used for unreachability with NOWARN) + when /^ on lines \d+..\d+ $/ then next # dead line warnings shouldn't count (used for unreachability with NOWARN) + else "other" + end oldwarn = warnings[i] if oldwarn.nil? then warnings[i] = thiswarn From a6e4af4083db6751e35f996d374b6fa5fa229016 Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Fri, 1 Sep 2023 15:56:15 +0200 Subject: [PATCH 0553/1312] Rename must_be_single_threaded_since_start to must_always_be_single_threaded --- src/analyses/loopTermination.ml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/analyses/loopTermination.ml b/src/analyses/loopTermination.ml index 563d6574bb..bfc600f830 100644 --- a/src/analyses/loopTermination.ml +++ b/src/analyses/loopTermination.ml @@ -78,9 +78,9 @@ struct | _ -> () else () - (** Checks whether a new thread was spawned some time. We want to always - * assume non-termination then (see query function). *) - let must_be_single_threaded_since_start ctx = + (* Checks whether the program always remains single-threaded. + If the program does not remain single-threaded, we assume non-termination (see query function). *) + let must_always_be_single_threaded ctx = let single_threaded = not (ctx.ask Queries.IsEverMultiThreaded) in single_thread := single_threaded; single_threaded @@ -88,12 +88,12 @@ struct let query ctx (type a) (q: a Queries.t): a Queries.result = match q with | Queries.MustTermLoop loop_statement -> - must_be_single_threaded_since_start ctx + must_always_be_single_threaded ctx && (match G.find_opt (`Lifted loop_statement) (ctx.global ()) with Some b -> b | None -> false) | Queries.MustTermAllLoops -> - let always_single_threaded = must_be_single_threaded_since_start ctx in + let always_single_threaded = must_always_be_single_threaded ctx in (* Must be the first to be evaluated! This has the side effect that * single_thread is set. In case of another order and due to lazy * evaluation, the correct value of single_thread can not be guaranteed! From fc67853d42ce48745443adc9768e3d4fe14f76d5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 20:41:25 +0000 Subject: [PATCH 0554/1312] Bump actions/checkout from 3 to 4 Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/coverage.yml | 2 +- .github/workflows/docker.yml | 2 +- .github/workflows/docs.yml | 2 +- .github/workflows/indentation.yml | 2 +- .github/workflows/locked.yml | 6 +++--- .github/workflows/metadata.yml | 4 ++-- .github/workflows/options.yml | 2 +- .github/workflows/semgrep.yml | 2 +- .github/workflows/unlocked.yml | 8 ++++---- 9 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 7472cbc820..5635ebbeea 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -28,7 +28,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up OCaml ${{ matrix.ocaml-compiler }} env: diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 02c5f07d90..1ef104db29 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -35,7 +35,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 # needed for GitHub Actions Cache in build-push-action diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index aa69baf958..e1648904c3 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -30,7 +30,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Check for undocumented modules run: python scripts/goblint-lib-modules.py diff --git a/.github/workflows/indentation.yml b/.github/workflows/indentation.yml index 14db288d60..e22e674301 100644 --- a/.github/workflows/indentation.yml +++ b/.github/workflows/indentation.yml @@ -20,7 +20,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 diff --git a/.github/workflows/locked.yml b/.github/workflows/locked.yml index 358682a2f3..007ea34619 100644 --- a/.github/workflows/locked.yml +++ b/.github/workflows/locked.yml @@ -30,7 +30,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up OCaml ${{ matrix.ocaml-compiler }} env: @@ -101,7 +101,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up OCaml ${{ matrix.ocaml-compiler }} env: @@ -141,7 +141,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up OCaml ${{ matrix.ocaml-compiler }} env: diff --git a/.github/workflows/metadata.yml b/.github/workflows/metadata.yml index da20c6b675..1092606bc6 100644 --- a/.github/workflows/metadata.yml +++ b/.github/workflows/metadata.yml @@ -19,7 +19,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Validate CITATION.cff uses: docker://citationcff/cffconvert:latest @@ -36,7 +36,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 diff --git a/.github/workflows/options.yml b/.github/workflows/options.yml index b8522c03bb..b5f690a700 100644 --- a/.github/workflows/options.yml +++ b/.github/workflows/options.yml @@ -15,7 +15,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 diff --git a/.github/workflows/semgrep.yml b/.github/workflows/semgrep.yml index b46713d510..bd2dfd285c 100644 --- a/.github/workflows/semgrep.yml +++ b/.github/workflows/semgrep.yml @@ -16,7 +16,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Run semgrep run: semgrep scan --config .semgrep/ --sarif > semgrep.sarif diff --git a/.github/workflows/unlocked.yml b/.github/workflows/unlocked.yml index 2bec6b72fb..15cd9138d8 100644 --- a/.github/workflows/unlocked.yml +++ b/.github/workflows/unlocked.yml @@ -45,7 +45,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up OCaml ${{ matrix.ocaml-compiler }} uses: ocaml/setup-ocaml@v2 @@ -131,7 +131,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up OCaml ${{ matrix.ocaml-compiler }} uses: ocaml/setup-ocaml@v2 @@ -208,7 +208,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 # needed for GitHub Actions Cache in build-push-action @@ -246,7 +246,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up OCaml ${{ matrix.ocaml-compiler }} uses: ocaml/setup-ocaml@v2 From dac7983d06e05f4180c7d53513d5b03cfe2d01b3 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 6 Sep 2023 10:05:31 +0200 Subject: [PATCH 0555/1312] Fix numbering --- .../{74-use_after_free => 78-use_after_free}/01-simple-uaf.c | 0 .../{74-use_after_free => 78-use_after_free}/02-conditional-uaf.c | 0 .../{74-use_after_free => 78-use_after_free}/03-nested-ptr-uaf.c | 0 .../04-function-call-uaf.c | 0 .../05-uaf-free-in-wrapper-fun.c | 0 .../{74-use_after_free => 78-use_after_free}/06-uaf-struct.c | 0 .../{74-use_after_free => 78-use_after_free}/07-itc-double-free.c | 0 .../08-itc-no-double-free.c | 0 .../{74-use_after_free => 78-use_after_free}/09-juliet-uaf.c | 0 .../10-juliet-double-free.c | 0 .../11-wrapper-funs-uaf.c | 0 .../12-multi-threaded-uaf.c | 0 .../13-multi-threaded-uaf-with-joined-thread.c | 0 13 files changed, 0 insertions(+), 0 deletions(-) rename tests/regression/{74-use_after_free => 78-use_after_free}/01-simple-uaf.c (100%) rename tests/regression/{74-use_after_free => 78-use_after_free}/02-conditional-uaf.c (100%) rename tests/regression/{74-use_after_free => 78-use_after_free}/03-nested-ptr-uaf.c (100%) rename tests/regression/{74-use_after_free => 78-use_after_free}/04-function-call-uaf.c (100%) rename tests/regression/{74-use_after_free => 78-use_after_free}/05-uaf-free-in-wrapper-fun.c (100%) rename tests/regression/{74-use_after_free => 78-use_after_free}/06-uaf-struct.c (100%) rename tests/regression/{74-use_after_free => 78-use_after_free}/07-itc-double-free.c (100%) rename tests/regression/{74-use_after_free => 78-use_after_free}/08-itc-no-double-free.c (100%) rename tests/regression/{74-use_after_free => 78-use_after_free}/09-juliet-uaf.c (100%) rename tests/regression/{74-use_after_free => 78-use_after_free}/10-juliet-double-free.c (100%) rename tests/regression/{74-use_after_free => 78-use_after_free}/11-wrapper-funs-uaf.c (100%) rename tests/regression/{74-use_after_free => 78-use_after_free}/12-multi-threaded-uaf.c (100%) rename tests/regression/{74-use_after_free => 78-use_after_free}/13-multi-threaded-uaf-with-joined-thread.c (100%) diff --git a/tests/regression/74-use_after_free/01-simple-uaf.c b/tests/regression/78-use_after_free/01-simple-uaf.c similarity index 100% rename from tests/regression/74-use_after_free/01-simple-uaf.c rename to tests/regression/78-use_after_free/01-simple-uaf.c diff --git a/tests/regression/74-use_after_free/02-conditional-uaf.c b/tests/regression/78-use_after_free/02-conditional-uaf.c similarity index 100% rename from tests/regression/74-use_after_free/02-conditional-uaf.c rename to tests/regression/78-use_after_free/02-conditional-uaf.c diff --git a/tests/regression/74-use_after_free/03-nested-ptr-uaf.c b/tests/regression/78-use_after_free/03-nested-ptr-uaf.c similarity index 100% rename from tests/regression/74-use_after_free/03-nested-ptr-uaf.c rename to tests/regression/78-use_after_free/03-nested-ptr-uaf.c diff --git a/tests/regression/74-use_after_free/04-function-call-uaf.c b/tests/regression/78-use_after_free/04-function-call-uaf.c similarity index 100% rename from tests/regression/74-use_after_free/04-function-call-uaf.c rename to tests/regression/78-use_after_free/04-function-call-uaf.c diff --git a/tests/regression/74-use_after_free/05-uaf-free-in-wrapper-fun.c b/tests/regression/78-use_after_free/05-uaf-free-in-wrapper-fun.c similarity index 100% rename from tests/regression/74-use_after_free/05-uaf-free-in-wrapper-fun.c rename to tests/regression/78-use_after_free/05-uaf-free-in-wrapper-fun.c diff --git a/tests/regression/74-use_after_free/06-uaf-struct.c b/tests/regression/78-use_after_free/06-uaf-struct.c similarity index 100% rename from tests/regression/74-use_after_free/06-uaf-struct.c rename to tests/regression/78-use_after_free/06-uaf-struct.c diff --git a/tests/regression/74-use_after_free/07-itc-double-free.c b/tests/regression/78-use_after_free/07-itc-double-free.c similarity index 100% rename from tests/regression/74-use_after_free/07-itc-double-free.c rename to tests/regression/78-use_after_free/07-itc-double-free.c diff --git a/tests/regression/74-use_after_free/08-itc-no-double-free.c b/tests/regression/78-use_after_free/08-itc-no-double-free.c similarity index 100% rename from tests/regression/74-use_after_free/08-itc-no-double-free.c rename to tests/regression/78-use_after_free/08-itc-no-double-free.c diff --git a/tests/regression/74-use_after_free/09-juliet-uaf.c b/tests/regression/78-use_after_free/09-juliet-uaf.c similarity index 100% rename from tests/regression/74-use_after_free/09-juliet-uaf.c rename to tests/regression/78-use_after_free/09-juliet-uaf.c diff --git a/tests/regression/74-use_after_free/10-juliet-double-free.c b/tests/regression/78-use_after_free/10-juliet-double-free.c similarity index 100% rename from tests/regression/74-use_after_free/10-juliet-double-free.c rename to tests/regression/78-use_after_free/10-juliet-double-free.c diff --git a/tests/regression/74-use_after_free/11-wrapper-funs-uaf.c b/tests/regression/78-use_after_free/11-wrapper-funs-uaf.c similarity index 100% rename from tests/regression/74-use_after_free/11-wrapper-funs-uaf.c rename to tests/regression/78-use_after_free/11-wrapper-funs-uaf.c diff --git a/tests/regression/74-use_after_free/12-multi-threaded-uaf.c b/tests/regression/78-use_after_free/12-multi-threaded-uaf.c similarity index 100% rename from tests/regression/74-use_after_free/12-multi-threaded-uaf.c rename to tests/regression/78-use_after_free/12-multi-threaded-uaf.c diff --git a/tests/regression/74-use_after_free/13-multi-threaded-uaf-with-joined-thread.c b/tests/regression/78-use_after_free/13-multi-threaded-uaf-with-joined-thread.c similarity index 100% rename from tests/regression/74-use_after_free/13-multi-threaded-uaf-with-joined-thread.c rename to tests/regression/78-use_after_free/13-multi-threaded-uaf-with-joined-thread.c From b3d9fe1ad02a2fd84455af118a3797922ba976d5 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 6 Sep 2023 11:07:26 +0200 Subject: [PATCH 0556/1312] Set the global SV-COMP analysis state vars at all necessary places --- src/analyses/base.ml | 28 ++++++++++++++++++++-------- src/analyses/memLeak.ml | 12 +++++++++--- src/analyses/memOutOfBounds.ml | 5 ++++- 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index f237ca8296..79ac13b861 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1050,10 +1050,16 @@ struct | Mem n, ofs -> begin match (eval_rv a gs st n) with | Address adr -> - (if AD.is_null adr - then M.error ~category:M.Category.Behavior.Undefined.nullpointer_dereference ~tags:[CWE 476] "Must dereference NULL pointer" - else if AD.may_be_null adr - then M.warn ~category:M.Category.Behavior.Undefined.nullpointer_dereference ~tags:[CWE 476] "May dereference NULL pointer"); + ( + if AD.is_null adr then ( + AnalysisState.svcomp_may_invalid_deref := true; + M.error ~category:M.Category.Behavior.Undefined.nullpointer_dereference ~tags:[CWE 476] "Must dereference NULL pointer" + ) + else if AD.may_be_null adr then ( + AnalysisState.svcomp_may_invalid_deref := true; + M.warn ~category:M.Category.Behavior.Undefined.nullpointer_dereference ~tags:[CWE 476] "May dereference NULL pointer" + ) + ); AD.map (add_offset_varinfo (convert_offset a gs st ofs)) adr | _ -> M.debug ~category:Analyzer "Failed evaluating %a to lvalue" d_lval lval; @@ -2023,12 +2029,18 @@ struct match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local ptr with | Address a -> let points_to_set = addrToLvalSet a in - if Q.LS.is_top points_to_set then - M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Points-to set for pointer %a in function %s is top. Potentially invalid memory deallocation may occur" d_exp ptr special_fn.vname - else if (Q.LS.exists (fun (v, _) -> not (ctx.ask (Q.IsHeapVar v))) points_to_set) || (AD.mem Addr.UnknownPtr a) then + if Q.LS.is_top points_to_set then ( + AnalysisState.svcomp_may_invalid_free := true; + M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590; CWE 761] "Points-to set for pointer %a in function %s is top. Potentially invalid memory deallocation may occur" d_exp ptr special_fn.vname + ) + else if (Q.LS.exists (fun (v, _) -> not (ctx.ask (Q.IsHeapVar v))) points_to_set) || (AD.mem Addr.UnknownPtr a) then ( + AnalysisState.svcomp_may_invalid_free := true; M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Free of non-dynamically allocated memory in function %s for pointer %a" special_fn.vname d_exp ptr - else if Q.LS.exists (fun (_, o) -> Offset.Exp.cmp_zero_offset o <> `MustZero) points_to_set then + ) + else if Q.LS.exists (fun (_, o) -> Offset.Exp.cmp_zero_offset o <> `MustZero) points_to_set then ( + AnalysisState.svcomp_may_invalid_free := true; M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 761] "Free of memory not at start of buffer in function %s for pointer %a" special_fn.vname d_exp ptr + ) | _ -> M.warn ~category:MessageCategory.Analyzer "Pointer %a in function %s doesn't evaluate to a valid address." d_exp ptr special_fn.vname let special ctx (lv:lval option) (f: varinfo) (args: exp list) = diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index 99df5695a7..688ce1024a 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -19,15 +19,21 @@ struct (* HELPER FUNCTIONS *) let warn_for_multi_threaded ctx = - if not (ctx.ask (Queries.MustBeSingleThreaded { since_start = true })) then + if not (ctx.ask (Queries.MustBeSingleThreaded { since_start = true })) then ( + AnalysisState.svcomp_may_invalid_memtrack := true; M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Program isn't running in single-threaded mode. A memory leak might occur due to multi-threading" + ) let check_for_mem_leak ?(assert_exp_imprecise = false) ?(exp = None) ctx = let state = ctx.local in if not @@ D.is_empty state then match assert_exp_imprecise, exp with - | true, Some exp -> M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "assert expression %a is unknown. Memory leak might possibly occur for heap variables: %a" d_exp exp D.pretty state - | _ -> M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory leak detected for heap variables: %a" D.pretty state + | true, Some exp -> + AnalysisState.svcomp_may_invalid_memtrack := true; + M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "assert expression %a is unknown. Memory leak might possibly occur for heap variables: %a" d_exp exp D.pretty state + | _ -> + AnalysisState.svcomp_may_invalid_memtrack := true; + M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory leak detected for heap variables: %a" D.pretty state (* TRANSFER FUNCTIONS *) let return ctx (exp:exp option) (f:fundec) : D.t = diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 0e93adb295..a7e95282fd 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -194,7 +194,10 @@ struct IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind () end | _ -> - M.warn "Pointer %a has a points-to-set of top. An invalid memory access might occur" d_exp ptr; + ( + AnalysisState.svcomp_may_invalid_deref :=true; + M.warn "Pointer %a has a points-to-set of top. An invalid memory access might occur" d_exp ptr + ); IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind () and check_lval_for_oob_access ctx ?(is_implicitly_derefed = false) lval = From 2b4549905c80bbca10d82e04a7d2f21b9978143e Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 6 Sep 2023 12:01:45 +0200 Subject: [PATCH 0557/1312] Disable Info warnings for test case 77/05 for now Also add a comment explaining why it's a temporary solution --- tests/regression/77-mem-oob/05-oob-implicit-deref.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/regression/77-mem-oob/05-oob-implicit-deref.c b/tests/regression/77-mem-oob/05-oob-implicit-deref.c index 088493d3b9..8bec6a72e0 100644 --- a/tests/regression/77-mem-oob/05-oob-implicit-deref.c +++ b/tests/regression/77-mem-oob/05-oob-implicit-deref.c @@ -1,4 +1,8 @@ -// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval +// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval --disable warn.info +/* + Note: the "--disable warn.info" above is a temporary workaround, + since the GitHub CI seems to be considering Info messages as violations of NOWARN (cf. https://github.com/goblint/analyzer/issues/1151) +*/ #include #include #include From 924130132a766149cc97745629fd52e08ec6b798 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 6 Sep 2023 12:02:35 +0200 Subject: [PATCH 0558/1312] Remove TODO comment and add a few other explaining comments --- src/analyses/memOutOfBounds.ml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 0e93adb295..1738970739 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -1,3 +1,5 @@ +(** An analysis for the detection of out-of-bounds memory accesses ([memOutOfBounds]).*) + open GoblintCil open Analyses open MessageCategory @@ -5,6 +7,12 @@ open MessageCategory module AS = AnalysisState module VDQ = ValueDomainQueries +(* + Note: + * This functionality is implemented as an analysis solely for the sake of maintaining + separation of concerns, as well as for having the ablility to conveniently turn it on or off + * It doesn't track any internal state +*) module Spec = struct include Analyses.IdentitySpec @@ -12,7 +20,6 @@ struct module D = Lattice.Unit module C = D - (* TODO: Check out later for benchmarking *) let context _ _ = () let name () = "memOutOfBounds" From 0bae455057d108b3d2e5f368622384ccdf9f8d44 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 6 Sep 2023 12:07:16 +0200 Subject: [PATCH 0559/1312] Use a record instead of a tuple for Queries.BlobSize --- src/analyses/base.ml | 2 +- src/analyses/memOutOfBounds.ml | 2 +- src/domains/queries.ml | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index f237ca8296..28a5b236a3 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1257,7 +1257,7 @@ struct end | Q.EvalValue e -> eval_rv (Analyses.ask_of_ctx ctx) ctx.global ctx.local e - | Q.BlobSize (e, from_base_addr) -> begin + | Q.BlobSize {exp = e; base_address = from_base_addr} -> begin let p = eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local e in (* ignore @@ printf "BlobSize %a MayPointTo %a\n" d_plainexp e VD.pretty p; *) match p with diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 1738970739..97907e9d6f 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -99,7 +99,7 @@ struct let get_size_of_ptr_target ctx ptr = if points_to_heap_only ctx ptr then (* Ask for BlobSize from the base address (the second component being set to true) in order to avoid BlobSize giving us bot *) - ctx.ask (Queries.BlobSize (ptr, true)) + ctx.ask (Queries.BlobSize {exp = ptr; base_address = true}) else match ctx.ask (Queries.MayPointTo ptr) with | a when not (Queries.LS.is_top a) -> diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 1735a047ea..d7de0b1e79 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -91,9 +91,9 @@ type _ t = | EvalStr: exp -> SD.t t | EvalLength: exp -> ID.t t (* length of an array or string *) | EvalValue: exp -> VD.t t - | BlobSize: exp * bool -> ID.t t + | BlobSize: {exp: Cil.exp; base_address: bool} -> ID.t t (* Size of a dynamically allocated `Blob pointed to by exp. *) - (* If the second component is set to true, then address offsets are discarded and the size of the `Blob is asked for the base address. *) + (* If the record's second field is set to true, then address offsets are discarded and the size of the `Blob is asked for the base address. *) | CondVars: exp -> ES.t t | PartAccess: access -> Obj.t t (** Only queried by access and deadlock analysis. [Obj.t] represents [MCPAccess.A.t], needed to break dependency cycle. *) | IterPrevVars: iterprevvar -> Unit.t t @@ -336,7 +336,7 @@ struct | Any (EvalLength e1), Any (EvalLength e2) -> CilType.Exp.compare e1 e2 | Any (EvalMutexAttr e1), Any (EvalMutexAttr e2) -> CilType.Exp.compare e1 e2 | Any (EvalValue e1), Any (EvalValue e2) -> CilType.Exp.compare e1 e2 - | Any (BlobSize (e1, _)), Any (BlobSize (e2, _)) -> CilType.Exp.compare e1 e2 + | Any (BlobSize {exp = e1; _}), Any (BlobSize {exp = e2; _}) -> CilType.Exp.compare e1 e2 | Any (CondVars e1), Any (CondVars e2) -> CilType.Exp.compare e1 e2 | Any (PartAccess p1), Any (PartAccess p2) -> compare_access p1 p2 | Any (IterPrevVars ip1), Any (IterPrevVars ip2) -> compare_iterprevvar ip1 ip2 @@ -381,7 +381,7 @@ struct | Any (EvalLength e) -> CilType.Exp.hash e | Any (EvalMutexAttr e) -> CilType.Exp.hash e | Any (EvalValue e) -> CilType.Exp.hash e - | Any (BlobSize (e, from_base_addr)) -> CilType.Exp.hash e + Hashtbl.hash from_base_addr + | Any (BlobSize {exp = e; base_address = b}) -> CilType.Exp.hash e + Hashtbl.hash b | Any (CondVars e) -> CilType.Exp.hash e | Any (PartAccess p) -> hash_access p | Any (IterPrevVars i) -> 0 @@ -429,7 +429,7 @@ struct | Any (EvalStr e) -> Pretty.dprintf "EvalStr %a" CilType.Exp.pretty e | Any (EvalLength e) -> Pretty.dprintf "EvalLength %a" CilType.Exp.pretty e | Any (EvalValue e) -> Pretty.dprintf "EvalValue %a" CilType.Exp.pretty e - | Any (BlobSize (e, from_base_addr)) -> Pretty.dprintf "BlobSize %a (from base address: %b)" CilType.Exp.pretty e from_base_addr + | Any (BlobSize {exp = e; base_address = b}) -> Pretty.dprintf "BlobSize %a (base_address: %b)" CilType.Exp.pretty e b | Any (CondVars e) -> Pretty.dprintf "CondVars %a" CilType.Exp.pretty e | Any (PartAccess p) -> Pretty.dprintf "PartAccess _" | Any (IterPrevVars i) -> Pretty.dprintf "IterPrevVars _" From b122f4c4c00b555bf757956b6c01de3a5bd80e13 Mon Sep 17 00:00:00 2001 From: Nathan Schmidt Date: Wed, 6 Sep 2023 12:23:19 +0200 Subject: [PATCH 0560/1312] Fixed cardinal on top, simplified compute_concat --- src/cdomains/arrayDomain.ml | 53 ++++++++++--------- .../regression/73-strings/07-larger_example.c | 2 +- 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 68e64f125b..e1d7062a70 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -253,7 +253,7 @@ struct let get_vars_in_e _ = [] let map f (xl, xr) = ((List.map f xl), f xr) let fold_left f a x = f a (join_of_all_parts x) - let content_to_top x = (Base.top (), Val.top ()) + let content_to_top _ = (Base.top (), Val.top ()) let printXml f (xl,xr) = BatPrintf.fprintf f "\n\n unrolled array\n xl\n%a\n\n @@ -867,7 +867,7 @@ struct let fold_left f a (x, l) = Base.fold_left f a x let get_vars_in_e _ = [] - let content_to_top (x, l) = (Base.content_to_top x, l) + let content_to_top (x, l) = (Base.content_to_top x, Idx.top_of ILong) let smart_join _ _ = join let smart_widen _ _ = widen @@ -916,7 +916,7 @@ struct let fold_left f a (x, l) = Base.fold_left f a x let get_vars_in_e (x, _) = Base.get_vars_in_e x - let content_to_top (x, l) = (Base.content_to_top x, l) + let content_to_top (x, l) = (Base.content_to_top x, Idx.top_of ILong) let smart_join x_eval_int y_eval_int (x,xl) (y,yl) = let l = Idx.join xl yl in @@ -970,7 +970,7 @@ struct let fold_left f a (x, l) = Base.fold_left f a x let get_vars_in_e _ = [] - let content_to_top (x, l) = (Base.content_to_top x, l) + let content_to_top (x, l) = (Base.content_to_top x, Idx.top_of ILong) let smart_join _ _ = join let smart_widen _ _ = widen @@ -1071,7 +1071,9 @@ struct check_all_indexes (Z.succ i) else false in - if Z.lt (Z.of_int (MustNulls.cardinal must_nulls_set)) (Z.sub max i) then + if MustNulls.is_bot may_nulls_set then + true + else if Z.lt (Z.of_int (MustNulls.cardinal must_nulls_set)) (Z.sub max i) then false else check_all_indexes i in @@ -1277,7 +1279,7 @@ struct let fold_left f acc _ = f acc (Val.top ()) - let content_to_top (_, _, size) = (MustNulls.top (), MayNulls.top (), size) + let content_to_top (_, _, size) = (MustNulls.top (), MayNulls.top (), Idx.top_of ILong) let smart_join _ _ = join let smart_widen _ _ = widen @@ -1607,22 +1609,25 @@ struct let compute_concat must_nulls_set2' may_nulls_set2' = let strlen1 = to_string_length (must_nulls_set1, may_nulls_set1, size1) in let strlen2 = to_string_length (must_nulls_set2', may_nulls_set2', size2) in - match Idx.minimal size1, idx_maximal size1, Idx.minimal strlen1, idx_maximal strlen1, Idx.minimal strlen2, idx_maximal strlen2 with - | Some min_size1, Some max_size1, Some minlen1, Some maxlen1, Some minlen2, Some maxlen2 -> - update_sets min_size1 max_size1 true minlen1 maxlen1 true minlen2 maxlen2 true must_nulls_set2' may_nulls_set2' - (* no upper bound for length of concatenation *) - | Some min_size1, Some max_size1, Some minlen1, None, Some minlen2, Some _ - | Some min_size1, Some max_size1, Some minlen1, Some _, Some minlen2, None - | Some min_size1, Some max_size1, Some minlen1, None, Some minlen2, None -> - update_sets min_size1 max_size1 true minlen1 Z.zero false minlen2 Z.zero false must_nulls_set2' may_nulls_set2' - (* no upper bound for size of dest *) - | Some min_size1, None, Some minlen1, Some maxlen1, Some minlen2, Some maxlen2 -> - update_sets min_size1 Z.zero false minlen1 maxlen1 true minlen2 maxlen2 true must_nulls_set2' may_nulls_set2' - (* no upper bound for size of dest and length of concatenation *) - | Some min_size1, None, Some minlen1, None, Some minlen2, Some _ - | Some min_size1, None, Some minlen1, Some _, Some minlen2, None - | Some min_size1, None, Some minlen1, None, Some minlen2, None -> - update_sets min_size1 Z.zero false minlen1 Z.zero false minlen2 Z.zero false must_nulls_set2' may_nulls_set2' + match Idx.minimal size1, Idx.minimal strlen1, Idx.minimal strlen2 with + | Some min_size1, Some minlen1, Some minlen2 -> + begin match idx_maximal size1, idx_maximal strlen1, idx_maximal strlen2 with + | Some max_size1, Some maxlen1, Some maxlen2 -> + update_sets min_size1 max_size1 true minlen1 maxlen1 true minlen2 maxlen2 true must_nulls_set2' may_nulls_set2' + (* no upper bound for length of concatenation *) + | Some max_size1, None, Some _ + | Some max_size1, Some _, None + | Some max_size1, None, None -> + update_sets min_size1 max_size1 true minlen1 Z.zero false minlen2 Z.zero false must_nulls_set2' may_nulls_set2' + (* no upper bound for size of dest *) + | None, Some maxlen1, Some maxlen2 -> + update_sets min_size1 Z.zero false minlen1 maxlen1 true minlen2 maxlen2 true must_nulls_set2' may_nulls_set2' + (* no upper bound for size of dest and length of concatenation *) + | None, None, Some _ + | None, Some _, None + | None, None, None -> + update_sets min_size1 Z.zero false minlen1 Z.zero false minlen2 Z.zero false must_nulls_set2' may_nulls_set2' + end (* any other case shouldn't happen as minimal index is always >= 0 *) | _ -> (MustNulls.top (), MayNulls.top (), size1) in @@ -1942,7 +1947,7 @@ struct let to_null_byte_domain s = if get_bool "ana.base.arrays.nullbytes" then - (F.top (), N.to_null_byte_domain s) + (F.make (Idx.top_of ILong) (Val.meet (Val.not_zero_of_ikind IChar) (Val.zero_of_ikind IChar)), N.to_null_byte_domain s) else (F.top (), N.top ()) let to_string_length (_, t_n) = @@ -1955,7 +1960,7 @@ struct (F.content_to_top t_f1, N.string_copy t_n1 t_n2 n) else (F.content_to_top t_f1, N.top ()) - let string_concat (t_f1, t_n1) (_, t_n2) n = + let string_concat (t_f1, t_n1) (_, t_n2) n = if get_bool "ana.base.arrays.nullbytes" then (F.content_to_top t_f1, N.string_concat t_n1 t_n2 n) else diff --git a/tests/regression/73-strings/07-larger_example.c b/tests/regression/73-strings/07-larger_example.c index 5dce3b0cfe..b20fa929b5 100644 --- a/tests/regression/73-strings/07-larger_example.c +++ b/tests/regression/73-strings/07-larger_example.c @@ -16,7 +16,7 @@ int main() { __goblint_check(strcmp(user, "Alice") == 0); // UNKNOWN __goblint_check(strcmp(user, "Bob") == 0); // UNKNOWN - __goblint_check(strcmp(user, "Eve") != 0); // TODO: check implementation, maybe returning top wrong and we should return bot in string literals domain + __goblint_check(strcmp(user, "Eve") != 0); char pwd_gen[20]; for (size_t i = 12; i < 20; i++) From 46ae6ee1d5bfacbed5ced3b475249d724d540601 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 6 Sep 2023 16:43:53 +0200 Subject: [PATCH 0561/1312] Add extra parentheses in witness.ml for memory safety result generation --- src/witness/witness.ml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/witness/witness.ml b/src/witness/witness.ml index 264e0bc066..31034a2d9a 100644 --- a/src/witness/witness.ml +++ b/src/witness/witness.ml @@ -512,7 +512,7 @@ struct let next _ = [] end in - if not !AnalysisState.svcomp_may_invalid_free then + if not !AnalysisState.svcomp_may_invalid_free then ( let module TaskResult = struct module Arg = Arg @@ -523,7 +523,7 @@ struct end in (module TaskResult:WitnessTaskResult) - else ( + ) else ( let module TaskResult = struct module Arg = TrivialArg @@ -542,7 +542,7 @@ struct let next _ = [] end in - if not !AnalysisState.svcomp_may_invalid_deref then + if not !AnalysisState.svcomp_may_invalid_deref then ( let module TaskResult = struct module Arg = Arg @@ -553,7 +553,7 @@ struct end in (module TaskResult:WitnessTaskResult) - else ( + ) else ( let module TaskResult = struct module Arg = TrivialArg @@ -572,7 +572,7 @@ struct let next _ = [] end in - if not !AnalysisState.svcomp_may_invalid_memtrack then + if not !AnalysisState.svcomp_may_invalid_memtrack then ( let module TaskResult = struct module Arg = Arg @@ -583,7 +583,7 @@ struct end in (module TaskResult:WitnessTaskResult) - else ( + ) else ( let module TaskResult = struct module Arg = TrivialArg From d4ef55594e2f388febfb31e598e47691b7dafc90 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 6 Sep 2023 17:21:58 +0200 Subject: [PATCH 0562/1312] Add quick and dirty workaround attempt for working with SV-COMP's memory-safety category --- src/autoTune.ml | 1 + src/witness/svcomp.ml | 1 + src/witness/svcompSpec.ml | 14 ++++++++---- src/witness/witness.ml | 46 ++++++++++++++++++++++++++++++++------- 4 files changed, 50 insertions(+), 12 deletions(-) diff --git a/src/autoTune.ml b/src/autoTune.ml index d532081799..9e3508ccd2 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -226,6 +226,7 @@ let focusOnSpecification () = (* TODO: Finish these two below later *) | ValidDeref | ValidMemtrack -> () + | MemorySafety -> () (* TODO: This is here for now just to complete the pattern match *) (*Detect enumerations and enable the "ana.int.enums" option*) exception EnumFound diff --git a/src/witness/svcomp.ml b/src/witness/svcomp.ml index f1ee18ed72..15d41c0210 100644 --- a/src/witness/svcomp.ml +++ b/src/witness/svcomp.ml @@ -55,6 +55,7 @@ struct | ValidFree -> "valid-free" | ValidDeref -> "valid-deref" | ValidMemtrack -> "valid-memtrack" + | MemorySafety -> "memory-safety" (* TODO: Currently here only to complete the pattern match *) in "false(" ^ result_spec ^ ")" | Unknown -> "unknown" diff --git a/src/witness/svcompSpec.ml b/src/witness/svcompSpec.ml index 8dafb8873c..f066610953 100644 --- a/src/witness/svcompSpec.ml +++ b/src/witness/svcompSpec.ml @@ -9,10 +9,11 @@ type t = | ValidFree | ValidDeref | ValidMemtrack + | MemorySafety (* Internal property for use in Goblint; serves as a summary for ValidFree, ValidDeref and ValidMemtrack *) let of_string s = let s = String.strip s in - let regexp = Str.regexp "CHECK( init(main()), LTL(G \\(.*\\)) )" in + let regexp = Str.regexp "CHECK( init(main()), LTL(G \\(.*\\)) )\nCHECK( init(main()), LTL(G \\(.*\\)) )\nCHECK( init(main()), LTL(G \\(.*\\)) )" in let regexp_negated = Str.regexp "CHECK( init(main()), LTL(G ! \\(.*\\)) )" in if Str.string_match regexp_negated s 0 then let global_not = Str.matched_group 1 s in @@ -28,13 +29,17 @@ let of_string s = else failwith "Svcomp.Specification.of_string: unknown global not expression" else if Str.string_match regexp s 0 then - let global = Str.matched_group 1 s in - if global = "valid-free" then + let global1 = Str.matched_group 1 s in + let global2 = Str.matched_group 2 s in + let global3 = Str.matched_group 3 s in + if global1 = "valid-free" && global2 = "valid-deref" && global3 = "valid-memtrack" then + MemorySafety + (* if global = "valid-free" then ValidFree else if global = "valid-deref" then ValidDeref else if global = "valid-memtrack" then - ValidMemtrack + ValidMemtrack *) else failwith "Svcomp.Specification.of_string: unknown global expression" else @@ -65,5 +70,6 @@ let to_string spec = | ValidFree -> "valid-free", false | ValidDeref -> "valid-deref", false | ValidMemtrack -> "valid-memtrack", false + | MemorySafety -> "memory-safety", false (* TODO: That's false, it's currently here just to complete the pattern match *) in print_output spec_str is_neg diff --git a/src/witness/witness.ml b/src/witness/witness.ml index 31034a2d9a..437ba187ae 100644 --- a/src/witness/witness.ml +++ b/src/witness/witness.ml @@ -505,8 +505,8 @@ struct in (module TaskResult:WitnessTaskResult) ) - | ValidFree -> - let module TrivialArg = + | ValidFree (*->*) + (* let module TrivialArg = struct include Arg let next _ = [] @@ -534,9 +534,9 @@ struct end in (module TaskResult:WitnessTaskResult) - ) - | ValidDeref -> - let module TrivialArg = + ) *) + | ValidDeref (*->*) + (* let module TrivialArg = struct include Arg let next _ = [] @@ -564,9 +564,9 @@ struct end in (module TaskResult:WitnessTaskResult) - ) - | ValidMemtrack -> - let module TrivialArg = + ) *) + | ValidMemtrack (*->*) + (* let module TrivialArg = struct include Arg let next _ = [] @@ -583,6 +583,36 @@ struct end in (module TaskResult:WitnessTaskResult) + ) else ( + let module TaskResult = + struct + module Arg = TrivialArg + let result = Result.Unknown + let invariant _ = Invariant.none + let is_violation _ = false + let is_sink _ = false + end + in + (module TaskResult:WitnessTaskResult) + ) *) + | MemorySafety -> + let module TrivialArg = + struct + include Arg + let next _ = [] + end + in + if not !AnalysisState.svcomp_may_invalid_free || not !AnalysisState.svcomp_may_invalid_deref || not !AnalysisState.svcomp_may_invalid_memtrack then ( + let module TaskResult = + struct + module Arg = Arg + let result = Result.True + let invariant _ = Invariant.none + let is_violation _ = false + let is_sink _ = false + end + in + (module TaskResult:WitnessTaskResult) ) else ( let module TaskResult = struct From 4a088c938f97f2bd61bc56f97356a7cff479d3d1 Mon Sep 17 00:00:00 2001 From: Nathan Schmidt Date: Wed, 6 Sep 2023 18:24:12 +0200 Subject: [PATCH 0563/1312] Fixed `content_to_top` --- src/cdomains/arrayDomain.ml | 32 ++++++++++++------- src/cdomains/arrayDomain.mli | 12 +++++-- src/cdomains/valueDomain.ml | 16 ++++++++++ .../regression/73-strings/07-larger_example.c | 4 +-- 4 files changed, 47 insertions(+), 17 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index e1d7062a70..1f1999514e 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -95,9 +95,15 @@ sig val get: ?checkBounds:bool -> VDQ.t -> t -> Basetype.CilExp.t option * idx -> value end -module type LatticeWithSmartOps = +module type LatticeWithInvalidate = sig include Lattice.S + val invalidate_abstract_value: t -> t +end + +module type LatticeWithSmartOps = +sig + include LatticeWithInvalidate val smart_join: (Cil.exp -> BI.t option) -> (Cil.exp -> BI.t option) -> t -> t -> t val smart_widen: (Cil.exp -> BI.t option) -> (Cil.exp -> BI.t option) -> t -> t -> t val smart_leq: (Cil.exp -> BI.t option) -> (Cil.exp -> BI.t option) -> t -> t -> bool @@ -116,7 +122,7 @@ sig val not_zero_of_ikind: Cil.ikind -> t end -module Trivial (Val: Lattice.S) (Idx: Lattice.S): S with type value = Val.t and type idx = Idx.t = +module Trivial (Val: LatticeWithInvalidate) (Idx: Lattice.S): S with type value = Val.t and type idx = Idx.t = struct include Val let name () = "trivial arrays" @@ -143,7 +149,7 @@ struct let map f x = f x let fold_left f a x = f a x - let content_to_top _ = Val.top () + let content_to_top x = Val.invalidate_abstract_value x let printXml f x = BatPrintf.fprintf f "\n\nAny\n%a\n\n\n" Val.printXml x let smart_join _ _ = join @@ -174,7 +180,7 @@ let factor () = | 0 -> failwith "ArrayDomain: ana.base.arrays.unrolling-factor needs to be set when using the unroll domain" | x -> x -module Unroll (Val: Lattice.S) (Idx:IntDomain.Z): S with type value = Val.t and type idx = Idx.t = +module Unroll (Val: LatticeWithInvalidate) (Idx:IntDomain.Z): S with type value = Val.t and type idx = Idx.t = struct module Factor = struct let x () = (get_int "ana.base.arrays.unrolling-factor") end module Base = Lattice.ProdList (Val) (Factor) @@ -253,7 +259,9 @@ struct let get_vars_in_e _ = [] let map f (xl, xr) = ((List.map f xl), f xr) let fold_left f a x = f a (join_of_all_parts x) - let content_to_top _ = (Base.top (), Val.top ()) + let content_to_top (xl, xr) = + let invalidated_val _ = Val.invalidate_abstract_value xr in + (List.map invalidated_val xl, invalidated_val xr) let printXml f (xl,xr) = BatPrintf.fprintf f "\n\n unrolled array\n xl\n%a\n\n @@ -346,7 +354,7 @@ struct let is_top = function | Joint x -> Val.is_top x | _-> false - let content_to_top _ = top () + let content_to_top x = Joint (Val.invalidate_abstract_value (join_of_all_parts x)) let join (x:t) (y:t) = normalize @@ match x, y with @@ -847,7 +855,7 @@ let array_oob_check ( type a ) (module Idx: IntDomain.Z with type t = a) (x, l) else () -module TrivialWithLength (Val: Lattice.S) (Idx: IntDomain.Z): S with type value = Val.t and type idx = Idx.t = +module TrivialWithLength (Val: LatticeWithInvalidate) (Idx: IntDomain.Z): S with type value = Val.t and type idx = Idx.t = struct module Base = Trivial (Val) (Idx) include Lattice.Prod (Base) (Idx) @@ -867,7 +875,7 @@ struct let fold_left f a (x, l) = Base.fold_left f a x let get_vars_in_e _ = [] - let content_to_top (x, l) = (Base.content_to_top x, Idx.top_of ILong) + let content_to_top (x, l) = (Base.content_to_top x, l) let smart_join _ _ = join let smart_widen _ _ = widen @@ -916,7 +924,7 @@ struct let fold_left f a (x, l) = Base.fold_left f a x let get_vars_in_e (x, _) = Base.get_vars_in_e x - let content_to_top (x, l) = (Base.content_to_top x, Idx.top_of ILong) + let content_to_top (x, l) = (Base.content_to_top x, l) let smart_join x_eval_int y_eval_int (x,xl) (y,yl) = let l = Idx.join xl yl in @@ -949,7 +957,7 @@ struct let to_yojson (x, y) = `Assoc [ (Base.name (), Base.to_yojson x); ("length", Idx.to_yojson y) ] end -module UnrollWithLength (Val: Lattice.S) (Idx: IntDomain.Z): S with type value = Val.t and type idx = Idx.t = +module UnrollWithLength (Val: LatticeWithInvalidate) (Idx: IntDomain.Z): S with type value = Val.t and type idx = Idx.t = struct module Base = Unroll (Val) (Idx) include Lattice.Prod (Base) (Idx) @@ -970,7 +978,7 @@ struct let fold_left f a (x, l) = Base.fold_left f a x let get_vars_in_e _ = [] - let content_to_top (x, l) = (Base.content_to_top x, Idx.top_of ILong) + let content_to_top (x, l) = (Base.content_to_top x, l) let smart_join _ _ = join let smart_widen _ _ = widen @@ -1279,7 +1287,7 @@ struct let fold_left f acc _ = f acc (Val.top ()) - let content_to_top (_, _, size) = (MustNulls.top (), MayNulls.top (), Idx.top_of ILong) + let content_to_top (_, _, size) = (MustNulls.top (), MayNulls.top (), size) let smart_join _ _ = join let smart_widen _ _ = widen diff --git a/src/cdomains/arrayDomain.mli b/src/cdomains/arrayDomain.mli index e8deae06e0..915dfee470 100644 --- a/src/cdomains/arrayDomain.mli +++ b/src/cdomains/arrayDomain.mli @@ -115,9 +115,15 @@ sig val get: ?checkBounds:bool -> VDQ.t -> t -> Basetype.CilExp.t option * idx -> value end -module type LatticeWithSmartOps = +module type LatticeWithInvalidate = sig include Lattice.S + val invalidate_abstract_value: t -> t +end + +module type LatticeWithSmartOps = +sig + include LatticeWithInvalidate val smart_join: (Cil.exp -> BigIntOps.t option) -> (Cil.exp -> BigIntOps.t option) -> t -> t -> t val smart_widen: (Cil.exp -> BigIntOps.t option) -> (Cil.exp -> BigIntOps.t option) -> t -> t -> t val smart_leq: (Cil.exp -> BigIntOps.t option) -> (Cil.exp -> BigIntOps.t option) -> t -> t -> bool @@ -136,12 +142,12 @@ sig val not_zero_of_ikind: Cil.ikind -> t end -module Trivial (Val: Lattice.S) (Idx: Lattice.S): S with type value = Val.t and type idx = Idx.t +module Trivial (Val: LatticeWithInvalidate) (Idx: Lattice.S): S with type value = Val.t and type idx = Idx.t (** This functor creates a trivial single cell representation of an array. The * indexing type is taken as a parameter to satisfy the type system, it is not * used in the implementation. *) -module TrivialWithLength (Val: Lattice.S) (Idx: IntDomain.Z): S with type value = Val.t and type idx = Idx.t +module TrivialWithLength (Val: LatticeWithInvalidate) (Idx: IntDomain.Z): S with type value = Val.t and type idx = Idx.t (** This functor creates a trivial single cell representation of an array. The * indexing type is also used to manage the length. *) diff --git a/src/cdomains/valueDomain.ml b/src/cdomains/valueDomain.ml index 6029111942..d204774493 100644 --- a/src/cdomains/valueDomain.ml +++ b/src/cdomains/valueDomain.ml @@ -24,6 +24,7 @@ sig val affect_move: ?replace_with_const:bool -> VDQ.t -> t -> varinfo -> (exp -> int option) -> t val affecting_vars: t -> varinfo list val invalidate_value: VDQ.t -> typ -> t -> t + val invalidate_abstract_value: t -> t val is_safe_cast: typ -> typ -> bool val cast: ?torg:typ -> typ -> t -> t val smart_join: (exp -> BI.t option) -> (exp -> BI.t option) -> t -> t -> t @@ -757,6 +758,21 @@ struct | _, Bot -> Bot (* Leave uninitialized value (from malloc) alone in free to avoid trashing everything. TODO: sound? *) | t , _ -> top_value t + let invalidate_abstract_value = function + | Top -> Top + | Int i -> Int (ID.top_of (ID.ikind i)) + | Float f -> Float (FD.top_of (FD.get_fkind f)) + | Address _ -> Address (AD.top_ptr) + | Struct _ -> Struct (Structs.top ()) + | Union _ -> Union (Unions.top ()) + | Array _ -> Array (CArrays.top ()) + | Blob _ -> Blob (Blobs.top ()) + | Thread _ -> Thread (Threads.top ()) + | JmpBuf _ -> JmpBuf (JmpBufs.top ()) + | Mutex -> Mutex + | MutexAttr _ -> MutexAttr (MutexAttrDomain.top ()) + | Bot -> Bot + (* take the last offset in offset and move it over to left *) let shift_one_over left offset = diff --git a/tests/regression/73-strings/07-larger_example.c b/tests/regression/73-strings/07-larger_example.c index b20fa929b5..f756108343 100644 --- a/tests/regression/73-strings/07-larger_example.c +++ b/tests/regression/73-strings/07-larger_example.c @@ -26,9 +26,9 @@ int main() { char* p2 = "12345"; strcat(pwd_gen, p1); // WARN strncpy(pwd_gen, p2, 6); - __goblint_check(pwd_gen[5] == '\0'); // TODO: fix get in attributeconfiguredarraydomain + __goblint_check(pwd_gen[5] == '\0'); strncat(pwd_gen, p1, 4); - __goblint_check(pwd_gen[5] != '\0'); // TODO: fix get in attributeconfiguredarraydomain + __goblint_check(pwd_gen[5] != '\0'); int cmp = strcmp(pwd_gen, "12345hello"); __goblint_check(cmp != 0); From 2fb4d743374fd4322ad2fa2ce98bcaf91d8870ee Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 6 Sep 2023 18:59:51 +0200 Subject: [PATCH 0564/1312] Use logical AND and not OR for the memory-safety category in witness --- src/witness/witness.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/witness/witness.ml b/src/witness/witness.ml index 437ba187ae..66a62d1b03 100644 --- a/src/witness/witness.ml +++ b/src/witness/witness.ml @@ -602,7 +602,7 @@ struct let next _ = [] end in - if not !AnalysisState.svcomp_may_invalid_free || not !AnalysisState.svcomp_may_invalid_deref || not !AnalysisState.svcomp_may_invalid_memtrack then ( + if not !AnalysisState.svcomp_may_invalid_free && not !AnalysisState.svcomp_may_invalid_deref && not !AnalysisState.svcomp_may_invalid_memtrack then ( let module TaskResult = struct module Arg = Arg From d97504bf08c8f79e04539665432ad05ffd843523 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 7 Sep 2023 16:13:29 +0300 Subject: [PATCH 0565/1312] Fix YAML witness unassume indentation (PR #1124) --- src/analyses/base.ml | 6 +++-- src/witness/witness.ml | 58 +++++++++++++++++++++--------------------- 2 files changed, 33 insertions(+), 31 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 4a49b9eed0..b74555b7ad 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2297,8 +2297,10 @@ struct let sizeval = eval_int (Analyses.ask_of_ctx ctx) gs st size in let countval = eval_int (Analyses.ask_of_ctx ctx) gs st n in if ID.to_int countval = Some Z.one then ( - set_many ~ctx (Analyses.ask_of_ctx ctx) gs st [(add_null (AD.of_var heap_var), TVoid [], Blob (VD.bot (), sizeval, false)); - (eval_lv (Analyses.ask_of_ctx ctx) gs st lv, (Cilfacade.typeOfLval lv), Address (add_null (AD.of_var heap_var)))] + set_many ~ctx (Analyses.ask_of_ctx ctx) gs st [ + add_null (AD.of_var heap_var), TVoid [], Blob (VD.bot (), sizeval, false); + eval_lv (Analyses.ask_of_ctx ctx) gs st lv, (Cilfacade.typeOfLval lv), Address (add_null (AD.of_var heap_var)) + ] ) else ( let blobsize = ID.mul (ID.cast_to ik @@ sizeval) (ID.cast_to ik @@ countval) in diff --git a/src/witness/witness.ml b/src/witness/witness.ml index f73a2755c8..baa465a1b3 100644 --- a/src/witness/witness.ml +++ b/src/witness/witness.ml @@ -306,35 +306,35 @@ struct let determine_result entrystates (module Task:Task): (module WitnessTaskResult) = let module Arg: BiArgInvariant = (val if GobConfig.get_bool "witness.enabled" then ( - let module Arg = (val ArgTool.create entrystates) in - let module Arg = - struct - include Arg - - let find_invariant (n, c, i) = - let context = {Invariant.default_context with path = Some i} in - ask_local (n, c) (Invariant context) - end - in - (module Arg: BiArgInvariant) - ) - else ( - let module Arg = - struct - module Node = ArgTool.Node - module Edge = MyARG.InlineEdge - let next _ = [] - let prev _ = [] - let find_invariant _ = Invariant.none - let main_entry = - let lvar = WitnessUtil.find_main_entry entrystates in - (fst lvar, snd lvar, -1) - let iter_nodes f = f main_entry - let query _ q = Queries.Result.top q - end - in - (module Arg: BiArgInvariant) - ) + let module Arg = (val ArgTool.create entrystates) in + let module Arg = + struct + include Arg + + let find_invariant (n, c, i) = + let context = {Invariant.default_context with path = Some i} in + ask_local (n, c) (Invariant context) + end + in + (module Arg: BiArgInvariant) + ) + else ( + let module Arg = + struct + module Node = ArgTool.Node + module Edge = MyARG.InlineEdge + let next _ = [] + let prev _ = [] + let find_invariant _ = Invariant.none + let main_entry = + let lvar = WitnessUtil.find_main_entry entrystates in + (fst lvar, snd lvar, -1) + let iter_nodes f = f main_entry + let query _ q = Queries.Result.top q + end + in + (module Arg: BiArgInvariant) + ) ) in From fd95dbe0947e85da6155a6ecba0c02efb270295d Mon Sep 17 00:00:00 2001 From: Nathan Schmidt Date: Thu, 7 Sep 2023 17:34:57 +0200 Subject: [PATCH 0566/1312] Minor bugfix, updated test IDs and annotations --- src/cdomains/arrayDomain.ml | 2 +- .../{04-char_arrays.c => 05-char_arrays.c} | 0 tests/regression/73-strings/06-juliet.c | 43 +++++++++++++------ 3 files changed, 31 insertions(+), 14 deletions(-) rename tests/regression/73-strings/{04-char_arrays.c => 05-char_arrays.c} (100%) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 1f1999514e..4503d3c7fb 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1079,7 +1079,7 @@ struct check_all_indexes (Z.succ i) else false in - if MustNulls.is_bot may_nulls_set then + if MustNulls.is_bot must_nulls_set then true else if Z.lt (Z.of_int (MustNulls.cardinal must_nulls_set)) (Z.sub max i) then false diff --git a/tests/regression/73-strings/04-char_arrays.c b/tests/regression/73-strings/05-char_arrays.c similarity index 100% rename from tests/regression/73-strings/04-char_arrays.c rename to tests/regression/73-strings/05-char_arrays.c diff --git a/tests/regression/73-strings/06-juliet.c b/tests/regression/73-strings/06-juliet.c index 53bc2ba4e9..a5320d4c4b 100644 --- a/tests/regression/73-strings/06-juliet.c +++ b/tests/regression/73-strings/06-juliet.c @@ -1,4 +1,4 @@ -// PARAM: --disable ana.base.limit-string-addresses --enable ana.int.interval --enable ana.base.arrays.nullbytes +// PARAM: --disable ana.base.limit-string-addresses --enable ana.int.interval --set ana.base.arrays.domain partitioned --enable ana.base.arrays.nullbytes #include #include @@ -24,8 +24,11 @@ void CWE121_Stack_Based_Buffer_Overflow__src_char_declare_cpy_01_bad() char dataBuffer[100]; data = dataBuffer; /* FLAW: Initialize data as a large buffer that is larger than the small buffer used in the sink */ - memset(data, 'A', 100-1); /* fill with 'A's */ + /* memset(data, 'A', 100-1); // fill with 'A's -- memset not supported currently, replaced with for-loop */ + for (size_t i = 0; i < 100-1; i++) + data[i] = 'A'; data[100-1] = '\0'; /* null terminate */ + __goblint_check(data[42] == 'A'); { char dest[50] = ""; /* POTENTIAL FLAW: Possible buffer overflow if data is larger than dest */ @@ -39,14 +42,16 @@ void CWE126_Buffer_Overread__CWE170_char_loop_01_bad() char src[150], dest[100]; int i; /* Initialize src */ - memset(src, 'A', 149); + /* memset(src, 'A', 149); */ + for (i = 0; i < 149; i++) + src[i] = 'A'; src[149] = '\0'; for(i=0; i < 99; i++) { dest[i] = src[i]; } /* FLAW: do not explicitly null terminate dest after the loop */ - __goblint_check(dest[42] != '\0'); + __goblint_check(dest[42] != '\0'); // UNKNOWN __goblint_check(dest[99] != '\0'); // UNKNOWN } } @@ -56,7 +61,9 @@ void CWE126_Buffer_Overread__CWE170_char_strncpy_01_bad() { char data[150], dest[100]; /* Initialize data */ - memset(data, 'A', 149); + /* memset(data, 'A', 149); */ + for (size_t i = 0; i < 149; i++) + data[i] = 'A'; data[149] = '\0'; /* strncpy() does not null terminate if the string in the src buffer is larger than * the number of characters being copied to the dest buffer */ @@ -70,19 +77,25 @@ void CWE126_Buffer_Overread__char_declare_loop_01_bad() char * data; char dataBadBuffer[50]; char dataGoodBuffer[100]; - memset(dataBadBuffer, 'A', 50-1); /* fill with 'A's */ + /* memset(dataBadBuffer, 'A', 50-1); // fill with 'A's */ + for (size_t i = 0; i < 50-1; i++) + dataBadBuffer[i] = 'A'; dataBadBuffer[50-1] = '\0'; /* null terminate */ - memset(dataGoodBuffer, 'A', 100-1); /* fill with 'A's */ + /* memset(dataGoodBuffer, 'A', 100-1); // fill with 'A's */ + for (size_t i = 0; i < 100-1; i++) + dataGoodBuffer[i] = 'A'; dataGoodBuffer[100-1] = '\0'; /* null terminate */ /* FLAW: Set data pointer to a small buffer */ data = dataBadBuffer; { size_t i, destLen; char dest[100]; - memset(dest, 'C', 100-1); + /* memset(dest, 'C', 100-1); */ + for (i = 0; i < 100-1; i++) + dest[i] = 'C'; dest[100-1] = '\0'; /* null terminate */ destLen = strlen(dest); - __goblint_check(destLen == 99); + __goblint_check(destLen <= 99); /* POTENTIAL FLAW: using length of the dest where data * could be smaller than dest causing buffer overread */ for (i = 0; i < destLen; i++) @@ -102,7 +115,9 @@ void CWE665_Improper_Initialization__char_cat_01_bad() ; /* empty statement needed for some flow variants */ { char source[100]; - memset(source, 'C', 100-1); /* fill with 'C's */ + /* memset(source, 'C', 100-1); // fill with 'C's */ + for (size_t i = 0; i < 100-1; i++) + source[i] = 'C'; source[100-1] = '\0'; /* null terminate */ /* POTENTIAL FLAW: If data is not initialized properly, strcat() may not function correctly */ strcat(data, source); // WARN @@ -135,11 +150,13 @@ void CWE665_Improper_Initialization__char_ncat_11_bad() { size_t sourceLen; char source[100]; - memset(source, 'C', 100-1); /* fill with 'C's */ + /* memset(source, 'C', 100-1); // fill with 'C's */ + for (size_t i = 0; i < 100-1; i++) + source[i] = 'C'; source[100-1] = '\0'; /* null terminate */ sourceLen = strlen(source); - __goblint_check(sourceLen == 99); + __goblint_check(sourceLen <= 99); /* POTENTIAL FLAW: If data is not initialized properly, strncat() may not function correctly */ - strncat(data, source, sourceLen); // WARN --> why not?? spurious + strncat(data, source, sourceLen); // NOWARN because sourceLen is not exactly known => array domain not consulted } } From e4d7e2bdb78f703ac78c7e35276c80f5425d91ef Mon Sep 17 00:00:00 2001 From: Nathan Schmidt Date: Fri, 8 Sep 2023 12:46:22 +0200 Subject: [PATCH 0567/1312] Fixed test 06 for MacOS --- tests/regression/73-strings/06-juliet.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/regression/73-strings/06-juliet.c b/tests/regression/73-strings/06-juliet.c index a5320d4c4b..cda8ffd6dd 100644 --- a/tests/regression/73-strings/06-juliet.c +++ b/tests/regression/73-strings/06-juliet.c @@ -157,6 +157,10 @@ void CWE665_Improper_Initialization__char_ncat_11_bad() sourceLen = strlen(source); __goblint_check(sourceLen <= 99); /* POTENTIAL FLAW: If data is not initialized properly, strncat() may not function correctly */ - strncat(data, source, sourceLen); // NOWARN because sourceLen is not exactly known => array domain not consulted + #ifdef __APPLE__ + ; + #else + strncat(data, source, sourceLen); // NOWARN because sourceLen is not exactly known => array domain not consulted + #endif } } From 9cbf2ab8d10c064a1c1714e700b3b405478b81ae Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 9 Sep 2023 23:13:03 +0200 Subject: [PATCH 0568/1312] Add util function for setting mem safety global vars Make sure to do this only if we're in postsolving --- src/analyses/base.ml | 10 +++++----- src/analyses/memLeak.ml | 7 ++++--- src/analyses/memOutOfBounds.ml | 13 +++++++------ src/analyses/useAfterFree.ml | 17 ++++++----------- src/util/analysisStateUtil.ml | 11 +++++++++++ 5 files changed, 33 insertions(+), 25 deletions(-) create mode 100644 src/util/analysisStateUtil.ml diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 79ac13b861..0cdcf98fc0 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1052,11 +1052,11 @@ struct | Address adr -> ( if AD.is_null adr then ( - AnalysisState.svcomp_may_invalid_deref := true; + AnalysisStateUtil.set_mem_safety_flag InvalidDeref; M.error ~category:M.Category.Behavior.Undefined.nullpointer_dereference ~tags:[CWE 476] "Must dereference NULL pointer" ) else if AD.may_be_null adr then ( - AnalysisState.svcomp_may_invalid_deref := true; + AnalysisStateUtil.set_mem_safety_flag InvalidDeref; M.warn ~category:M.Category.Behavior.Undefined.nullpointer_dereference ~tags:[CWE 476] "May dereference NULL pointer" ) ); @@ -2030,15 +2030,15 @@ struct | Address a -> let points_to_set = addrToLvalSet a in if Q.LS.is_top points_to_set then ( - AnalysisState.svcomp_may_invalid_free := true; + AnalysisStateUtil.set_mem_safety_flag InvalidFree; M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590; CWE 761] "Points-to set for pointer %a in function %s is top. Potentially invalid memory deallocation may occur" d_exp ptr special_fn.vname ) else if (Q.LS.exists (fun (v, _) -> not (ctx.ask (Q.IsHeapVar v))) points_to_set) || (AD.mem Addr.UnknownPtr a) then ( - AnalysisState.svcomp_may_invalid_free := true; + AnalysisStateUtil.set_mem_safety_flag InvalidFree; M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Free of non-dynamically allocated memory in function %s for pointer %a" special_fn.vname d_exp ptr ) else if Q.LS.exists (fun (_, o) -> Offset.Exp.cmp_zero_offset o <> `MustZero) points_to_set then ( - AnalysisState.svcomp_may_invalid_free := true; + AnalysisStateUtil.set_mem_safety_flag InvalidFree; M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 761] "Free of memory not at start of buffer in function %s for pointer %a" special_fn.vname d_exp ptr ) | _ -> M.warn ~category:MessageCategory.Analyzer "Pointer %a in function %s doesn't evaluate to a valid address." d_exp ptr special_fn.vname diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index 688ce1024a..0ea4fc96b1 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -3,6 +3,7 @@ open GoblintCil open Analyses open MessageCategory +open AnalysisStateUtil module ToppedVarInfoSet = SetDomain.ToppedSet(CilType.Varinfo)(struct let topname = "All Heap Variables" end) @@ -20,7 +21,7 @@ struct (* HELPER FUNCTIONS *) let warn_for_multi_threaded ctx = if not (ctx.ask (Queries.MustBeSingleThreaded { since_start = true })) then ( - AnalysisState.svcomp_may_invalid_memtrack := true; + set_mem_safety_flag InvalidMemTrack; M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Program isn't running in single-threaded mode. A memory leak might occur due to multi-threading" ) @@ -29,10 +30,10 @@ struct if not @@ D.is_empty state then match assert_exp_imprecise, exp with | true, Some exp -> - AnalysisState.svcomp_may_invalid_memtrack := true; + AnalysisStateUtil.set_mem_safety_flag InvalidMemTrack; M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "assert expression %a is unknown. Memory leak might possibly occur for heap variables: %a" d_exp exp D.pretty state | _ -> - AnalysisState.svcomp_may_invalid_memtrack := true; + AnalysisStateUtil.set_mem_safety_flag InvalidMemTrack; M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory leak detected for heap variables: %a" D.pretty state (* TRANSFER FUNCTIONS *) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index a7e95282fd..a5cf7d2ca2 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -1,6 +1,7 @@ open GoblintCil open Analyses open MessageCategory +open AnalysisStateUtil module AS = AnalysisState module VDQ = ValueDomainQueries @@ -195,7 +196,7 @@ struct end | _ -> ( - AnalysisState.svcomp_may_invalid_deref :=true; + set_mem_safety_flag InvalidDeref; M.warn "Pointer %a has a points-to-set of top. An invalid memory access might occur" d_exp ptr ); IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind () @@ -229,12 +230,12 @@ struct | Some t -> begin match VDQ.ID.is_top ptr_size with | true -> - AS.svcomp_may_invalid_deref := true; + set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer %a not known. Memory out-of-bounds access might occur due to pointer arithmetic" d_exp lval_exp | false -> let offs = `Lifted addr_offs in if ptr_size < offs then begin - AS.svcomp_may_invalid_deref := true; + set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer is %a (in bytes). It is offset by %a (in bytes) due to pointer arithmetic. Memory out-of-bounds access must occur" VDQ.ID.pretty ptr_size VDQ.ID.pretty offs end end @@ -287,14 +288,14 @@ struct in begin match VDQ.ID.is_top ptr_size, VDQ.ID.is_top offset_size_with_addr_size with | true, _ -> - AS.svcomp_may_invalid_deref := true; + set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer %a in expression %a not known. Memory out-of-bounds access might occur" d_exp e1 d_exp binopexp | _, true -> - AS.svcomp_may_invalid_deref := true; + set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Operand value for pointer arithmetic in expression %a not known. Memory out-of-bounds access might occur" d_exp binopexp | false, false -> if ptr_size < offset_size_with_addr_size then begin - AS.svcomp_may_invalid_deref := true; + set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer in expression %a is %a (in bytes). It is offset by %a (in bytes). Memory out-of-bounds access must occur" d_exp binopexp VDQ.ID.pretty ptr_size VDQ.ID.pretty offset_size_with_addr_size end end diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index 9af1b8ca7a..9dac016ce5 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -3,6 +3,7 @@ open GoblintCil open Analyses open MessageCategory +open AnalysisStateUtil module ToppedVarInfoSet = SetDomain.ToppedSet(CilType.Varinfo)(struct let topname = "All Heap Variables" end) module ThreadIdToJoinedThreadsMap = MapDomain.MapBot(ThreadIdDomain.ThreadLifted)(ConcDomain.MustThreadSet) @@ -24,12 +25,6 @@ struct (* HELPER FUNCTIONS *) - let set_global_svcomp_var is_double_free = - if is_double_free then - AnalysisState.svcomp_may_invalid_free := true - else - AnalysisState.svcomp_may_invalid_deref := true - let get_current_threadid ctx = ctx.ask Queries.CurrentThreadId @@ -65,23 +60,23 @@ struct | `Lifted current -> let possibly_started = G.exists (possibly_started current) freeing_threads in if possibly_started then begin - set_global_svcomp_var is_double_free; + if is_double_free then set_mem_safety_flag InvalidFree else set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "There's a thread that's been started in parallel with the memory-freeing threads for heap variable %a. %s might occur" CilType.Varinfo.pretty heap_var bug_name end else begin let current_is_unique = ThreadId.Thread.is_unique current in let any_equal_current threads = G.exists (equal_current current) threads in if not current_is_unique && any_equal_current freeing_threads then begin - set_global_svcomp_var is_double_free; + if is_double_free then set_mem_safety_flag InvalidFree else set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Current thread is not unique and a %s might occur for heap variable %a" bug_name CilType.Varinfo.pretty heap_var end else if D.mem heap_var ctx.local then begin - set_global_svcomp_var is_double_free; + if is_double_free then set_mem_safety_flag InvalidFree else set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "%s might occur in current unique thread %a for heap variable %a" bug_name ThreadIdDomain.FlagConfiguredTID.pretty current CilType.Varinfo.pretty heap_var end end | `Top -> - set_global_svcomp_var is_double_free; + if is_double_free then set_mem_safety_flag InvalidFree else set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "CurrentThreadId is top. %s might occur for heap variable %a" bug_name CilType.Varinfo.pretty heap_var | `Bot -> M.warn ~category:MessageCategory.Analyzer "CurrentThreadId is bottom" @@ -110,7 +105,7 @@ struct | a when not (Queries.LS.is_top a) && not (Queries.LS.mem (dummyFunDec.svar, `NoOffset) a) -> let warn_for_heap_var var = if D.mem var state then begin - set_global_svcomp_var is_double_free; + if is_double_free then set_mem_safety_flag InvalidFree else set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "lval (%s) in \"%s\" points to a maybe freed memory region" var.vname transfer_fn_name end in diff --git a/src/util/analysisStateUtil.ml b/src/util/analysisStateUtil.ml new file mode 100644 index 0000000000..25914f8c8e --- /dev/null +++ b/src/util/analysisStateUtil.ml @@ -0,0 +1,11 @@ +type mem_safety_violation = + | InvalidFree + | InvalidDeref + | InvalidMemTrack + +let set_mem_safety_flag violation_type = + if !AnalysisState.postsolving then + match violation_type with + | InvalidFree -> AnalysisState.svcomp_may_invalid_free := true + | InvalidDeref -> AnalysisState.svcomp_may_invalid_deref := true + | InvalidMemTrack -> AnalysisState.svcomp_may_invalid_memtrack := true \ No newline at end of file From 3258cce90b88ea14db99c5e6466b43b7fd8a01a5 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 9 Sep 2023 23:35:54 +0200 Subject: [PATCH 0569/1312] Don't warn for pointers that only contain strings in their pts --- src/analyses/memOutOfBounds.ml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index a5cf7d2ca2..4c2576ec24 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -161,6 +161,13 @@ struct let remaining_offset = offs_to_idx typ o in IntDomain.IntDomTuple.add bytes_offset remaining_offset + let ptr_only_has_str_addr ctx ptr = + ctx.ask (Queries.MayPointTo ptr) + |> VDQ.LS.elements + |> List.map (fun (v, o) -> ValueDomain.Addr.of_mval (v, ValueDomain.Addr.Offs.of_exp o)) + |> ValueDomain.AD.of_list + |> ValueDomain.AD.for_all (fun addr -> match addr with | StrPtr _ -> true | _ -> false) + let rec get_addr_offs ctx ptr = match ctx.ask (Queries.MayPointTo ptr) with | a when not (VDQ.LS.is_top a) -> @@ -202,7 +209,8 @@ struct IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind () and check_lval_for_oob_access ctx ?(is_implicitly_derefed = false) lval = - if not @@ lval_contains_a_ptr lval then () + (* If the lval does not contain a pointer or if it does contain a pointer, but only points to string addresses, then no need to WARN *) + if (not @@ lval_contains_a_ptr lval) || ptr_only_has_str_addr ctx (Lval lval) then () else (* If the lval doesn't indicate an explicit dereference, we still need to check for an implicit dereference *) (* An implicit dereference is, e.g., printf("%p", ptr), where ptr is a pointer *) From 0881fb786b7a4a17c0fd5adadc50378580c99a73 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 9 Sep 2023 23:39:05 +0200 Subject: [PATCH 0570/1312] Set global mem safety flags upon array oob as well --- src/cdomains/arrayDomain.ml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index c099a94f96..2f91e47663 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -787,14 +787,19 @@ let array_oob_check ( type a ) (module Idx: IntDomain.Z with type t = a) (x, l) | Some true, Some true -> (* Certainly in bounds on both sides.*) () | Some true, Some false -> (* The following matching differentiates the must and may cases*) + AnalysisStateUtil.set_mem_safety_flag InvalidDeref; M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Must access array past end" | Some true, None -> + AnalysisStateUtil.set_mem_safety_flag InvalidDeref; M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "May access array past end" | Some false, Some true -> + AnalysisStateUtil.set_mem_safety_flag InvalidDeref; M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.before_start "Must access array before start" | None, Some true -> + AnalysisStateUtil.set_mem_safety_flag InvalidDeref; M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.before_start "May access array before start" | _ -> + AnalysisStateUtil.set_mem_safety_flag InvalidDeref; M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.unknown "May access array out of bounds" else () From b9faa8f039a6ad5faeb2ea16483c9c1b179b71da Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 9 Sep 2023 23:47:58 +0200 Subject: [PATCH 0571/1312] Cast pointer arithmetic offsets without using Option.get --- src/analyses/memOutOfBounds.ml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 4c2576ec24..a2c180a453 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -138,11 +138,12 @@ struct let eval_ptr_offset_in_binop ctx exp ptr_contents_typ = let eval_offset = ctx.ask (Queries.EvalInt exp) in - let eval_offset = Option.get @@ VDQ.ID.to_int eval_offset in - let eval_offset = VDQ.ID.of_int (Cilfacade.ptrdiff_ikind ()) eval_offset in let ptr_contents_typ_size_in_bytes = size_of_type_in_bytes ptr_contents_typ in match eval_offset with - | `Lifted i -> `Lifted (IntDomain.IntDomTuple.mul i ptr_contents_typ_size_in_bytes) + | `Lifted i -> + (* The offset must be casted to ptrdiff_ikind in order to have matching ikinds for the multiplication below *) + let casted_offset = IntDomain.IntDomTuple.cast_to (Cilfacade.ptrdiff_ikind ()) i in + `Lifted (IntDomain.IntDomTuple.mul casted_offset ptr_contents_typ_size_in_bytes) | `Top -> `Top | `Bot -> `Bot From 8a6cf0a10258cd6c433f6067111b10f93323002d Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 10 Sep 2023 02:11:41 +0200 Subject: [PATCH 0572/1312] Check and warn everywhere for a top points-to set in memOutOfBounds --- src/analyses/memOutOfBounds.ml | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index a2c180a453..781c48d8e9 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -88,7 +88,9 @@ struct match ctx.ask (Queries.MayPointTo ptr) with | a when not (Queries.LS.is_top a) && not (Queries.LS.mem (dummyFunDec.svar, `NoOffset) a) -> Queries.LS.for_all (fun (v, _) -> ctx.ask (Queries.IsHeapVar v)) a - | _ -> false + | _ -> + M.warn "Pointer %a has a points-to set of top. An invalid memory access might occur" d_exp ptr; + false let get_size_of_ptr_target ctx ptr = if points_to_heap_only ctx ptr then @@ -124,7 +126,7 @@ struct ) x xs end | _ -> - M.warn "Pointer %a has a points-to-set of top. An invalid memory access might occur" d_exp ptr; + M.warn "Pointer %a has a points-to set of top. An invalid memory access might occur" d_exp ptr; VDQ.ID.top () let get_ptr_deref_type ptr_typ = @@ -163,11 +165,16 @@ struct IntDomain.IntDomTuple.add bytes_offset remaining_offset let ptr_only_has_str_addr ctx ptr = - ctx.ask (Queries.MayPointTo ptr) - |> VDQ.LS.elements - |> List.map (fun (v, o) -> ValueDomain.Addr.of_mval (v, ValueDomain.Addr.Offs.of_exp o)) - |> ValueDomain.AD.of_list - |> ValueDomain.AD.for_all (fun addr -> match addr with | StrPtr _ -> true | _ -> false) + match ctx.ask (Queries.MayPointTo ptr) with + | a when not (VDQ.LS.is_top a) -> + VDQ.LS.elements a + |> List.map (fun (v, o) -> ValueDomain.Addr.of_mval (v, ValueDomain.Addr.Offs.of_exp o)) + |> ValueDomain.AD.of_list + |> ValueDomain.AD.for_all (fun addr -> match addr with | StrPtr _ -> true | _ -> false) + | _ -> + M.warn "Pointer %a has a points-to set of top. An invalid memory access might occur" d_exp ptr; + (* Intuition: if the points-to set is top, then we don't know with certainty if there are only string addresses inside *) + false let rec get_addr_offs ctx ptr = match ctx.ask (Queries.MayPointTo ptr) with @@ -203,10 +210,8 @@ struct IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind () end | _ -> - ( - set_mem_safety_flag InvalidDeref; - M.warn "Pointer %a has a points-to-set of top. An invalid memory access might occur" d_exp ptr - ); + set_mem_safety_flag InvalidDeref; + M.warn "Pointer %a has a points-to set of top. An invalid memory access might occur" d_exp ptr; IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind () and check_lval_for_oob_access ctx ?(is_implicitly_derefed = false) lval = From 0193efad541bb0ba9d3ee79b3b8cf1dabd9aff05 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 10 Sep 2023 12:59:02 +0200 Subject: [PATCH 0573/1312] handle `bitand` in congruences in restricted cases --- src/cdomains/intDomain.ml | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/cdomains/intDomain.ml b/src/cdomains/intDomain.ml index 5417598360..c78e66e2bf 100644 --- a/src/cdomains/intDomain.ml +++ b/src/cdomains/intDomain.ml @@ -514,7 +514,7 @@ module Size = struct (* size in bits as int, range as int64 *) let cast t x = (* TODO: overflow is implementation-dependent! *) if t = IBool then - (* C11 6.3.1.2 Boolean type *) + (* C11 6.3.1.2 Boolean type *) if Z.equal x Z.zero then Z.zero else Z.one else let a,b = range t in @@ -1978,7 +1978,7 @@ struct let top_of ik = `Excluded (S.empty (), size ik) let cast_to ?torg ?no_ov ik = function | `Excluded (s,r) -> - let r' = size ik in + let r' = size ik in if R.leq r r' then (* upcast -> no change *) `Excluded (s, r) else if ik = IBool then (* downcast to bool *) @@ -1986,7 +1986,7 @@ struct `Definite (BI.one) else `Excluded (S.empty(), r') - else + else (* downcast: may overflow *) (* let s' = S.map (BigInt.cast_to ik) s in *) (* We want to filter out all i in s' where (t)x with x in r could be i. *) @@ -3134,7 +3134,7 @@ struct (** The implementation of the bit operations could be improved based on the master’s thesis 'Abstract Interpretation and Abstract Domains' written by Stefan Bygde. - see: https://www.dsi.unive.it/~avp/domains.pdf *) + see: http://www.es.mdh.se/pdf_publications/948.pdf *) let bit2 f ik x y = match x, y with | None, None -> None | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) @@ -3144,7 +3144,19 @@ struct let bitor ik x y = bit2 Ints_t.bitor ik x y - let bitand ik x y = bit2 Ints_t.bitand ik x y + let bitand ik x y = match x, y with + | None, None -> None + | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) + | Some (c, m), Some (c', m') -> + if (m =: Ints_t.zero && m' =: Ints_t.zero) then + (* both arguments constant *) + Some (Ints_t.bitand c c', Ints_t.zero) + else if m' =: Ints_t.zero && c' =: Ints_t.one && Ints_t.rem m (Ints_t.of_int 2) =: Ints_t.zero then + (* x & 1 and x == c (mod 2*z) *) + (* Value is equal to LSB of c *) + Some (Ints_t.bitand c c', Ints_t.zero) + else + top () let bitxor ik x y = bit2 Ints_t.bitxor ik x y From 4d18300af5f6ae1b04bdbf29b29775af07fbc96c Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 10 Sep 2023 13:53:37 +0200 Subject: [PATCH 0574/1312] Handle `bitand` in intervals and def_exc; Add test --- src/analyses/baseInvariant.ml | 26 ++++++++++---- src/cdomains/intDomain.ml | 36 +++++++++++++++++-- tests/regression/37-congruence/13-bitand.c | 40 ++++++++++++++++++++++ 3 files changed, 93 insertions(+), 9 deletions(-) create mode 100644 tests/regression/37-congruence/13-bitand.c diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index 096d50b89b..70c6ed9101 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -410,6 +410,18 @@ struct meet_bin c c else a, b + | BAnd as op -> + (* we only attempt to refine a here *) + let a = + match ID.to_int b with + | Some x when BI.equal x BI.one -> + (match ID.to_bool c with + | Some true -> ID.meet a (ID.of_congruence ikind (Z.one, Z.of_int 2)) + | Some false -> ID.meet a (ID.of_congruence ikind (Z.zero, Z.of_int 2)) + | None -> if M.tracing then M.tracel "inv" "Unhandled case for operator x %a 1 = %a\n" d_binop op ID.pretty c; a) + | _ -> if M.tracing then M.tracel "inv" "Unhandled case for operator x %a %a = %a\n" d_binop op ID.pretty b ID.pretty c; a + in + a, b | op -> if M.tracing then M.tracel "inv" "Unhandled operator %a\n" d_binop op; a, b @@ -545,8 +557,8 @@ struct in let eval e st = eval_rv a gs st e in let eval_bool e st = match eval e st with Int i -> ID.to_bool i | _ -> None in - let unroll_fk_of_exp e = - match unrollType (Cilfacade.typeOf e) with + let unroll_fk_of_exp e = + match unrollType (Cilfacade.typeOf e) with | TFloat (fk, _) -> fk | _ -> failwith "value which was expected to be a float is of different type?!" in @@ -700,8 +712,8 @@ struct begin match t with | TInt (ik, _) -> begin match x with - | ((Var v), offs) -> - if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs))); + | ((Var v), offs) -> + if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs))); let tv_opt = ID.to_bool c in begin match tv_opt with | Some tv -> @@ -735,12 +747,12 @@ struct begin match t with | TFloat (fk, _) -> begin match x with - | ((Var v), offs) -> - if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs))); + | ((Var v), offs) -> + if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs))); begin match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs)) with | `Lifted (Ceil (ret_fk, xFloat)) -> inv_exp (Float (FD.inv_ceil (FD.cast_to ret_fk c))) xFloat st | `Lifted (Floor (ret_fk, xFloat)) -> inv_exp (Float (FD.inv_floor (FD.cast_to ret_fk c))) xFloat st - | `Lifted (Fabs (ret_fk, xFloat)) -> + | `Lifted (Fabs (ret_fk, xFloat)) -> let inv = FD.inv_fabs (FD.cast_to ret_fk c) in if FD.is_bot inv then raise Analyses.Deadcode diff --git a/src/cdomains/intDomain.ml b/src/cdomains/intDomain.ml index c78e66e2bf..312f5d7540 100644 --- a/src/cdomains/intDomain.ml +++ b/src/cdomains/intDomain.ml @@ -826,7 +826,18 @@ struct | _ -> (top_of ik,{underflow=true; overflow=true}) let bitxor = bit (fun _ik -> Ints_t.bitxor) - let bitand = bit (fun _ik -> Ints_t.bitand) + + let bitand ik i1 i2 = + match is_bot i1, is_bot i2 with + | true, true -> bot_of ik + | true, _ + | _ , true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show i1) (show i2))) + | _ -> + match to_int i1, to_int i2 with + | Some x, Some y -> (try of_int ik (Ints_t.bitand x y) |> fst with Division_by_zero -> top_of ik) + | _, Some y when Ints_t.equal y Ints_t.one -> of_interval ik (Ints_t.zero, Ints_t.one) |> fst + | _ -> top_of ik + let bitor = bit (fun _ik -> Ints_t.bitor) let bit1 f ik i1 = @@ -2282,7 +2293,28 @@ struct let ge ik x y = le ik y x let bitnot = lift1 BigInt.bitnot - let bitand = lift2 BigInt.bitand + + let bitand ik x y = norm ik (match x,y with + (* We don't bother with exclusion sets: *) + | `Excluded _, `Definite i -> + (* Except in two special cases *) + if BigInt.equal i BigInt.zero then + `Definite BigInt.zero + else if BigInt.equal i BigInt.one then + of_interval IBool (BigInt.zero, BigInt.one) + else + top () + | `Definite _, `Excluded _ + | `Excluded _, `Excluded _ -> top () + (* The good case: *) + | `Definite x, `Definite y -> + (try `Definite (BigInt.bitand x y) with | Division_by_zero -> top ()) + | `Bot, `Bot -> `Bot + | _ -> + (* If only one of them is bottom, we raise an exception that eval_rv will catch *) + raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y)))) + + let bitor = lift2 BigInt.bitor let bitxor = lift2 BigInt.bitxor diff --git a/tests/regression/37-congruence/13-bitand.c b/tests/regression/37-congruence/13-bitand.c new file mode 100644 index 0000000000..c785e722c1 --- /dev/null +++ b/tests/regression/37-congruence/13-bitand.c @@ -0,0 +1,40 @@ +// PARAM: --enable ana.int.congruence --set sem.int.signed_overflow assume_none +#include + +int main() +{ + // Assuming modulo expression + + long x; + __goblint_assume(x % 2 == 1); + __goblint_check(x % 2 == 1); + __goblint_check(x & 1); + + long y; + __goblint_assume(y % 2 == 0); + __goblint_check(y % 2 == 0); + __goblint_check(y & 1); //FAIL + + long z; + __goblint_check(z & 1); //UNKNOWN! + __goblint_assume(z % 8 == 1); + __goblint_check(z & 1); + + long xz; + __goblint_assume(xz % 3 == 1); + __goblint_check(xz & 1); //UNKNOWN! + __goblint_assume(xz % 6 == 1); + __goblint_check(xz & 1); + + // Assuming bitwise expression + + long a; + __goblint_assume(a & 1); + __goblint_check(a % 2 == 1); + __goblint_check(a & 1); + + int b; + __goblint_assume(b & 1); + __goblint_check(b % 2 == 1); + __goblint_check(b & 1); +} From 29f13a281aa183722b375c163d427c434d70dcbd Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 10 Sep 2023 14:06:34 +0200 Subject: [PATCH 0575/1312] Simplify string address check and remove extra warning --- src/analyses/memOutOfBounds.ml | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 781c48d8e9..de18021278 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -88,9 +88,7 @@ struct match ctx.ask (Queries.MayPointTo ptr) with | a when not (Queries.LS.is_top a) && not (Queries.LS.mem (dummyFunDec.svar, `NoOffset) a) -> Queries.LS.for_all (fun (v, _) -> ctx.ask (Queries.IsHeapVar v)) a - | _ -> - M.warn "Pointer %a has a points-to set of top. An invalid memory access might occur" d_exp ptr; - false + | _ -> false let get_size_of_ptr_target ctx ptr = if points_to_heap_only ctx ptr then @@ -165,16 +163,14 @@ struct IntDomain.IntDomTuple.add bytes_offset remaining_offset let ptr_only_has_str_addr ctx ptr = - match ctx.ask (Queries.MayPointTo ptr) with - | a when not (VDQ.LS.is_top a) -> - VDQ.LS.elements a - |> List.map (fun (v, o) -> ValueDomain.Addr.of_mval (v, ValueDomain.Addr.Offs.of_exp o)) - |> ValueDomain.AD.of_list - |> ValueDomain.AD.for_all (fun addr -> match addr with | StrPtr _ -> true | _ -> false) - | _ -> - M.warn "Pointer %a has a points-to set of top. An invalid memory access might occur" d_exp ptr; - (* Intuition: if the points-to set is top, then we don't know with certainty if there are only string addresses inside *) - false + match ctx.ask (Queries.EvalValue ptr) with + | a when not (Queries.VD.is_top a) -> + begin match a with + | Address a -> ValueDomain.AD.for_all (fun addr -> match addr with | StrPtr _ -> true | _ -> false) a + | _ -> false + end + (* Intuition: if ptr evaluates to top, it could all sorts of things and not only string addresses *) + | _ -> false let rec get_addr_offs ctx ptr = match ctx.ask (Queries.MayPointTo ptr) with From 7bdce29722c52c4366f0ff7457ad2b6c12f2ebeb Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 10 Sep 2023 14:17:25 +0200 Subject: [PATCH 0576/1312] Add check for dereferencing a pointer containing an unknown address --- src/analyses/memOutOfBounds.ml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index de18021278..9f9708335f 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -162,6 +162,22 @@ struct let remaining_offset = offs_to_idx typ o in IntDomain.IntDomTuple.add bytes_offset remaining_offset + let check_unknown_addr_deref ctx ptr = + let may_contain_unknown_addr = + match ctx.ask (Queries.EvalValue ptr) with + | a when not (Queries.VD.is_top a) -> + begin match a with + | Address a -> ValueDomain.AD.may_be_unknown a + | _ -> false + end + (* Intuition: if ptr evaluates to top, it could potentially evaluate to the unknown address *) + | _ -> true + in + if may_contain_unknown_addr then begin + set_mem_safety_flag InvalidDeref; + M.warn ~category:(Behavior (Undefined Other)) "Pointer %a contains an unknown address. Invalid dereference may occur" d_exp ptr + end + let ptr_only_has_str_addr ctx ptr = match ctx.ask (Queries.EvalValue ptr) with | a when not (Queries.VD.is_top a) -> @@ -230,6 +246,7 @@ struct end and check_no_binop_deref ctx lval_exp = + check_unknown_addr_deref ctx lval_exp; let behavior = Undefined MemoryOutOfBoundsAccess in let cwe_number = 823 in let ptr_size = get_size_of_ptr_target ctx lval_exp in @@ -276,6 +293,7 @@ struct | AddrOf lval -> check_lval_for_oob_access ctx ~is_implicitly_derefed lval and check_binop_exp ctx binop e1 e2 t = + check_unknown_addr_deref ctx e1; let binopexp = BinOp (binop, e1, e2, t) in let behavior = Undefined MemoryOutOfBoundsAccess in let cwe_number = 823 in From d3ec6172b6f9f8060e0dadcbee439f4b29d00a9b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 11 Sep 2023 11:49:16 +0300 Subject: [PATCH 0577/1312] Add missing thread-unsafe functions to LibraryFunctions (closes #723) --- src/analyses/libraryFunctions.ml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 5dc311a587..c4378f6e14 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -111,7 +111,10 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("difftime", unknown [drop "time1" []; drop "time2" []]); ("system", unknown ~attrs:[ThreadUnsafe] [drop "command" [r]]); ("wcscat", unknown [drop "dest" [r; w]; drop "src" [r]]); + ("wctomb", unknown ~attrs:[ThreadUnsafe] [drop "s" [w]; drop "wc" []]); ("wcrtomb", unknown ~attrs:[ThreadUnsafe] [drop "s" [w]; drop "wc" []; drop "ps" [r_deep; w_deep]]); + ("wcstombs", unknown ~attrs:[ThreadUnsafe] [drop "dst" [w]; drop "src" [r]; drop "size" []]); + ("wcsrtombs", unknown ~attrs:[ThreadUnsafe] [drop "dst" [w]; drop "src" [r_deep; w]; drop "size" []; drop "ps" [r_deep; w_deep]]); ("abs", unknown [drop "j" []]); ("localtime_r", unknown [drop "timep" [r]; drop "result" [w]]); ("strpbrk", unknown [drop "s" [r]; drop "accept" [r]]); @@ -142,9 +145,11 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("dlerror", unknown ~attrs:[ThreadUnsafe] []); ("drand48", unknown ~attrs:[ThreadUnsafe] []); ("encrypt", unknown ~attrs:[ThreadUnsafe] [drop "block" [r; w]; drop "edflag" []]); + ("setkey", unknown ~attrs:[ThreadUnsafe] [drop "key" [r]]); ("endgrent", unknown ~attrs:[ThreadUnsafe] []); ("endpwent", unknown ~attrs:[ThreadUnsafe] []); ("fcvt", unknown ~attrs:[ThreadUnsafe] [drop "number" []; drop "ndigits" []; drop "decpt" [w]; drop "sign" [w]]); + ("ecvt", unknown ~attrs:[ThreadUnsafe] [drop "number" []; drop "ndigits" []; drop "decpt" [w]; drop "sign" [w]]); ("gcvt", unknown ~attrs:[ThreadUnsafe] [drop "number" []; drop "ndigit" []; drop "buf" [w]]); ("getdate", unknown ~attrs:[ThreadUnsafe] [drop "string" [r]]); ("getenv", unknown ~attrs:[ThreadUnsafe] [drop "name" [r]]); @@ -279,6 +284,8 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__sigsetjmp", special [__ "env" [w]; drop "savesigs" []] @@ fun env -> Setjmp { env }); (* has two underscores *) ("sigsetjmp", special [__ "env" [w]; drop "savesigs" []] @@ fun env -> Setjmp { env }); ("siglongjmp", special [__ "env" [r]; __ "value" []] @@ fun env value -> Longjmp { env; value }); + ("ftw", unknown ~attrs:[ThreadUnsafe] [drop "dirpath" [r]; drop "fn" [s]; drop "nopenfd" []]); (* TODO: use Call instead of Spawn *) + ("nftw", unknown ~attrs:[ThreadUnsafe] [drop "dirpath" [r]; drop "fn" [s]; drop "nopenfd" []; drop "flags" []]); (* TODO: use Call instead of Spawn *) ] (** Pthread functions. *) From e40255bdaca9bebe56ccde7f77ded60c25af9023 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 11 Sep 2023 12:52:13 +0200 Subject: [PATCH 0578/1312] Add special case for &0 in the interval domain --- src/cdomains/intDomain.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cdomains/intDomain.ml b/src/cdomains/intDomain.ml index 312f5d7540..f1ed546e95 100644 --- a/src/cdomains/intDomain.ml +++ b/src/cdomains/intDomain.ml @@ -835,7 +835,8 @@ struct | _ -> match to_int i1, to_int i2 with | Some x, Some y -> (try of_int ik (Ints_t.bitand x y) |> fst with Division_by_zero -> top_of ik) - | _, Some y when Ints_t.equal y Ints_t.one -> of_interval ik (Ints_t.zero, Ints_t.one) |> fst + | _, Some y when Ints_t.equal y Ints_t.zero -> of_int ik Ints_t.zero |> fst + | _, Some y when Ints_t.equal y Ints_t.one -> of_interval ik (Ints_t.zero, Ints_t.one) |> fst | _ -> top_of ik let bitor = bit (fun _ik -> Ints_t.bitor) From f2b002d664cab336f91b4032658de237168092ce Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 11 Sep 2023 14:31:01 +0300 Subject: [PATCH 0579/1312] Add CHANGELOG for v2.2.0 --- CHANGELOG.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a9531a5766..a126514852 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,27 @@ +## v2.2.0 +* TODO OCaml 5 (#1003, #1137). +* TODO Library #1138 +* TODO refactor race #1136 + +* Add `setjmp`/`longjmp` analysis (#887, #970, #1015, #1019). +* Refactor race analysis to lazy distribution (#1084, #1089, #1016). +* Add thread-unsafe library function call analysis (#723, #1082). +* Add mutex type analysis and mutex API analysis (#800, #839, #1073). +* Add interval set domain and string literals domain (#901, #966, #994, #1048). +* Add affine equalities analysis (#592). +* Add use-after-free analysis (#1050, #1114). +* Add dead code elimination transformation (#850, #979). +* Add taint analysis for partial contexts (#553, #952). +* Add YAML witness validation via unassume (#796, #977, #1044, #1045, #1124). +* Add incremental analysis rename detection (#774, #777). +* Fix address sets unsoundness (#822, #967, #564, #1032, #998, #1031). +* Fix thread escape analysis unsoundness (#939, #984, #1074, #1078). +* Fix many incremental analysis issues (#627, #836, #835, #841, #932, #678, #942, #949, #950, #957, #955, #954, #960, #959, #1004, #558, #1010, #1091). +* Fix server mode for abstract debugging (#983, #990, #997, #1000, #1001, #1013, #1018, #1017, #1026, #1027). +* Add documentation for configuration JSON schema and OCaml API (#999, #1054, #1055, #1053). +* Add many library function specifications (#962, #996, #1028, #1079, #1121, #1135). +* Add OCaml 5.0 support (#945). + ## v2.1.0 Functionally equivalent to Goblint in SV-COMP 2023. From 1128aba94f87b95031bef9929dea7294c78b880f Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 11 Sep 2023 15:30:40 +0300 Subject: [PATCH 0580/1312] Fix deprecated File.exists? in update_suite.rb --- scripts/update_suite.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/update_suite.rb b/scripts/update_suite.rb index aeac526987..ca408a513a 100755 --- a/scripts/update_suite.rb +++ b/scripts/update_suite.rb @@ -41,7 +41,7 @@ def clearline $goblint = File.join(Dir.getwd,"goblint") goblintbyte = File.join(Dir.getwd,"goblint.byte") -if File.exists?(goblintbyte) then +if File.exist?(goblintbyte) then puts "Running the byte-code version! Continue? (y/n)" exit unless $stdin.gets()[0] == 'y' $goblint = goblintbyte @@ -50,11 +50,11 @@ def clearline end $vrsn = `#{$goblint} --version` -if not File.exists? "linux-headers" then +if not File.exist? "linux-headers" then puts "Missing linux-headers, will download now!" `make headers` end -has_linux_headers = File.exists? "linux-headers" # skip kernel tests if make headers failed (e.g. on opam-repository opam-ci where network is forbidden) +has_linux_headers = File.exist? "linux-headers" # skip kernel tests if make headers failed (e.g. on opam-repository opam-ci where network is forbidden) #Command line parameters #Either only run a single test, or From fd092e36fe2927adc284af3f37d900aa3ab1f763 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= Date: Mon, 11 Sep 2023 15:23:23 +0100 Subject: [PATCH 0581/1312] goblint: fix lower bound on yaml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See build failure at https://opam.ci.ocaml.org/github/ocaml/opam-repository/commit/b49e6de20d0a04ef393c2e153d87d6e00f63c455 'opam-ci' would otherwise try 'yaml.1.0.0' which doesn't work: ``` <><> Error report <><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><> +- The following actions failed | - build goblint 2.1.0 +- ``` Signed-off-by: Edwin Török --- dune-project | 2 +- goblint.opam | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dune-project b/dune-project index 1da7492d7d..54ed811ddf 100644 --- a/dune-project +++ b/dune-project @@ -42,7 +42,7 @@ (sha (>= 1.12)) cpu arg-complete - yaml + (yaml (>= 3.0.0)) uuidm catapult catapult-file diff --git a/goblint.opam b/goblint.opam index ce7df4a3b0..04e4b27f9d 100644 --- a/goblint.opam +++ b/goblint.opam @@ -39,7 +39,7 @@ depends: [ "sha" {>= "1.12"} "cpu" "arg-complete" - "yaml" + "yaml" {>= "3.0.0"} "uuidm" "catapult" "catapult-file" From c8cc6c47cde0cefc4f78459e3a4a42f0d10b8311 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 11 Sep 2023 17:24:13 +0300 Subject: [PATCH 0582/1312] Add TODOs about AddressDomain queries usage --- src/analyses/condVars.ml | 2 +- src/analyses/malloc_null.ml | 2 +- src/analyses/memLeak.ml | 2 +- src/analyses/mutexTypeAnalysis.ml | 4 ++-- src/analyses/poisonVariables.ml | 2 +- src/analyses/taintPartialContexts.ml | 2 +- src/analyses/uninit.ml | 2 +- src/analyses/varEq.ml | 4 ++-- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/analyses/condVars.ml b/src/analyses/condVars.ml index 04246bf28a..3d07520576 100644 --- a/src/analyses/condVars.ml +++ b/src/analyses/condVars.ml @@ -72,7 +72,7 @@ struct ) else ad in List.filter_map (function - | ValueDomain.Addr.Addr (v,o) -> Some (v, ValueDomain.Addr.Offs.to_exp o) + | ValueDomain.Addr.Addr (v,o) -> Some (v, ValueDomain.Addr.Offs.to_exp o) (* TODO: use unconverted addrs in domain? *) | _ -> None ) (Queries.AD.elements a') | _ -> [] diff --git a/src/analyses/malloc_null.ml b/src/analyses/malloc_null.ml index dca158c973..55d6a49bcb 100644 --- a/src/analyses/malloc_null.ml +++ b/src/analyses/malloc_null.ml @@ -99,7 +99,7 @@ struct | ad when not (Queries.AD.is_top ad) -> let to_extra addr ads = match addr with - | Queries.AD.Addr.Addr mval -> AD.of_mval mval :: ads + | Queries.AD.Addr.Addr mval -> AD.of_mval mval :: ads (* TODO: why list of singleton AD-s? *) | _ -> ads in Queries.AD.fold to_extra ad [] diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index 0bc2196aff..4f27cfea69 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -57,7 +57,7 @@ struct | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsMultiple v) -> D.singleton v | _ -> D.empty () in - D.diff state unique_pointed_to_heap_vars + D.diff state unique_pointed_to_heap_vars (* TODO: use D.remove instead of diffing singleton *) | _ -> state end | Abort -> diff --git a/src/analyses/mutexTypeAnalysis.ml b/src/analyses/mutexTypeAnalysis.ml index 4d9de5f9c4..806c35f464 100644 --- a/src/analyses/mutexTypeAnalysis.ml +++ b/src/analyses/mutexTypeAnalysis.ml @@ -18,7 +18,7 @@ struct module O = Offset.Unit module V = struct - include Printable.Prod(CilType.Varinfo)(O) + include Printable.Prod(CilType.Varinfo)(O) (* TODO: use Mval.Unit *) let is_write_only _ = false end @@ -56,7 +56,7 @@ struct let attr = ctx.ask (Queries.EvalMutexAttr attr) in let mutexes = ctx.ask (Queries.MayPointTo mutex) in (* It is correct to iter over these sets here, as mutexes need to be intialized before being used, and an analysis that detects usage before initialization is a different analysis. *) - Queries.AD.iter (function addr -> + Queries.AD.iter (function addr -> match addr with | Queries.AD.Addr.Addr (v,o) -> ctx.sideg (v,O.of_offs o) attr | _ -> () diff --git a/src/analyses/poisonVariables.ml b/src/analyses/poisonVariables.ml index f69e5b2fbc..1bd4b6d544 100644 --- a/src/analyses/poisonVariables.ml +++ b/src/analyses/poisonVariables.ml @@ -103,7 +103,7 @@ struct | ad -> Queries.AD.fold (fun addr vs -> match addr with - | Queries.AD.Addr.Addr (v,o) -> rem_mval vs (v, ValueDomain.Offs.to_exp o) + | Queries.AD.Addr.Addr (v,o) -> rem_mval vs (v, ValueDomain.Offs.to_exp o) (* TODO: use unconverted addrs in domain? *) | _ -> vs ) ad ctx.local end diff --git a/src/analyses/taintPartialContexts.ml b/src/analyses/taintPartialContexts.ml index d4ea17d2e0..d053cd103b 100644 --- a/src/analyses/taintPartialContexts.ml +++ b/src/analyses/taintPartialContexts.ml @@ -12,7 +12,7 @@ let to_mvals ad = (* TODO: should one handle ad with unknown pointers separately like in (all) other analyses? *) Queries.AD.fold (fun addr mvals -> match addr with - | Queries.AD.Addr.Addr (v,o) -> D.add (v, ValueDomain.Offs.to_exp o) mvals + | Queries.AD.Addr.Addr (v,o) -> D.add (v, ValueDomain.Offs.to_exp o) mvals (* TODO: use unconverted addrs in domain? *) | _ -> mvals ) ad (D.empty ()) diff --git a/src/analyses/uninit.ml b/src/analyses/uninit.ml index 9bd8db2fe5..887035663d 100644 --- a/src/analyses/uninit.ml +++ b/src/analyses/uninit.ml @@ -198,7 +198,7 @@ struct | ad when not (Queries.AD.is_top ad) -> let to_extra ad ads = match ad with - | Queries.AD.Addr.Addr mval -> AD.of_mval mval :: ads + | Queries.AD.Addr.Addr mval -> AD.of_mval mval :: ads (* TODO: why list of singleton AD-s? *) | _ -> ads in Queries.AD.fold to_extra ad [] diff --git a/src/analyses/varEq.ml b/src/analyses/varEq.ml index 77823d99d7..bd7d23ab8b 100644 --- a/src/analyses/varEq.ml +++ b/src/analyses/varEq.ml @@ -258,7 +258,7 @@ struct if Queries.AD.is_top aad then false else Queries.AD.exists (function - | Addr (u,s) -> CilType.Varinfo.equal v u && oleq o (Addr.Offs.to_exp s) + | Addr (u,s) -> CilType.Varinfo.equal v u && oleq o (Addr.Offs.to_exp s) (* TODO: avoid conversion? *) | _ -> false ) aad in @@ -298,7 +298,7 @@ struct else if Queries.AD.is_top bad then ((*Messages.warn ~category:Analyzer "No PT-set: switching to types ";*) type_may_change_apt a ) else Queries.AD.exists (function - | Addr (v,o) -> lval_may_change_pt a (v, Addr.Offs.to_exp o) + | Addr (v,o) -> lval_may_change_pt a (v, Addr.Offs.to_exp o) (* TODO: avoid conversion? *) | _ -> false ) bad in From 7edb312254bdbe221f72c57c16ca8a7eacac8263 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 11 Sep 2023 17:29:23 +0300 Subject: [PATCH 0583/1312] Simplify some AddressDomain query result manipulation --- src/analyses/apron/relationAnalysis.apron.ml | 2 +- src/analyses/base.ml | 4 ++-- src/analyses/condVars.ml | 22 +++++++++----------- src/analyses/extractPthread.ml | 21 +++++-------------- src/analyses/pthreadSignals.ml | 6 ++---- src/analyses/useAfterFree.ml | 11 ++-------- 6 files changed, 22 insertions(+), 44 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index ca60e5dc30..46c620f390 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -276,7 +276,7 @@ struct let reachable_from_args ctx args = let to_vs e = ctx.ask (ReachableFrom e) - |> LockDomain.MayLocksetNoRW.to_var_may + |> Queries.AD.to_var_may |> VS.of_list in List.fold (fun vs e -> VS.join vs (to_vs e)) (VS.empty ()) args diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 4183261ddb..e59d8a2fc6 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1319,8 +1319,8 @@ struct (* ignore @@ printf "EvalStr Address: %a -> %s (must %i, may %i)\n" d_plainexp e (VD.short 80 (Address a)) (List.length @@ AD.to_var_must a) (List.length @@ AD.to_var_may a); *) begin match unrollType (Cilfacade.typeOf e) with | TPtr(TInt(IChar, _), _) -> - let (v,o) = List.hd (AD.to_mval a) in - let lval = Mval.Exp.to_cil @@ (v, Addr.Offs.to_exp o) in + let mval = List.hd (AD.to_mval a) in + let lval = Addr.Mval.to_cil mval in (try `Lifted (Bytes.to_string (Hashtbl.find char_array lval)) with Not_found -> Queries.Result.top q) | _ -> (* what about ISChar and IUChar? *) diff --git a/src/analyses/condVars.ml b/src/analyses/condVars.ml index 3d07520576..04b148dd02 100644 --- a/src/analyses/condVars.ml +++ b/src/analyses/condVars.ml @@ -64,18 +64,16 @@ struct let (>?) = Option.bind let mayPointTo ctx exp = - match ctx.ask (Queries.MayPointTo exp) with - | ad when not (Queries.AD.is_top ad) && not (Queries.AD.is_empty ad) -> - let a' = if Queries.AD.mem UnknownPtr ad then ( - M.info ~category:Unsound "mayPointTo: query result for %a contains TOP!" d_exp exp; (* UNSOUND *) - Queries.AD.remove UnknownPtr ad - ) else ad - in - List.filter_map (function - | ValueDomain.Addr.Addr (v,o) -> Some (v, ValueDomain.Addr.Offs.to_exp o) (* TODO: use unconverted addrs in domain? *) - | _ -> None - ) (Queries.AD.elements a') - | _ -> [] + let ad = ctx.ask (Queries.MayPointTo exp) in + let a' = if Queries.AD.mem UnknownPtr ad then ( + M.info ~category:Unsound "mayPointTo: query result for %a contains TOP!" d_exp exp; (* UNSOUND *) + Queries.AD.remove UnknownPtr ad + ) else ad + in + List.filter_map (function + | ValueDomain.Addr.Addr (v,o) -> Some (v, ValueDomain.Addr.Offs.to_exp o) (* TODO: use unconverted addrs in domain? *) + | _ -> None + ) (Queries.AD.elements a') let mustPointTo ctx exp = (* this is just to get Mval.Exp *) match mayPointTo ctx exp with diff --git a/src/analyses/extractPthread.ml b/src/analyses/extractPthread.ml index 211b88fd6e..74657c7468 100644 --- a/src/analyses/extractPthread.ml +++ b/src/analyses/extractPthread.ml @@ -877,16 +877,9 @@ module Spec : Analyses.MCPSpec = struct module ExprEval = struct let eval_ptr ctx exp = - let ad = ctx.ask (Queries.MayPointTo exp) in - if (not (Queries.AD.is_top ad)) && not (Queries.AD.is_empty ad) then - if Queries.AD.mem UnknownPtr ad - then (* UNSOUND *) - Queries.AD.remove UnknownPtr ad - |> Queries.AD.to_var_may - else - Queries.AD.to_var_may ad - else - [] + ctx.ask (Queries.MayPointTo exp) + |> Queries.AD.remove UnknownPtr + |> Queries.AD.to_var_may let eval_var ctx exp = match exp with @@ -1126,11 +1119,7 @@ module Spec : Analyses.MCPSpec = struct ad in let thread_fun = - Queries.AD.fold (fun addr vars -> - match addr with - | Queries.AD.Addr.Addr (v,_) -> v :: vars - | _ -> vars - ) funs_ad [] + Queries.AD.to_var_may funs_ad |> List.unique ~eq:(fun a b -> a.vid = b.vid) |> List.hd in @@ -1255,7 +1244,7 @@ module Spec : Analyses.MCPSpec = struct (* TODO: optimize finding *) let tasks_f = let var_in_ad ad f = Queries.AD.exists (function - | Queries.AD.Addr.Addr (ls_f,_) -> ls_f = f + | Queries.AD.Addr.Addr (ls_f,_) -> CilType.Varinfo.equal ls_f f | _ -> false ) ad in diff --git a/src/analyses/pthreadSignals.ml b/src/analyses/pthreadSignals.ml index d515820514..0b776282e8 100644 --- a/src/analyses/pthreadSignals.ml +++ b/src/analyses/pthreadSignals.ml @@ -17,10 +17,8 @@ struct module C = MustSignals module G = SetDomain.ToppedSet (MHP) (struct let topname = "All Threads" end) - let eval_exp_addr (a: Queries.ask) exp = Queries.AD.elements (a.f (Queries.MayPointTo exp)) - - let possible_vinfos a cv_arg = - List.filter_map ValueDomain.Addr.to_var_may (eval_exp_addr a cv_arg) + let possible_vinfos (a: Queries.ask) cv_arg = + Queries.AD.to_var_may (a.f (Queries.MayPointTo cv_arg)) (* transfer functions *) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index f828e17e2b..0aafbd1ad4 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -160,15 +160,8 @@ struct if Queries.AD.is_top reachable_from_args || D.is_top caller_state then [caller_state, caller_state] else - let reachable_vars = - let get_vars addr vars = - match addr with - | Queries.AD.Addr.Addr (v,_) -> v :: vars - | _ -> vars - in - Queries.AD.fold get_vars reachable_from_args [] - in - let callee_state = D.filter (fun var -> List.mem var reachable_vars) caller_state in + let reachable_vars = Queries.AD.to_var_may reachable_from_args in + let callee_state = D.filter (fun var -> List.mem var reachable_vars) caller_state in (* TODO: use AD.mem directly *) [caller_state, callee_state] ) From 3e01426ccd87dd02b6519db783eff107028368f7 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 11 Sep 2023 17:29:53 +0300 Subject: [PATCH 0584/1312] Cherry-pick AddressDomain.filter from 224615f63b07448ba45afb79f42c2d8284d4b968 Required to fix Promela extraction tests. --- src/cdomains/addressDomain.ml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cdomains/addressDomain.ml b/src/cdomains/addressDomain.ml index 9f6ee56cbf..5981caf9ea 100644 --- a/src/cdomains/addressDomain.ml +++ b/src/cdomains/addressDomain.ml @@ -440,4 +440,6 @@ struct let r = narrow x y in if M.tracing then M.traceu "ad" "-> %a\n" pretty r; r + + let filter f ad = fold (fun addr ad -> if f addr then add addr ad else ad) ad (empty ()) end From 70cd4e879945ad637827953e593010e93a954e50 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 12 Sep 2023 10:36:22 +0300 Subject: [PATCH 0585/1312] Fix Calloc indentation in base --- src/analyses/base.ml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index b74555b7ad..6ab4015460 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2298,15 +2298,17 @@ struct let countval = eval_int (Analyses.ask_of_ctx ctx) gs st n in if ID.to_int countval = Some Z.one then ( set_many ~ctx (Analyses.ask_of_ctx ctx) gs st [ - add_null (AD.of_var heap_var), TVoid [], Blob (VD.bot (), sizeval, false); - eval_lv (Analyses.ask_of_ctx ctx) gs st lv, (Cilfacade.typeOfLval lv), Address (add_null (AD.of_var heap_var)) + (add_null (AD.of_var heap_var), TVoid [], Blob (VD.bot (), sizeval, false)); + (eval_lv (Analyses.ask_of_ctx ctx) gs st lv, (Cilfacade.typeOfLval lv), Address (add_null (AD.of_var heap_var))) ] ) else ( let blobsize = ID.mul (ID.cast_to ik @@ sizeval) (ID.cast_to ik @@ countval) in (* the memory that was allocated by calloc is set to bottom, but we keep track that it originated from calloc, so when bottom is read from memory allocated by calloc it is turned to zero *) - set_many ~ctx (Analyses.ask_of_ctx ctx) gs st [(add_null (AD.of_var heap_var), TVoid [], Array (CArrays.make (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) BI.one) (Blob (VD.bot (), blobsize, false)))); - (eval_lv (Analyses.ask_of_ctx ctx) gs st lv, (Cilfacade.typeOfLval lv), Address (add_null (AD.of_mval (heap_var, `Index (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) BI.zero, `NoOffset)))))] + set_many ~ctx (Analyses.ask_of_ctx ctx) gs st [ + (add_null (AD.of_var heap_var), TVoid [], Array (CArrays.make (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) BI.one) (Blob (VD.bot (), blobsize, false)))); + (eval_lv (Analyses.ask_of_ctx ctx) gs st lv, (Cilfacade.typeOfLval lv), Address (add_null (AD.of_mval (heap_var, `Index (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) BI.zero, `NoOffset))))) + ] ) | _ -> st end From dce70e2953189412c2248a67d147937e56216fd9 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 12 Sep 2023 10:59:27 +0300 Subject: [PATCH 0586/1312] Add comment about unsoundness back to extractPthread --- src/analyses/extractPthread.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/extractPthread.ml b/src/analyses/extractPthread.ml index 74657c7468..60e389fedf 100644 --- a/src/analyses/extractPthread.ml +++ b/src/analyses/extractPthread.ml @@ -878,7 +878,7 @@ module Spec : Analyses.MCPSpec = struct module ExprEval = struct let eval_ptr ctx exp = ctx.ask (Queries.MayPointTo exp) - |> Queries.AD.remove UnknownPtr + |> Queries.AD.remove UnknownPtr (* UNSOUND *) |> Queries.AD.to_var_may let eval_var ctx exp = From 0568edd8bdc6218354b6793d0b90c76d9014c882 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 12 Sep 2023 11:56:44 +0300 Subject: [PATCH 0587/1312] Simplify do_exp in uninit and malloc_null --- src/analyses/malloc_null.ml | 12 ++++++------ src/analyses/uninit.ml | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/analyses/malloc_null.ml b/src/analyses/malloc_null.ml index 55d6a49bcb..354429cad6 100644 --- a/src/analyses/malloc_null.ml +++ b/src/analyses/malloc_null.ml @@ -97,16 +97,16 @@ struct let do_exp e = match ask.f (Queries.ReachableFrom e) with | ad when not (Queries.AD.is_top ad) -> - let to_extra addr ads = + let to_extra addr ad = match addr with - | Queries.AD.Addr.Addr mval -> AD.of_mval mval :: ads (* TODO: why list of singleton AD-s? *) - | _ -> ads + | Queries.AD.Addr.Addr _ -> AD.add addr ad + | _ -> ad in - Queries.AD.fold to_extra ad [] + Queries.AD.fold to_extra ad (AD.empty ()) (* Ignore soundness warnings, as invalidation proper will raise them. *) - | _ -> [] + | _ -> AD.empty () in - List.concat_map do_exp args + List.map do_exp args in let add_exploded_struct (one: AD.t) (many: AD.t) : AD.t = let vars = AD.to_var_may one in diff --git a/src/analyses/uninit.ml b/src/analyses/uninit.ml index 887035663d..56a1c7d843 100644 --- a/src/analyses/uninit.ml +++ b/src/analyses/uninit.ml @@ -196,16 +196,16 @@ struct let do_exp e = match ask.f (Queries.ReachableFrom e) with | ad when not (Queries.AD.is_top ad) -> - let to_extra ad ads = - match ad with - | Queries.AD.Addr.Addr mval -> AD.of_mval mval :: ads (* TODO: why list of singleton AD-s? *) - | _ -> ads + let to_extra addr ad = + match addr with + | Queries.AD.Addr.Addr _ -> AD.add addr ad + | _ -> ad in - Queries.AD.fold to_extra ad [] + Queries.AD.fold to_extra ad (AD.empty ()) (* Ignore soundness warnings, as invalidation proper will raise them. *) - | _ -> [] + | _ -> AD.empty () in - List.concat_map do_exp args + List.map do_exp args in let add_exploded_struct (one: AD.t) (many: AD.t) : AD.t = let vars = AD.to_var_may one in From f643bb5ad0ae3c7e4f012d91be88e1557de7bd1a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 12 Sep 2023 13:37:25 +0300 Subject: [PATCH 0588/1312] Add external benchmarking guide --- docs/user-guide/benchmarking.md | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/docs/user-guide/benchmarking.md b/docs/user-guide/benchmarking.md index 44811b61a5..5417375bdb 100644 --- a/docs/user-guide/benchmarking.md +++ b/docs/user-guide/benchmarking.md @@ -1,6 +1,31 @@ # Benchmarking +The following best practices should be followed when benchmarking Goblint. +This is to ensure valid, reproducible and representative benchmarking results. -To achieve reproducible builds and the best performance for benchmarking, it is recommended to compile Goblint using the `release` option: +# External benchmarking +External users should choose the version of Goblint to evaluate or benchmark as follows: + +1. Use the newest version release. + + The version from git `master` branch or any other intermediate git commit come without any guarantees. + They are bleeding-edge and haven't gone through validation like the version releases. + + SV-COMP releases are highly preferable since they've gone through rigorous validation in SV-COMP. + +2. Download the corresponding version from a Zenodo artifact or by checking out the respective git tag. **Do not install directly from opam repository!** + + Goblint pins optimized versions of some dependencies which cannot be done on the opam repository releases. + Thus, using the latter would yield unrepresentative results. + + Zenodo artifacts come with DOIs, which make them ideal for citation. + +3. Use OCaml 4.14. **Do not use OCaml 5!** + + OCaml 5 has significant performance regressions, which yield unrepresentative benchmarking results. + Goblint's `make setup` installs the correct OCaml version into a new opam switch. + +# Release build +To achieve the best performance for benchmarking, Goblint should be compiled using the `release` option: ```sh make release From a3eaf9aece32da7a67d65dc17bffa57dbc9b8530 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 12 Sep 2023 13:39:42 +0300 Subject: [PATCH 0589/1312] Refer to benchmarking guide in README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b03b7bbe36..4d97baa842 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ Documentation can be browsed on [Read the Docs](https://goblint.readthedocs.io/e ## Installing Both for using an up-to-date version of Goblint or developing it, the best way is to install from source by cloning this repository. +For benchmarking Goblint, please follow the [Benchmarking guide on Read the Docs](https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/). ### Linux 1. Install [opam](https://opam.ocaml.org/doc/Install.html). From cd45df53d63174ad77f05d93d34472f4cdbd639a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 12 Sep 2023 14:02:11 +0300 Subject: [PATCH 0590/1312] Add opam post-message about OCaml 5 benchmarking --- goblint.opam | 3 +++ goblint.opam.locked | 3 +++ goblint.opam.template | 3 +++ 3 files changed, 9 insertions(+) diff --git a/goblint.opam b/goblint.opam index a20df919d0..59f8a59c09 100644 --- a/goblint.opam +++ b/goblint.opam @@ -81,3 +81,6 @@ pin-depends: [ # TODO: add back after release, only pinned for CI stability [ "apron.v0.9.13" "git+https://github.com/antoinemine/apron.git#1a8e91062c0d7d1e80333d19d5a432332bbbaec8"] ] +post-messages: [ + "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} +] diff --git a/goblint.opam.locked b/goblint.opam.locked index acb49a7b14..fe5bb65a00 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -140,3 +140,6 @@ pin-depends: [ "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] ] +post-messages: [ + "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} +] diff --git a/goblint.opam.template b/goblint.opam.template index b7f5a7abff..5c13293196 100644 --- a/goblint.opam.template +++ b/goblint.opam.template @@ -8,3 +8,6 @@ pin-depends: [ # TODO: add back after release, only pinned for CI stability [ "apron.v0.9.13" "git+https://github.com/antoinemine/apron.git#1a8e91062c0d7d1e80333d19d5a432332bbbaec8"] ] +post-messages: [ + "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} +] From ee31392babcb1107763f5b5658215716c7d90dc3 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 21 Aug 2023 17:49:48 +0300 Subject: [PATCH 0591/1312] Use opam 2.0 compatible switch create (closes #1133) --- make.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/make.sh b/make.sh index 788289c5ed..af1411a8d3 100755 --- a/make.sh +++ b/make.sh @@ -8,7 +8,7 @@ opam_setup() { set -x opam init -y -a --bare $SANDBOXING # sandboxing is disabled in travis and docker opam update - opam switch -y create . --deps-only ocaml-variants.4.14.0+options ocaml-option-flambda --locked + opam switch -y create . --deps-only --packages=ocaml-variants.4.14.0+options,ocaml-option-flambda --locked } rule() { From b25cb45cca2d755d26c86dfe9529ce15b761a161 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 12 Sep 2023 14:23:02 +0300 Subject: [PATCH 0592/1312] Use OCaml 5.0 compatible Apron --- goblint.opam | 2 -- goblint.opam.locked | 12 ++++-------- goblint.opam.template | 2 -- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/goblint.opam b/goblint.opam index 59f8a59c09..794b43e770 100644 --- a/goblint.opam +++ b/goblint.opam @@ -78,8 +78,6 @@ pin-depends: [ [ "goblint-cil.2.0.1" "git+https://github.com/goblint/cil.git#4df989fe625d91ce07d94afe1d85b3b5c6cdd63e" ] # TODO: add back after release, only pinned for optimization (https://github.com/ocaml-ppx/ppx_deriving/pull/252) [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] - # TODO: add back after release, only pinned for CI stability - [ "apron.v0.9.13" "git+https://github.com/antoinemine/apron.git#1a8e91062c0d7d1e80333d19d5a432332bbbaec8"] ] post-messages: [ "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} diff --git a/goblint.opam.locked b/goblint.opam.locked index fe5bb65a00..5f81506183 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -21,7 +21,7 @@ doc: "https://goblint.readthedocs.io/en/latest/" bug-reports: "https://github.com/goblint/analyzer/issues" depends: [ "angstrom" {= "0.15.0"} - "apron" {= "v0.9.13"} + "apron" {= "v0.9.14~beta.2"} "arg-complete" {= "0.1.0"} "astring" {= "0.8.5"} "base-bigarray" {= "base"} @@ -56,22 +56,22 @@ depends: [ "dune-private-libs" {= "3.6.1"} "dune-site" {= "3.6.1"} "dyn" {= "3.6.1"} + "fileutils" {= "0.6.4"} "fmt" {= "0.9.0"} "fpath" {= "0.7.3"} "goblint-cil" {= "2.0.1"} "integers" {= "0.7.0"} "json-data-encoding" {= "0.12.1"} "jsonrpc" {= "1.15.0~5.0preview1"} - "fileutils" {= "0.6.4"} "logs" {= "0.7.0"} - "mlgmpidl" {= "1.2.14"} + "mlgmpidl" {= "1.2.15"} "num" {= "1.4"} "ocaml" {= "4.14.0"} - "ocaml-variants" {= "4.14.0+options"} "ocaml-compiler-libs" {= "v0.12.4"} "ocaml-config" {= "2"} "ocaml-option-flambda" {= "1"} "ocaml-syntax-shims" {= "1.0.0"} + "ocaml-variants" {= "4.14.0+options"} "ocamlbuild" {= "0.14.2"} "ocamlfind" {= "1.9.5"} "odoc" {= "2.2.0" & with-doc} @@ -131,10 +131,6 @@ pin-depends: [ "goblint-cil.2.0.1" "git+https://github.com/goblint/cil.git#4df989fe625d91ce07d94afe1d85b3b5c6cdd63e" ] - [ - "apron.v0.9.13" - "git+https://github.com/antoinemine/apron.git#1a8e91062c0d7d1e80333d19d5a432332bbbaec8" - ] [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" diff --git a/goblint.opam.template b/goblint.opam.template index 5c13293196..ff1364fc20 100644 --- a/goblint.opam.template +++ b/goblint.opam.template @@ -5,8 +5,6 @@ pin-depends: [ [ "goblint-cil.2.0.1" "git+https://github.com/goblint/cil.git#4df989fe625d91ce07d94afe1d85b3b5c6cdd63e" ] # TODO: add back after release, only pinned for optimization (https://github.com/ocaml-ppx/ppx_deriving/pull/252) [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] - # TODO: add back after release, only pinned for CI stability - [ "apron.v0.9.13" "git+https://github.com/antoinemine/apron.git#1a8e91062c0d7d1e80333d19d5a432332bbbaec8"] ] post-messages: [ "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} From 13062dfc37b5b1108bf44081c0c0b8363ce81083 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 12 Sep 2023 14:24:26 +0300 Subject: [PATCH 0593/1312] Add OCaml 5.0 to unlocked workflow --- .github/workflows/unlocked.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/unlocked.yml b/.github/workflows/unlocked.yml index 15cd9138d8..65d85f0b3d 100644 --- a/.github/workflows/unlocked.yml +++ b/.github/workflows/unlocked.yml @@ -18,6 +18,7 @@ jobs: - ubuntu-latest - macos-latest ocaml-compiler: + - 5.0.x - ocaml-variants.4.14.0+options,ocaml-option-flambda - 4.14.x - 4.13.x From 3ee9af873e2075824833b42f4e3bd84e197e1f35 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 12 Sep 2023 15:45:51 +0300 Subject: [PATCH 0594/1312] Simplify remove_unreachable in uninit and malloc_null --- src/analyses/malloc_null.ml | 24 ++++++++++++------------ src/analyses/uninit.ml | 24 ++++++++++++------------ 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/analyses/malloc_null.ml b/src/analyses/malloc_null.ml index 354429cad6..4d5871cb80 100644 --- a/src/analyses/malloc_null.ml +++ b/src/analyses/malloc_null.ml @@ -94,25 +94,25 @@ struct (* Remove null values from state that are unreachable from exp.*) let remove_unreachable (ask: Queries.ask) (args: exp list) (st: D.t) : D.t = let reachable = - let do_exp e = + let do_exp e a = match ask.f (Queries.ReachableFrom e) with | ad when not (Queries.AD.is_top ad) -> - let to_extra addr ad = - match addr with - | Queries.AD.Addr.Addr _ -> AD.add addr ad - | _ -> ad - in - Queries.AD.fold to_extra ad (AD.empty ()) + ad + |> Queries.AD.filter (function + | Queries.AD.Addr.Addr _ -> true + | _ -> false) + |> Queries.AD.join a (* Ignore soundness warnings, as invalidation proper will raise them. *) | _ -> AD.empty () in - List.map do_exp args + List.fold_right do_exp args (AD.empty ()) in - let add_exploded_struct (one: AD.t) (many: AD.t) : AD.t = - let vars = AD.to_var_may one in - List.fold_right AD.add (List.concat_map to_addrs vars) many + let vars = + reachable + |> AD.to_var_may + |> List.concat_map to_addrs + |> AD.of_list in - let vars = List.fold_right add_exploded_struct reachable (AD.empty ()) in if D.is_top st then D.top () else D.filter (fun x -> AD.mem x vars) st diff --git a/src/analyses/uninit.ml b/src/analyses/uninit.ml index 56a1c7d843..f8759d9134 100644 --- a/src/analyses/uninit.ml +++ b/src/analyses/uninit.ml @@ -193,25 +193,25 @@ struct let remove_unreachable (ask: Queries.ask) (args: exp list) (st: D.t) : D.t = let reachable = - let do_exp e = + let do_exp e a = match ask.f (Queries.ReachableFrom e) with | ad when not (Queries.AD.is_top ad) -> - let to_extra addr ad = - match addr with - | Queries.AD.Addr.Addr _ -> AD.add addr ad - | _ -> ad - in - Queries.AD.fold to_extra ad (AD.empty ()) + ad + |> Queries.AD.filter (function + | Queries.AD.Addr.Addr _ -> true + | _ -> false) + |> Queries.AD.join a (* Ignore soundness warnings, as invalidation proper will raise them. *) | _ -> AD.empty () in - List.map do_exp args + List.fold_right do_exp args (AD.empty ()) in - let add_exploded_struct (one: AD.t) (many: AD.t) : AD.t = - let vars = AD.to_var_may one in - List.fold_right AD.add (List.concat_map to_addrs vars) many + let vars = + reachable + |> AD.to_var_may + |> List.concat_map to_addrs + |> AD.of_list in - let vars = List.fold_right add_exploded_struct reachable (AD.empty ()) in if D.is_top st then D.top () else D.filter (fun x -> AD.mem x vars) st From 94db94deb38ffa5b880214d36c17defbf53683df Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 12 Sep 2023 14:32:45 +0300 Subject: [PATCH 0595/1312] Update Gobview to OCaml 5 compatible --- gobview | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gobview b/gobview index c3dcfaba97..70f76a1692 160000 --- a/gobview +++ b/gobview @@ -1 +1 @@ -Subproject commit c3dcfaba97a1df72f027e5dad317e2c201ce5e4b +Subproject commit 70f76a16927b14fca0ccfd3ab032076381bdc834 From bff915968251f99b5ba14ac4dfa1021f0b2c371b Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 12 Sep 2023 16:03:00 +0200 Subject: [PATCH 0596/1312] Congruences: Make % sound by restricting cases where we return a constant --- src/cdomains/intDomain.ml | 4 ++-- tests/regression/37-congruence/06-refinements.c | 5 +++-- tests/regression/37-congruence/07-refinements-o.c | 5 +++-- .../regression/37-congruence/11-overflow-signed.c | 6 +++--- tests/regression/37-congruence/13-bitand.c | 12 +++++++++--- tests/regression/37-congruence/14-negative.c | 15 +++++++++++++++ 6 files changed, 35 insertions(+), 12 deletions(-) create mode 100644 tests/regression/37-congruence/14-negative.c diff --git a/src/cdomains/intDomain.ml b/src/cdomains/intDomain.ml index f1ed546e95..748df62300 100644 --- a/src/cdomains/intDomain.ml +++ b/src/cdomains/intDomain.ml @@ -3199,8 +3199,8 @@ struct | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) | Some (c1, m1), Some(c2, m2) -> if m2 =: Ints_t.zero then - if (c2 |: m1) then - Some(c1 %: c2,Ints_t.zero) + if (c2 |: m1) && (c1 %: c2 =: Ints_t.zero || m1 =: Ints_t.zero || not (Cil.isSigned ik)) then + Some(c1 %: c2, Ints_t.zero) else normalize ik (Some(c1, (Ints_t.gcd m1 c2))) else diff --git a/tests/regression/37-congruence/06-refinements.c b/tests/regression/37-congruence/06-refinements.c index c0b7b0564c..38bf9458cc 100644 --- a/tests/regression/37-congruence/06-refinements.c +++ b/tests/regression/37-congruence/06-refinements.c @@ -5,14 +5,15 @@ int main() { int top; int i = 0; if(top % 17 == 3) { - __goblint_check(top%17 ==3); + __goblint_check(top%17 ==3); //TODO (Refine top to be positive above, and reuse information in %) if(top %17 != 3) { i = 12; } else { } } - __goblint_check(i ==0); + __goblint_check(i ==0); // TODO + i = 0; if(top % 17 == 0) { __goblint_check(top%17 == 0); diff --git a/tests/regression/37-congruence/07-refinements-o.c b/tests/regression/37-congruence/07-refinements-o.c index 44f21b7c8c..49148d6683 100644 --- a/tests/regression/37-congruence/07-refinements-o.c +++ b/tests/regression/37-congruence/07-refinements-o.c @@ -32,15 +32,16 @@ int main() { int top; int i = 0; if(top % 17 == 3) { - __goblint_check(top%17 ==3); + __goblint_check(top%17 ==3); //TODO (Refine top to be positive above, and reuse information in %) if(top %17 != 3) { i = 12; } else { } } - __goblint_check(i ==0); + __goblint_check(i ==0); //TODO + i = 0; if(top % 17 == 0) { __goblint_check(top%17 == 0); if(top %17 != 0) { diff --git a/tests/regression/37-congruence/11-overflow-signed.c b/tests/regression/37-congruence/11-overflow-signed.c index 29599fe246..031d88ce45 100644 --- a/tests/regression/37-congruence/11-overflow-signed.c +++ b/tests/regression/37-congruence/11-overflow-signed.c @@ -12,8 +12,8 @@ int basic(){ { if (b % two_pow_16 == 5) { - __goblint_check(a % two_pow_16 == 3); - __goblint_check(b % two_pow_16 == 5); + __goblint_check(a % two_pow_16 == 3); //TODO (Refine a to be positive above, and reuse information in %) + __goblint_check(b % two_pow_16 == 5); //TODO (Refine a to be positive above, and reuse information in %) unsigned int e = a * b; __goblint_check(e % two_pow_16 == 15); // UNKNOWN! @@ -35,7 +35,7 @@ int special(){ if (a % two_pow_18 == two_pow_17) { - __goblint_check(a % two_pow_18 == two_pow_17); + __goblint_check(a % two_pow_18 == two_pow_17); //TODO (Refine a to be positive above, and reuse information in %) unsigned int e = a * a; __goblint_check(e == 0); //UNKNOWN! diff --git a/tests/regression/37-congruence/13-bitand.c b/tests/regression/37-congruence/13-bitand.c index c785e722c1..500fb9d1cc 100644 --- a/tests/regression/37-congruence/13-bitand.c +++ b/tests/regression/37-congruence/13-bitand.c @@ -5,11 +5,16 @@ int main() { // Assuming modulo expression - long x; + unsigned long x; __goblint_assume(x % 2 == 1); __goblint_check(x % 2 == 1); __goblint_check(x & 1); + long xx; + __goblint_assume(xx % 2 == 1); + __goblint_check(xx % 2 == 1); //TODO (Refine xx to be positive above, and reuse information in %) + __goblint_check(xx & 1); + long y; __goblint_assume(y % 2 == 0); __goblint_check(y % 2 == 0); @@ -27,14 +32,15 @@ int main() __goblint_check(xz & 1); // Assuming bitwise expression + // Does NOT actually lead to modulo information, as negative values may also have their last bit set! long a; __goblint_assume(a & 1); - __goblint_check(a % 2 == 1); + __goblint_check(a % 2 == 1); //UNKNOWN! __goblint_check(a & 1); int b; __goblint_assume(b & 1); - __goblint_check(b % 2 == 1); + __goblint_check(b % 2 == 1); //UNKNOWN! __goblint_check(b & 1); } diff --git a/tests/regression/37-congruence/14-negative.c b/tests/regression/37-congruence/14-negative.c new file mode 100644 index 0000000000..eae8307ab1 --- /dev/null +++ b/tests/regression/37-congruence/14-negative.c @@ -0,0 +1,15 @@ +// PARAM: --enable ana.int.congruence --set sem.int.signed_overflow assume_none +#include + +int main() +{ + int top; + + int c = -5; + if (top) + { + c = -7; + } + __goblint_check(c % 2 == 1); //UNKNOWN! (Does not hold at runtime) + __goblint_check(c % 2 == -1); //TODO (Track information that c is negative) +} From c6e43c552ec54324e793954aa616b0b21b1d717f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Sep 2023 20:07:09 +0000 Subject: [PATCH 0597/1312] Bump docker/build-push-action from 4 to 5 Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 4 to 5. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v4...v5) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/docker.yml | 4 ++-- .github/workflows/unlocked.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 1ef104db29..b0f7b30f01 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -59,7 +59,7 @@ jobs: - name: Build Docker image id: build - uses: docker/build-push-action@v4 + uses: docker/build-push-action@v5 with: context: . load: true # load into docker instead of immediately pushing @@ -72,7 +72,7 @@ jobs: run: docker run --rm -v $(pwd):/data ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }} /data/tests/regression/04-mutex/01-simple_rc.c # run image by version in case multiple tags - name: Push Docker image - uses: docker/build-push-action@v4 + uses: docker/build-push-action@v5 with: context: . push: true diff --git a/.github/workflows/unlocked.yml b/.github/workflows/unlocked.yml index 15cd9138d8..f5a5ef0138 100644 --- a/.github/workflows/unlocked.yml +++ b/.github/workflows/unlocked.yml @@ -215,7 +215,7 @@ jobs: - name: Build dev Docker image id: build - uses: docker/build-push-action@v4 + uses: docker/build-push-action@v5 with: context: . target: dev From 72550593ba3b4157a0ed4ea6bde1f1974e31c1c6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Sep 2023 20:07:12 +0000 Subject: [PATCH 0598/1312] Bump docker/metadata-action from 4 to 5 Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 4 to 5. - [Release notes](https://github.com/docker/metadata-action/releases) - [Upgrade guide](https://github.com/docker/metadata-action/blob/master/UPGRADE.md) - [Commits](https://github.com/docker/metadata-action/compare/v4...v5) --- updated-dependencies: - dependency-name: docker/metadata-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 1ef104db29..40647f1335 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -49,7 +49,7 @@ jobs: - name: Extract metadata (tags, labels) for Docker id: meta - uses: docker/metadata-action@v4 + uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} tags: | From 25ce122603ca04eea4323b2448bb290cb0bc2b93 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Sep 2023 20:07:16 +0000 Subject: [PATCH 0599/1312] Bump docker/setup-buildx-action from 2 to 3 Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 2 to 3. - [Release notes](https://github.com/docker/setup-buildx-action/releases) - [Commits](https://github.com/docker/setup-buildx-action/compare/v2...v3) --- updated-dependencies: - dependency-name: docker/setup-buildx-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/docker.yml | 2 +- .github/workflows/unlocked.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 1ef104db29..716ad314bf 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -38,7 +38,7 @@ jobs: uses: actions/checkout@v4 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 # needed for GitHub Actions Cache in build-push-action + uses: docker/setup-buildx-action@v3 # needed for GitHub Actions Cache in build-push-action - name: Log in to the Container registry uses: docker/login-action@v2 diff --git a/.github/workflows/unlocked.yml b/.github/workflows/unlocked.yml index 15cd9138d8..15179fda03 100644 --- a/.github/workflows/unlocked.yml +++ b/.github/workflows/unlocked.yml @@ -211,7 +211,7 @@ jobs: uses: actions/checkout@v4 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 # needed for GitHub Actions Cache in build-push-action + uses: docker/setup-buildx-action@v3 # needed for GitHub Actions Cache in build-push-action - name: Build dev Docker image id: build From 02c839f198c48b3256aa815ade4590c3a159bf0c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Sep 2023 20:07:20 +0000 Subject: [PATCH 0600/1312] Bump docker/login-action from 2 to 3 Bumps [docker/login-action](https://github.com/docker/login-action) from 2 to 3. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/v2...v3) --- updated-dependencies: - dependency-name: docker/login-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 1ef104db29..410aa05ea1 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -41,7 +41,7 @@ jobs: uses: docker/setup-buildx-action@v2 # needed for GitHub Actions Cache in build-push-action - name: Log in to the Container registry - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} From bef3c54e0f6c5c13aca320774107cc626cb79368 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 13 Sep 2023 10:43:00 +0300 Subject: [PATCH 0601/1312] Replace goblint-cil pin with published 2.0.2 --- dune-project | 2 +- goblint.opam | 5 +++-- goblint.opam.locked | 6 +----- goblint.opam.template | 3 ++- gobview | 2 +- 5 files changed, 8 insertions(+), 10 deletions(-) diff --git a/dune-project b/dune-project index b8b8481c97..c5dc943c5d 100644 --- a/dune-project +++ b/dune-project @@ -24,7 +24,7 @@ (synopsis "Static analysis framework for C") (depends (ocaml (>= 4.10)) - (goblint-cil (>= 2.0.1)) ; TODO no way to define as pin-depends? Used goblint.opam.template to add it for now. https://github.com/ocaml/dune/issues/3231. Alternatively, removing this line and adding cil as a git submodule and `(vendored_dirs cil)` as ./dune also works. This way, no more need to reinstall the pinned cil opam package on changes. However, then cil is cleaned and has to be rebuild together with goblint. + (goblint-cil (>= 2.0.2)) ; TODO no way to define as pin-depends? Used goblint.opam.template to add it for now. https://github.com/ocaml/dune/issues/3231. Alternatively, removing this line and adding cil as a git submodule and `(vendored_dirs cil)` as ./dune also works. This way, no more need to reinstall the pinned cil opam package on changes. However, then cil is cleaned and has to be rebuild together with goblint. (batteries (>= 3.4.0)) (zarith (>= 1.8)) (yojson (>= 2.0.0)) diff --git a/goblint.opam b/goblint.opam index 59f8a59c09..3b1afdfff8 100644 --- a/goblint.opam +++ b/goblint.opam @@ -21,7 +21,7 @@ bug-reports: "https://github.com/goblint/analyzer/issues" depends: [ "dune" {>= "3.6"} "ocaml" {>= "4.10"} - "goblint-cil" {>= "2.0.1"} + "goblint-cil" {>= "2.0.2"} "batteries" {>= "3.4.0"} "zarith" {>= "1.8"} "yojson" {>= "2.0.0"} @@ -75,7 +75,8 @@ dev-repo: "git+https://github.com/goblint/analyzer.git" # also remember to generate/adjust goblint.opam.locked! available: os-distribution != "alpine" & arch != "arm64" pin-depends: [ - [ "goblint-cil.2.0.1" "git+https://github.com/goblint/cil.git#4df989fe625d91ce07d94afe1d85b3b5c6cdd63e" ] + # published goblint-cil 2.0.2 is currently up-to-date, so no pin needed + # [ "goblint-cil.2.0.2" "git+https://github.com/goblint/cil.git#98598d94f796a63751e5a9d39c6b3a9fe1f32330" ] # TODO: add back after release, only pinned for optimization (https://github.com/ocaml-ppx/ppx_deriving/pull/252) [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] # TODO: add back after release, only pinned for CI stability diff --git a/goblint.opam.locked b/goblint.opam.locked index fe5bb65a00..d0323713ad 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -58,7 +58,7 @@ depends: [ "dyn" {= "3.6.1"} "fmt" {= "0.9.0"} "fpath" {= "0.7.3"} - "goblint-cil" {= "2.0.1"} + "goblint-cil" {= "2.0.2"} "integers" {= "0.7.0"} "json-data-encoding" {= "0.12.1"} "jsonrpc" {= "1.15.0~5.0preview1"} @@ -127,10 +127,6 @@ conflicts: [ ] # TODO: manually reordered to avoid opam pin crash: https://github.com/ocaml/opam/issues/4936 pin-depends: [ - [ - "goblint-cil.2.0.1" - "git+https://github.com/goblint/cil.git#4df989fe625d91ce07d94afe1d85b3b5c6cdd63e" - ] [ "apron.v0.9.13" "git+https://github.com/antoinemine/apron.git#1a8e91062c0d7d1e80333d19d5a432332bbbaec8" diff --git a/goblint.opam.template b/goblint.opam.template index 5c13293196..eb795f29d2 100644 --- a/goblint.opam.template +++ b/goblint.opam.template @@ -2,7 +2,8 @@ # also remember to generate/adjust goblint.opam.locked! available: os-distribution != "alpine" & arch != "arm64" pin-depends: [ - [ "goblint-cil.2.0.1" "git+https://github.com/goblint/cil.git#4df989fe625d91ce07d94afe1d85b3b5c6cdd63e" ] + # published goblint-cil 2.0.2 is currently up-to-date, so no pin needed + # [ "goblint-cil.2.0.2" "git+https://github.com/goblint/cil.git#98598d94f796a63751e5a9d39c6b3a9fe1f32330" ] # TODO: add back after release, only pinned for optimization (https://github.com/ocaml-ppx/ppx_deriving/pull/252) [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] # TODO: add back after release, only pinned for CI stability diff --git a/gobview b/gobview index c3dcfaba97..b373d06174 160000 --- a/gobview +++ b/gobview @@ -1 +1 @@ -Subproject commit c3dcfaba97a1df72f027e5dad317e2c201ce5e4b +Subproject commit b373d06174667537b671f3122daf4ebd4b195aea From 8440ff01c6f63e9480b578e79bb7dc28893ed5e1 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 13 Sep 2023 11:08:32 +0300 Subject: [PATCH 0602/1312] Update releasing documentation about Zenodo webhook --- docs/developer-guide/releasing.md | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/docs/developer-guide/releasing.md b/docs/developer-guide/releasing.md index f6bfbb459e..69ffcb2461 100644 --- a/docs/developer-guide/releasing.md +++ b/docs/developer-guide/releasing.md @@ -45,13 +45,20 @@ 10. Check that analysis works: `goblint -v tests/regression/04-mutex/01-simple_rc.c`. 11. Exit Docker container. -12. Create a GitHub release with the git tag: `DUNE_RELEASE_DELEGATE=github-dune-release-delegate dune-release publish distrib`. +12. Temporarily enable Zenodo GitHub webhook. + + This is because we only want numbered version releases to automatically add a new version to our Zenodo artifact. + Other tags (like SV-COMP or paper artifacts) have manually created Zenodo artifacts anyway and thus shouldn't add new versions to the main Zenodo artifact. + +13. Create a GitHub release with the git tag: `DUNE_RELEASE_DELEGATE=github-dune-release-delegate dune-release publish distrib`. Explicitly specify `distrib` because we don't want to publish OCaml API docs. Environment variable workaround for the package having a Read the Docs `doc` URL (see ). -13. Create an opam package: `dune-release opam pkg`. -14. Submit the opam package to opam-repository: `dune-release opam submit`. +14. Re-disable Zenodo GitHub webhook. + +15. Create an opam package: `dune-release opam pkg`. +16. Submit the opam package to opam-repository: `dune-release opam submit`. ## SV-COMP @@ -104,15 +111,9 @@ ### After all preruns 1. Push git tag from last prerun: `git push origin svcompXY`. -2. Temporarily disable Zenodo webhook. - - This is because we don't want a new out-of-place version of Goblint in our Zenodo artifact. - A separate Zenodo artifact for the SV-COMP version can be created later if tool paper is submitted. - -3. Create GitHub release from the git tag and attach latest submitted archive as a download. -4. Manually run `docker` workflow on `svcompXY` git tag and targeting `svcompXY` Docker tag. +2. Create GitHub release from the git tag and attach latest submitted archive as a download. +3. Manually run `docker` workflow on `svcompXY` git tag and targeting `svcompXY` Docker tag. This is because the usual `docker` workflow only handles semver releases. -5. Re-enable Zenodo webhook. -6. Release new semver version on opam. See above. +4. Release new semver version on opam. See above. From 336c5ffaa050cdd8e9760b0fad7565df5351892f Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 13 Sep 2023 11:26:57 +0300 Subject: [PATCH 0603/1312] Finalize v2.2.0 CHANGELOG --- CHANGELOG.md | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a126514852..045e06815a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,6 @@ ## v2.2.0 -* TODO OCaml 5 (#1003, #1137). -* TODO Library #1138 -* TODO refactor race #1136 - * Add `setjmp`/`longjmp` analysis (#887, #970, #1015, #1019). -* Refactor race analysis to lazy distribution (#1084, #1089, #1016). +* Refactor race analysis to lazy distribution (#1084, #1089, #1136, #1016). * Add thread-unsafe library function call analysis (#723, #1082). * Add mutex type analysis and mutex API analysis (#800, #839, #1073). * Add interval set domain and string literals domain (#901, #966, #994, #1048). @@ -19,8 +15,8 @@ * Fix many incremental analysis issues (#627, #836, #835, #841, #932, #678, #942, #949, #950, #957, #955, #954, #960, #959, #1004, #558, #1010, #1091). * Fix server mode for abstract debugging (#983, #990, #997, #1000, #1001, #1013, #1018, #1017, #1026, #1027). * Add documentation for configuration JSON schema and OCaml API (#999, #1054, #1055, #1053). -* Add many library function specifications (#962, #996, #1028, #1079, #1121, #1135). -* Add OCaml 5.0 support (#945). +* Add many library function specifications (#962, #996, #1028, #1079, #1121, #1135, #1138). +* Add OCaml 5.0 support (#1003, #945, #1162). ## v2.1.0 Functionally equivalent to Goblint in SV-COMP 2023. From b46aeda9eaecd3ec42ec14c977d1550faad0de23 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 13 Sep 2023 11:29:16 +0300 Subject: [PATCH 0604/1312] Disable pins for v2.2.0 release --- goblint.opam | 6 +++--- goblint.opam.locked | 7 ------- goblint.opam.template | 6 +++--- 3 files changed, 6 insertions(+), 13 deletions(-) diff --git a/goblint.opam b/goblint.opam index e42f4f7f47..efdd0aaa00 100644 --- a/goblint.opam +++ b/goblint.opam @@ -74,12 +74,12 @@ dev-repo: "git+https://github.com/goblint/analyzer.git" # on `dune build` goblint.opam will be generated from goblint.opam.template and dune-project # also remember to generate/adjust goblint.opam.locked! available: os-distribution != "alpine" & arch != "arm64" -pin-depends: [ +# pin-depends: [ # published goblint-cil 2.0.2 is currently up-to-date, so no pin needed # [ "goblint-cil.2.0.2" "git+https://github.com/goblint/cil.git#98598d94f796a63751e5a9d39c6b3a9fe1f32330" ] # TODO: add back after release, only pinned for optimization (https://github.com/ocaml-ppx/ppx_deriving/pull/252) - [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] -] + # [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] +# ] post-messages: [ "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} ] diff --git a/goblint.opam.locked b/goblint.opam.locked index b97faad221..bb59c41dd1 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -125,13 +125,6 @@ available: os-distribution != "alpine" & arch != "arm64" conflicts: [ "result" {< "1.5"} ] -# TODO: manually reordered to avoid opam pin crash: https://github.com/ocaml/opam/issues/4936 -pin-depends: [ - [ - "ppx_deriving.5.2.1" - "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" - ] -] post-messages: [ "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} ] diff --git a/goblint.opam.template b/goblint.opam.template index 59160bf171..6259c4d498 100644 --- a/goblint.opam.template +++ b/goblint.opam.template @@ -1,12 +1,12 @@ # on `dune build` goblint.opam will be generated from goblint.opam.template and dune-project # also remember to generate/adjust goblint.opam.locked! available: os-distribution != "alpine" & arch != "arm64" -pin-depends: [ +# pin-depends: [ # published goblint-cil 2.0.2 is currently up-to-date, so no pin needed # [ "goblint-cil.2.0.2" "git+https://github.com/goblint/cil.git#98598d94f796a63751e5a9d39c6b3a9fe1f32330" ] # TODO: add back after release, only pinned for optimization (https://github.com/ocaml-ppx/ppx_deriving/pull/252) - [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] -] + # [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] +# ] post-messages: [ "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} ] From f18d8085ff4ba1af670356abee38fcfb820c2c6b Mon Sep 17 00:00:00 2001 From: karoliineh Date: Wed, 13 Sep 2023 12:32:41 +0300 Subject: [PATCH 0605/1312] D.remove instead of diffing singleton in memLeak --- src/analyses/memLeak.ml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index 4f27cfea69..1bff7611a4 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -52,12 +52,10 @@ struct begin match ctx.ask (Queries.MayPointTo ptr) with | ad when not (Queries.AD.is_top ad) && Queries.AD.cardinal ad = 1 -> (* Note: Need to always set "ana.malloc.unique_address_count" to a value > 0 *) - let unique_pointed_to_heap_vars = - match Queries.AD.choose ad with - | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsMultiple v) -> D.singleton v - | _ -> D.empty () - in - D.diff state unique_pointed_to_heap_vars (* TODO: use D.remove instead of diffing singleton *) + begin match Queries.AD.choose ad with + | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsMultiple v) -> D.remove v state (* Unique pointed to heap vars *) + | _ -> state + end | _ -> state end | Abort -> From 246c999ad775c7f4bb8db7989fc975ab5d774444 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 13 Sep 2023 14:45:29 +0300 Subject: [PATCH 0606/1312] Bump batteries lower bound to 3.5.0 Required for Hashtbl.exists. --- dune-project | 2 +- goblint.opam | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dune-project b/dune-project index c5dc943c5d..4a9cd8e3c1 100644 --- a/dune-project +++ b/dune-project @@ -25,7 +25,7 @@ (depends (ocaml (>= 4.10)) (goblint-cil (>= 2.0.2)) ; TODO no way to define as pin-depends? Used goblint.opam.template to add it for now. https://github.com/ocaml/dune/issues/3231. Alternatively, removing this line and adding cil as a git submodule and `(vendored_dirs cil)` as ./dune also works. This way, no more need to reinstall the pinned cil opam package on changes. However, then cil is cleaned and has to be rebuild together with goblint. - (batteries (>= 3.4.0)) + (batteries (>= 3.5.0)) (zarith (>= 1.8)) (yojson (>= 2.0.0)) (qcheck-core (>= 0.19)) diff --git a/goblint.opam b/goblint.opam index efdd0aaa00..661222805b 100644 --- a/goblint.opam +++ b/goblint.opam @@ -22,7 +22,7 @@ depends: [ "dune" {>= "3.6"} "ocaml" {>= "4.10"} "goblint-cil" {>= "2.0.2"} - "batteries" {>= "3.4.0"} + "batteries" {>= "3.5.0"} "zarith" {>= "1.8"} "yojson" {>= "2.0.0"} "qcheck-core" {>= "0.19"} From 1a731fd3eb9bed59843d0e5a9e96694d6592a1a5 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 13 Sep 2023 15:10:54 +0300 Subject: [PATCH 0607/1312] Fix deprecated tail syntax in 70-transform/02-deadcode.t --- tests/regression/70-transform/02-deadcode.t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression/70-transform/02-deadcode.t b/tests/regression/70-transform/02-deadcode.t index ed3cd80e17..8bc4f9bf13 100644 --- a/tests/regression/70-transform/02-deadcode.t +++ b/tests/regression/70-transform/02-deadcode.t @@ -210,7 +210,7 @@ Transformation still works with 'exp.mincfg', but can not find all dead code; test against the diff. Macintosh's diff(1) adds whitespace after the function names, strip with sed. - $ diff -p -U0 "$(./transform.sh --file $args 02-deadcode.c)" "$(./transform.sh --file $args --enable exp.mincfg 02-deadcode.c)" | sed 's/[[:blank:]]*$//' | tail +3 + $ diff -p -U0 "$(./transform.sh --file $args 02-deadcode.c)" "$(./transform.sh --file $args --enable exp.mincfg 02-deadcode.c)" | sed 's/[[:blank:]]*$//' | tail -n +3 @@ -13,0 +14,3 @@ int basic1(int n ) + if (n < 0) { + return (0); From 833d90d41b5d14934edbf283cb90fdafbf382798 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 13 Sep 2023 15:16:01 +0300 Subject: [PATCH 0608/1312] Remove -p in 70-transform/02-deadcode.t due to OSX On opam-repository CI, OSX has different -p output. --- tests/regression/70-transform/02-deadcode.t | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/regression/70-transform/02-deadcode.t b/tests/regression/70-transform/02-deadcode.t index 8bc4f9bf13..03a46b891e 100644 --- a/tests/regression/70-transform/02-deadcode.t +++ b/tests/regression/70-transform/02-deadcode.t @@ -210,15 +210,15 @@ Transformation still works with 'exp.mincfg', but can not find all dead code; test against the diff. Macintosh's diff(1) adds whitespace after the function names, strip with sed. - $ diff -p -U0 "$(./transform.sh --file $args 02-deadcode.c)" "$(./transform.sh --file $args --enable exp.mincfg 02-deadcode.c)" | sed 's/[[:blank:]]*$//' | tail -n +3 - @@ -13,0 +14,3 @@ int basic1(int n ) + $ diff -U0 "$(./transform.sh --file $args 02-deadcode.c)" "$(./transform.sh --file $args --enable exp.mincfg 02-deadcode.c)" | sed 's/[[:blank:]]*$//' | tail -n +3 + @@ -13,0 +14,3 @@ + if (n < 0) { + return (0); + } - @@ -54,0 +58,2 @@ int one_branch_dead(int x ) + @@ -54,0 +58,2 @@ + } else { + return (7 - x); - @@ -65,0 +71,8 @@ int uncalled_but_referenced_function(int + @@ -65,0 +71,8 @@ +int uncalled1(void) +{ + @@ -227,17 +227,17 @@ Macintosh's diff(1) adds whitespace after the function names, strip with sed. + +} +} - @@ -79,0 +93,5 @@ int conditional_call_in_loop(int x ) + @@ -79,0 +93,5 @@ + if (i > 7) { + { + uncalled1(); + } + } - @@ -151,0 +170,4 @@ int loop_dead_on_break(int z ) + @@ -151,0 +170,4 @@ + { + s += s; + i ++; + } - @@ -203,0 +226,2 @@ int main(void) + @@ -203,0 +226,2 @@ + uncalled1(); + uncalled_but_referenced_function(3); From 166a9b619b87456059de8f1839fb810621302efb Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 13 Sep 2023 15:20:48 +0300 Subject: [PATCH 0609/1312] Add CHANGELOG for v2.2.1 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 045e06815a..97cc399133 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## v2.2.1 +* Bump batteries lower bound to 3.5.0. +* Fix flaky dead code elimination transformation test. + ## v2.2.0 * Add `setjmp`/`longjmp` analysis (#887, #970, #1015, #1019). * Refactor race analysis to lazy distribution (#1084, #1089, #1136, #1016). From 3c014186d9f77bec2e734828aee0c1caf8a24b6b Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 13 Sep 2023 15:14:54 +0200 Subject: [PATCH 0610/1312] Remove unused function --- src/analyses/memOutOfBounds.ml | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 9f9708335f..01bc12d7cb 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -23,33 +23,6 @@ struct let intdom_of_int x = IntDomain.IntDomTuple.of_int (Cilfacade.ptrdiff_ikind ()) (Z.of_int x) - let to_index ?typ offs = - let idx_of_int x = - IntDomain.IntDomTuple.of_int (Cilfacade.ptrdiff_ikind ()) (Z.of_int (x / 8)) - in - let rec offset_to_index_offset ?typ offs = match offs with - | `NoOffset -> idx_of_int 0 - | `Field (field, o) -> - let field_as_offset = Field (field, NoOffset) in - let bits_offset, _size = GoblintCil.bitsOffset (TComp (field.fcomp, [])) field_as_offset in - let bits_offset = idx_of_int bits_offset in - let remaining_offset = offset_to_index_offset ~typ:field.ftype o in - IntDomain.IntDomTuple.add bits_offset remaining_offset - | `Index (x, o) -> - let (item_typ, item_size_in_bits) = - match Option.map unrollType typ with - | Some TArray(item_typ, _, _) -> - let item_size_in_bits = bitsSizeOf item_typ in - (Some item_typ, idx_of_int item_size_in_bits) - | _ -> - (None, IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind ()) - in - let bits_offset = IntDomain.IntDomTuple.mul item_size_in_bits x in - let remaining_offset = offset_to_index_offset ?typ:item_typ o in - IntDomain.IntDomTuple.add bits_offset remaining_offset - in - offset_to_index_offset ?typ offs - let rec exp_contains_a_ptr (exp:exp) = match exp with | Const _ From 15f782b9efd276da84cbbf8121f04eb2e47101b8 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 13 Sep 2023 15:25:44 +0200 Subject: [PATCH 0611/1312] Add exception handling for intdom arithmetic in memOutOfBounds --- src/analyses/memOutOfBounds.ml | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 01bc12d7cb..61e1b1a808 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -77,7 +77,11 @@ struct let item_typ_size_in_bytes = (bitsSizeOf item_typ) / 8 in let item_typ_size_in_bytes = intdom_of_int item_typ_size_in_bytes in begin match ctx.ask (Queries.EvalLength ptr) with - | `Lifted arr_len -> `Lifted (IntDomain.IntDomTuple.mul item_typ_size_in_bytes arr_len) + | `Lifted arr_len -> + begin + try `Lifted (IntDomain.IntDomTuple.mul item_typ_size_in_bytes arr_len) + with IntDomain.ArithmeticOnIntegerBot _ -> VDQ.ID.bot () + end | `Bot -> VDQ.ID.bot () | `Top -> VDQ.ID.top () end @@ -116,7 +120,10 @@ struct | `Lifted i -> (* The offset must be casted to ptrdiff_ikind in order to have matching ikinds for the multiplication below *) let casted_offset = IntDomain.IntDomTuple.cast_to (Cilfacade.ptrdiff_ikind ()) i in - `Lifted (IntDomain.IntDomTuple.mul casted_offset ptr_contents_typ_size_in_bytes) + begin + try `Lifted (IntDomain.IntDomTuple.mul casted_offset ptr_contents_typ_size_in_bytes) + with IntDomain.ArithmeticOnIntegerBot _ -> `Bot + end | `Top -> `Top | `Bot -> `Bot @@ -128,12 +135,18 @@ struct let bits_offset, _size = GoblintCil.bitsOffset (TComp (field.fcomp, [])) field_as_offset in let bytes_offset = intdom_of_int (bits_offset / 8) in let remaining_offset = offs_to_idx field.ftype o in - IntDomain.IntDomTuple.add bytes_offset remaining_offset + begin + try IntDomain.IntDomTuple.add bytes_offset remaining_offset + with IntDomain.ArithmeticOnIntegerBot _ -> IntDomain.IntDomTuple.bot_of @@ Cilfacade.ptrdiff_ikind () + end | `Index (x, o) -> - let typ_size_in_bytes = size_of_type_in_bytes typ in - let bytes_offset = IntDomain.IntDomTuple.mul typ_size_in_bytes x in - let remaining_offset = offs_to_idx typ o in - IntDomain.IntDomTuple.add bytes_offset remaining_offset + begin try + let typ_size_in_bytes = size_of_type_in_bytes typ in + let bytes_offset = IntDomain.IntDomTuple.mul typ_size_in_bytes x in + let remaining_offset = offs_to_idx typ o in + IntDomain.IntDomTuple.add bytes_offset remaining_offset + with IntDomain.ArithmeticOnIntegerBot _ -> IntDomain.IntDomTuple.bot_of @@ Cilfacade.ptrdiff_ikind () + end let check_unknown_addr_deref ctx ptr = let may_contain_unknown_addr = @@ -283,7 +296,11 @@ struct let offset_size = eval_ptr_offset_in_binop ctx e2 t in (* Make sure to add the address offset to the binop offset *) let offset_size_with_addr_size = match offset_size with - | `Lifted os -> `Lifted (IntDomain.IntDomTuple.add os addr_offs) + | `Lifted os -> + begin + try `Lifted (IntDomain.IntDomTuple.add os addr_offs) + with IntDomain.ArithmeticOnIntegerBot _ -> `Bot + end | `Top -> `Top | `Bot -> `Bot in From 20433e34c968f8c413bb1042f1165f547a70f203 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 13 Sep 2023 17:15:58 +0200 Subject: [PATCH 0612/1312] Ignore info messages for test case 77/05 due to MacOS CI --- tests/regression/77-mem-oob/05-oob-implicit-deref.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/regression/77-mem-oob/05-oob-implicit-deref.c b/tests/regression/77-mem-oob/05-oob-implicit-deref.c index 088493d3b9..8bec6a72e0 100644 --- a/tests/regression/77-mem-oob/05-oob-implicit-deref.c +++ b/tests/regression/77-mem-oob/05-oob-implicit-deref.c @@ -1,4 +1,8 @@ -// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval +// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval --disable warn.info +/* + Note: the "--disable warn.info" above is a temporary workaround, + since the GitHub CI seems to be considering Info messages as violations of NOWARN (cf. https://github.com/goblint/analyzer/issues/1151) +*/ #include #include #include From a36572925450fc31fe95f448a75118f004af537a Mon Sep 17 00:00:00 2001 From: karoliineh Date: Thu, 14 Sep 2023 12:23:21 +0300 Subject: [PATCH 0613/1312] Add zlib and liblzma functions used in the silver searcher --- src/analyses/libraryFunctions.ml | 18 ++++++++++++++++++ src/util/options.schema.json | 4 +++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index e8fabe7dc1..2eb68dd437 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -102,6 +102,8 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("vprintf", unknown [drop "format" [r]; drop "vlist" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) ("vfprintf", unknown [drop "stream" [r_deep; w_deep]; drop "format" [r]; drop "vlist" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) ("vsprintf", unknown [drop "buffer" [w]; drop "format" [r]; drop "vlist" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) + ("vasprintf", unknown [drop "strp" [w]; drop "format" [r]; drop "ap" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) + ("vsprintf", unknown [drop "str" [w]; drop "format" [r]; drop "ap" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) ("mktime", unknown [drop "tm" [r;w]]); ("ctime", unknown ~attrs:[ThreadUnsafe] [drop "rm" [r]]); ("clearerr", unknown [drop "stream" [w]]); @@ -288,6 +290,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("ftw", unknown ~attrs:[ThreadUnsafe] [drop "dirpath" [r]; drop "fn" [s]; drop "nopenfd" []]); (* TODO: use Call instead of Spawn *) ("nftw", unknown ~attrs:[ThreadUnsafe] [drop "dirpath" [r]; drop "fn" [s]; drop "nopenfd" []; drop "flags" []]); (* TODO: use Call instead of Spawn *) ("fnmatch", unknown [drop "pattern" [r]; drop "string" [r]; drop "flags" []]); + ("realpath", unknown [drop "path" [r]; drop "resolved_path" [w]]); ] (** Pthread functions. *) @@ -824,6 +827,19 @@ let pcre_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("pcre_version", unknown []); ] +let zlib_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ + ("inflate", unknown [drop "strm" [r_deep]; drop "flush" []]); + ("inflateInit2", unknown [drop "strm" [r_deep; w_deep]; drop "windowBits" []]); + ("inflateInit2_", unknown [drop "strm" [r_deep; w_deep]; drop "windowBits" []; drop "version" [r]; drop "stream_size" []]); + ("inflateEnd", unknown [drop "strm" [f_deep]]); + ] + +let liblzma_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ + ("lzma_code", unknown [drop "strm" [r_deep; w_deep]; drop "action" []]); + ("lzma_auto_decoder", unknown [drop "strm" [r_deep; w_deep]; drop "memlimit" []; drop "flags" []]); + ("lzma_end", unknown [drop "strm" [r_deep; w_deep]]); + ] + let libraries = Hashtbl.of_list [ ("c", c_descs_list @ math_descs_list); ("posix", posix_descs_list); @@ -837,6 +853,8 @@ let libraries = Hashtbl.of_list [ ("ncurses", ncurses_descs_list); ("zstd", zstd_descs_list); ("pcre", pcre_descs_list); + ("zlib", zlib_descs_list); + ("liblzma", liblzma_descs_list); ] let activated_library_descs: (string, LibraryDesc.t) Hashtbl.t ResettableLazy.t = diff --git a/src/util/options.schema.json b/src/util/options.schema.json index 1ef73378fb..1b9c7d3fd5 100644 --- a/src/util/options.schema.json +++ b/src/util/options.schema.json @@ -1285,7 +1285,9 @@ "sv-comp", "ncurses", "zstd", - "pcre" + "pcre", + "zlib", + "liblzma" ] }, "default": [ From 0a5737414fd9aac74b4adfff61e4e842bb37aad7 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 14 Sep 2023 14:48:51 +0200 Subject: [PATCH 0614/1312] Make it work with Blobs --- src/analyses/base.ml | 30 +++++++++++++---- .../regression/73-strings/03-string_basics.c | 4 +-- tests/regression/73-strings/08-cursed.c | 32 +++++++++++++++++++ 3 files changed, 57 insertions(+), 9 deletions(-) create mode 100644 tests/regression/73-strings/08-cursed.c diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 30c1fc3c52..cc8f912832 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2065,7 +2065,7 @@ struct | Addr.Addr (v, o) -> Addr.Addr (v, lo o) | other -> other in AD.map rmLastOffset a - | _ -> raise (Failure "String function: not an address") + | _ -> raise (Failure "String function: not an address") in let string_manipulation s1 s2 lv all op_addr op_array = let s1_v = eval_rv (Analyses.ask_of_ctx ctx) gs st s1 in @@ -2075,6 +2075,7 @@ struct let s2_a = address_from_value s2_v in let s2_typ = AD.type_of s2_a in (* compute value in string literals domain if s1 and s2 are both string literals *) + (* TODO: comparing types structurally should not be done (use typSig instead!) *) if s1_typ = charPtrType && s2_typ = charPtrType then begin match lv, op_addr with | Some lv_val, Some f -> @@ -2093,16 +2094,30 @@ struct set ~ctx (Analyses.ask_of_ctx ctx) gs st s1_a s1_typ (VD.top_value (unrollType s1_typ)) end (* else compute value in array domain *) - else + else let lv_a, lv_typ = match lv with | Some lv_val -> eval_lv (Analyses.ask_of_ctx ctx) gs st lv_val, Cilfacade.typeOfLval lv_val | None -> s1_a, s1_typ in - begin match get (Analyses.ask_of_ctx ctx) gs st s1_a None, get (Analyses.ask_of_ctx ctx) gs st s2_a None with + begin match (get (Analyses.ask_of_ctx ctx) gs st s1_a None), get (Analyses.ask_of_ctx ctx) gs st s2_a None with | Array array_s1, Array array_s2 -> set ~ctx (Analyses.ask_of_ctx ctx) gs st lv_a lv_typ (op_array array_s1 array_s2) - | Array array_s1, _ when s2_typ = charPtrType -> + | Array array_s1, _ when s2_typ = charPtrType -> let s2_null_bytes = List.map CArrays.to_null_byte_domain (AD.to_string s2_a) in let array_s2 = List.fold_left CArrays.join (CArrays.bot ()) s2_null_bytes in set ~ctx (Analyses.ask_of_ctx ctx) gs st lv_a lv_typ (op_array array_s1 array_s2) + | Bot, Array array_s2 -> + (* If we have bot inside here, we assume the blob is used as a char array and create one inside *) + let size = ctx.ask (Q.BlobSize s1) in + let s_id = ValueDomainQueries.ID.unlift (Fun.id) size in + let empty_array = CArrays.make (ID.cast_to (Cilfacade.ptrdiff_ikind ()) s_id) (Int (ID.top_of IChar)) in + set ~ctx (Analyses.ask_of_ctx ctx) gs st lv_a lv_typ (op_array empty_array array_s2) + | Bot , _ when s2_typ = charPtrType -> + (* If we have bot inside here, we assume the blob is used as a char array and create one inside *) + let size = ctx.ask (Q.BlobSize s1) in + let s_id = ValueDomainQueries.ID.unlift (Fun.id) size in + let empty_array = CArrays.make (ID.cast_to (Cilfacade.ptrdiff_ikind ()) s_id) (Int (ID.top_of IChar)) in + let s2_null_bytes = List.map CArrays.to_null_byte_domain (AD.to_string s2_a) in + let array_s2 = List.fold_left CArrays.join (CArrays.bot ()) s2_null_bytes in + set ~ctx (Analyses.ask_of_ctx ctx) gs st lv_a lv_typ (op_array empty_array array_s2) | _, Array array_s2 when s1_typ = charPtrType -> (* if s1 is string literal, str(n)cpy and str(n)cat are undefined *) if op_addr = None then @@ -2113,7 +2128,8 @@ struct let s1_null_bytes = List.map CArrays.to_null_byte_domain (AD.to_string s1_a) in let array_s1 = List.fold_left CArrays.join (CArrays.bot ()) s1_null_bytes in set ~ctx (Analyses.ask_of_ctx ctx) gs st lv_a lv_typ (op_array array_s1 array_s2) - | _ -> set ~ctx (Analyses.ask_of_ctx ctx) gs st lv_a lv_typ (VD.top_value (unrollType lv_typ)) + | vals1, _ -> + set ~ctx (Analyses.ask_of_ctx ctx) gs st lv_a lv_typ (VD.top_value (unrollType lv_typ)) end in let st = match desc.special args, f.vname with @@ -2157,7 +2173,7 @@ struct let dest_typ = Cilfacade.typeOfLval lv_val in let v = eval_rv (Analyses.ask_of_ctx ctx) gs st s in let a = address_from_value v in - let value:value = + let value:value = (* if s string literal, compute strlen in string literals domain *) if AD.type_of a = charPtrType then Int (AD.to_string_length a) @@ -2181,7 +2197,7 @@ struct (fun h_ar n_ar -> match CArrays.substring_extraction h_ar n_ar with | true, false -> Address (AD.null_ptr) | false, true -> Address (eval_lv (Analyses.ask_of_ctx ctx) gs st (mkMem ~addr:(Cil.stripCasts haystack) ~off:NoOffset)) - | _ -> Address (AD.join (eval_lv (Analyses.ask_of_ctx ctx) gs st + | _ -> Address (AD.join (eval_lv (Analyses.ask_of_ctx ctx) gs st (mkMem ~addr:(Cil.stripCasts haystack) ~off:(Index (Offset.Index.Exp.any, NoOffset)))) (AD.null_ptr))) | None -> st end diff --git a/tests/regression/73-strings/03-string_basics.c b/tests/regression/73-strings/03-string_basics.c index 3487a36be7..09a1ad8e81 100644 --- a/tests/regression/73-strings/03-string_basics.c +++ b/tests/regression/73-strings/03-string_basics.c @@ -30,7 +30,7 @@ int main() { __goblint_check(len == 4); len = strlen(s5); - __goblint_check(len == 5); // UNKNOWN + __goblint_check(len == 5); strcat(s1, s2); len = strlen(s1); @@ -87,4 +87,4 @@ int main() { free(s1); return 0; -} \ No newline at end of file +} diff --git a/tests/regression/73-strings/08-cursed.c b/tests/regression/73-strings/08-cursed.c new file mode 100644 index 0000000000..421f9f7b18 --- /dev/null +++ b/tests/regression/73-strings/08-cursed.c @@ -0,0 +1,32 @@ +// PARAM: --disable ana.base.limit-string-addresses --enable ana.int.interval --enable ana.base.arrays.nullbytes + +#include +#include +#include + +int main() { + // These should behave identically + char s1[40]; + char* s5 = malloc(40); + char* s6 = malloc(40); + + strcpy(s1, "hello"); + strcpy(s5, "hello"); + + int len = strlen(s5); + __goblint_check(len == 5); + + int len2 = strlen(s1); + __goblint_check(len2 == 5); + + strcpy(s6,s5); + int len3 = strlen(s6); + __goblint_check(len3 == 5); + + // Why does this not know the string length after the copy? + // This goes into the array/array case, so it seems unrelated to blob problems. + strcpy(s5, "badabingbadaboom"); + len2 = strlen(s5); // no must 0 bytes anywhere? + + return 0; +} From 1aaec466e5234e8906fbf9075f3177bd99b88724 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 14 Sep 2023 15:42:30 +0200 Subject: [PATCH 0615/1312] Update malloced strings destructively where possible --- src/analyses/base.ml | 8 ++++---- src/cdomains/valueDomain.ml | 14 ++++++++------ tests/regression/73-strings/08-cursed.c | 7 +++---- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index cc8f912832..44ef339d2e 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1382,7 +1382,7 @@ struct (** [set st addr val] returns a state where [addr] is set to [val] * it is always ok to put None for lval_raw and rval_raw, this amounts to not using/maintaining * precise information about arrays. *) - let set (a: Q.ask) ~(ctx: _ ctx) ?(invariant=false) ?lval_raw ?rval_raw ?t_override (gs:glob_fun) (st: store) (lval: AD.t) (lval_type: Cil.typ) (value: value) : store = + let set (a: Q.ask) ~(ctx: _ ctx) ?(invariant=false) ?(blob_destructive=false) ?lval_raw ?rval_raw ?t_override (gs:glob_fun) (st: store) (lval: AD.t) (lval_type: Cil.typ) (value: value) : store = let update_variable x t y z = if M.tracing then M.tracel "set" ~var:x.vname "update_variable: start '%s' '%a'\nto\n%a\n\n" x.vname VD.pretty y CPA.pretty z; let r = update_variable x t y z in (* refers to defintion that is outside of set *) @@ -1415,7 +1415,7 @@ struct let update_offset old_value = (* Projection globals to highest Precision *) let projected_value = project_val (Queries.to_value_domain_ask a) None None value (is_global a x) in - let new_value = VD.update_offset (Queries.to_value_domain_ask a) old_value offs projected_value lval_raw ((Var x), cil_offset) t in + let new_value = VD.update_offset ~blob_destructive (Queries.to_value_domain_ask a) old_value offs projected_value lval_raw ((Var x), cil_offset) t in if WeakUpdates.mem x st.weak then VD.join old_value new_value else if invariant then ( @@ -2099,11 +2099,11 @@ struct | Some lv_val -> eval_lv (Analyses.ask_of_ctx ctx) gs st lv_val, Cilfacade.typeOfLval lv_val | None -> s1_a, s1_typ in begin match (get (Analyses.ask_of_ctx ctx) gs st s1_a None), get (Analyses.ask_of_ctx ctx) gs st s2_a None with - | Array array_s1, Array array_s2 -> set ~ctx (Analyses.ask_of_ctx ctx) gs st lv_a lv_typ (op_array array_s1 array_s2) + | Array array_s1, Array array_s2 -> set ~ctx ~blob_destructive:true (Analyses.ask_of_ctx ctx) gs st lv_a lv_typ (op_array array_s1 array_s2) | Array array_s1, _ when s2_typ = charPtrType -> let s2_null_bytes = List.map CArrays.to_null_byte_domain (AD.to_string s2_a) in let array_s2 = List.fold_left CArrays.join (CArrays.bot ()) s2_null_bytes in - set ~ctx (Analyses.ask_of_ctx ctx) gs st lv_a lv_typ (op_array array_s1 array_s2) + set ~ctx ~blob_destructive:true (Analyses.ask_of_ctx ctx) gs st lv_a lv_typ (op_array array_s1 array_s2) | Bot, Array array_s2 -> (* If we have bot inside here, we assume the blob is used as a char array and create one inside *) let size = ctx.ask (Q.BlobSize s1) in diff --git a/src/cdomains/valueDomain.ml b/src/cdomains/valueDomain.ml index e5c4727b72..9b4b09d930 100644 --- a/src/cdomains/valueDomain.ml +++ b/src/cdomains/valueDomain.ml @@ -19,7 +19,7 @@ sig include Lattice.S type offs val eval_offset: VDQ.t -> (AD.t -> t) -> t-> offs -> exp option -> lval option -> typ -> t - val update_offset: VDQ.t -> t -> offs -> t -> exp option -> lval -> typ -> t + val update_offset: ?blob_destructive:bool -> VDQ.t -> t -> offs -> t -> exp option -> lval -> typ -> t val update_array_lengths: (exp -> t) -> t -> Cil.typ -> t val affect_move: ?replace_with_const:bool -> VDQ.t -> t -> varinfo -> (exp -> int option) -> t val affecting_vars: t -> varinfo list @@ -288,12 +288,12 @@ struct true else false - | Some min, None -> + | Some min, None -> if Z.gt min Z.zero then true else false - | None, Some max -> + | None, Some max -> if Z.lt max Z.zero then true else @@ -953,7 +953,7 @@ struct in do_eval_offset ask f x offs exp l o v t - let update_offset (ask: VDQ.t) (x:t) (offs:offs) (value:t) (exp:exp option) (v:lval) (t:typ): t = + let update_offset ?(blob_destructive=false) (ask: VDQ.t) (x:t) (offs:offs) (value:t) (exp:exp option) (v:lval) (t:typ): t = let rec do_update_offset (ask:VDQ.t) (x:t) (offs:offs) (value:t) (exp:exp option) (l:lval option) (o:offset option) (v:lval) (t:typ):t = if M.tracing then M.traceli "update_offset" "do_update_offset %a %a (%a) %a\n" pretty x Offs.pretty offs (Pretty.docOpt (CilType.Exp.pretty ())) exp pretty value; let mu = function Blob (Blob (y, s', orig), s, orig2) -> Blob (y, ID.join s s',orig) | x -> x in @@ -1001,9 +1001,11 @@ struct | (Var var, _) -> let blob_size_opt = ID.to_int s in not @@ ask.is_multiple var - && not @@ Cil.isVoidType t (* Size of value is known *) && Option.is_some blob_size_opt (* Size of blob is known *) - && BI.equal (Option.get blob_size_opt) (BI.of_int @@ Cil.alignOf_int t) + && (( + not @@ Cil.isVoidType t (* Size of value is known *) + && BI.equal (Option.get blob_size_opt) (BI.of_int @@ Cil.alignOf_int t) + ) || blob_destructive) | _ -> false end in diff --git a/tests/regression/73-strings/08-cursed.c b/tests/regression/73-strings/08-cursed.c index 421f9f7b18..1507b92563 100644 --- a/tests/regression/73-strings/08-cursed.c +++ b/tests/regression/73-strings/08-cursed.c @@ -1,4 +1,4 @@ -// PARAM: --disable ana.base.limit-string-addresses --enable ana.int.interval --enable ana.base.arrays.nullbytes +// PARAM: --disable ana.base.limit-string-addresses --enable ana.int.interval --enable ana.base.arrays.nullbytes --set ana.malloc.unique_address_count 1 #include #include @@ -23,10 +23,9 @@ int main() { int len3 = strlen(s6); __goblint_check(len3 == 5); - // Why does this not know the string length after the copy? - // This goes into the array/array case, so it seems unrelated to blob problems. strcpy(s5, "badabingbadaboom"); - len2 = strlen(s5); // no must 0 bytes anywhere? + int len2 = strlen(s5); + __goblint_check(len2 == 16); return 0; } From a1464e1ed471aa8fc55b78eae130902da97f1791 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Thu, 14 Sep 2023 21:46:00 +0300 Subject: [PATCH 0616/1312] Use record as Access.A type instead of a quintuple --- src/analyses/raceAnalysis.ml | 33 +++++++++++++++++---------------- src/domains/access.ml | 32 ++++++++++++++++++-------------- 2 files changed, 35 insertions(+), 30 deletions(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index 74a98af6be..a89ef9206a 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -225,9 +225,9 @@ struct | _ -> () - let side_access ctx (conf, w, loc, e, a) ((memoroot, offset) as memo) = + let side_access ctx Access.A.{conf; kind; node; exp; acc} ((memoroot, offset) as memo) = if !AnalysisState.should_warn then - ctx.sideg (V.access memoroot) (G.create_access (OffsetTrie.singleton offset (`Lifted (Access.AS.singleton (conf, w, loc, e, a))))); + ctx.sideg (V.access memoroot) (G.create_access (OffsetTrie.singleton offset (`Lifted (Access.AS.singleton {conf; kind; node; exp; acc})))); side_vars ctx memo (** Side-effect empty access set for prefix-type_suffix race checking. *) @@ -302,24 +302,24 @@ struct ) (G.vars (ctx.global (V.vars g))) | _ -> Queries.Result.top q - let event ctx e octx = - match e with - | Events.Access {exp=e; lvals; kind; reach} when ThreadFlag.is_currently_multi (Analyses.ask_of_ctx ctx) -> (* threadflag query in post-threadspawn ctx *) + let event ctx exp octx = + match exp with + | Events.Access {exp; lvals; kind; reach} when ThreadFlag.is_currently_multi (Analyses.ask_of_ctx ctx) -> (* threadflag query in post-threadspawn ctx *) (* must use original (pre-assign, etc) ctx queries *) let conf = 110 in let module LS = Queries.LS in let part_access (vo:varinfo option): MCPAccess.A.t = (*partitions & locks*) - Obj.obj (octx.ask (PartAccess (Memory {exp=e; var_opt=vo; kind}))) + Obj.obj (octx.ask (PartAccess (Memory {exp; var_opt=vo; kind}))) in - let loc = Option.get !Node.current_node in + let node = Option.get !Node.current_node in let add_access conf voffs = - let a = part_access (Option.map fst voffs) in - Access.add ~side:(side_access octx (conf, kind, loc, e, a)) ~side_empty:(side_access_empty octx) e voffs; + let acc = part_access (Option.map fst voffs) in + Access.add ~side:(side_access octx {conf; kind; node; exp; acc}) ~side_empty:(side_access_empty octx) exp voffs; in let add_access_struct conf ci = - let a = part_access None in - Access.add_one ~side:(side_access octx (conf, kind, loc, e, a)) (`Type (TSComp (ci.cstruct, ci.cname, [])), `NoOffset) + let acc = part_access None in + Access.add_one ~side:(side_access octx {conf; kind; node; exp; acc}) (`Type (TSComp (ci.cstruct, ci.cname, [])), `NoOffset) in let has_escaped g = octx.ask (Queries.MayEscape g) in (* The following function adds accesses to the lval-set ls @@ -345,7 +345,7 @@ struct (* the case where the points-to set is non top and contains unknown values *) let includes_uk = ref false in (* now we need to access all fields that might be pointed to: is this correct? *) - begin match octx.ask (ReachableUkTypes e) with + begin match octx.ask (ReachableUkTypes exp) with | ts when Queries.TS.is_top ts -> includes_uk := true | ts -> @@ -370,12 +370,13 @@ struct (* perform shallow and deep invalidate according to Library descriptors *) let desc = LibraryFunctions.find f in if List.mem LibraryDesc.ThreadUnsafe desc.attrs then ( - let e = Lval (Var f, NoOffset) in + let exp = Lval (Var f, NoOffset) in let conf = 110 in - let loc = Option.get !Node.current_node in + let kind = AccessKind.Call in + let node = Option.get !Node.current_node in let vo = Some f in - let a = Obj.obj (ctx.ask (PartAccess (Memory {exp=e; var_opt=vo; kind=Call}))) in - side_access ctx (conf, Call, loc, e, a) ((`Var f), `NoOffset) ; + let acc = Obj.obj (ctx.ask (PartAccess (Memory {exp; var_opt=vo; kind}))) in + side_access ctx {conf; kind; node; exp; acc} ((`Var f), `NoOffset) ; ); ctx.local diff --git a/src/domains/access.ml b/src/domains/access.ml index 675d1c2e72..05308b5ed9 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -329,12 +329,18 @@ and distribute_access_type f = function module A = struct include Printable.Std - type t = int * AccessKind.t * Node.t * CilType.Exp.t * MCPAccess.A.t [@@deriving eq, ord, hash] + type t = { + conf : int; + kind : AccessKind.t; + node : Node.t; + exp : CilType.Exp.t; + acc : MCPAccess.A.t; + } [@@deriving eq, ord, hash] let name () = "access" - let pretty () (conf, kind, node, e, lp) = - Pretty.dprintf "%d, %a, %a, %a, %a" conf AccessKind.pretty kind CilType.Location.pretty (Node.location node) CilType.Exp.pretty e MCPAccess.A.pretty lp + let pretty () {conf; kind; node; exp; acc} = + Pretty.dprintf "%d, %a, %a, %a, %a" conf AccessKind.pretty kind CilType.Location.pretty (Node.location node) CilType.Exp.pretty exp MCPAccess.A.pretty acc include Printable.SimplePretty ( struct @@ -343,10 +349,8 @@ struct end ) - let conf (conf, _, _, _, _) = conf - - let relift (conf, kind, node, e, a) = - (conf, kind, node, e, MCPAccess.A.relift a) + let relift {conf; kind; node; exp; acc} = + {conf; kind; node; exp; acc = MCPAccess.A.relift acc} end module AS = @@ -354,17 +358,17 @@ struct include SetDomain.Make (A) let max_conf accs = - accs |> elements |> List.map A.conf |> (List.max ~cmp:Int.compare) + accs |> elements |> List.map (fun {A.conf; _} -> conf) |> (List.max ~cmp:Int.compare) end (* Check if two accesses may race and if yes with which confidence *) -let may_race (conf,(kind: AccessKind.t),loc,e,a) (conf2,(kind2: AccessKind.t),loc2,e2,a2) = +let may_race A.{kind; acc; _} A.{kind=kind2; acc=acc2; _} = if kind = Read && kind2 = Read then false (* two read/read accesses do not race *) else if not (get_bool "ana.race.free") && (kind = Free || kind2 = Free) then false - else if not (MCPAccess.A.may_race a a2) then + else if not (MCPAccess.A.may_race acc acc2) then false (* analysis-specific information excludes race *) else true @@ -487,7 +491,7 @@ let race_conf accs = if AS.cardinal accs = 1 then ( (* singleton component *) let acc = AS.choose accs in if may_race acc acc then (* self-race *) - Some (A.conf acc) + Some (acc.conf) else None ) @@ -516,9 +520,9 @@ let print_accesses memo grouped_accs = let allglobs = get_bool "allglobs" in let race_threshold = get_int "warn.race-threshold" in let msgs race_accs = - let h (conf,kind,node,e,a) = - let d_msg () = dprintf "%a with %a (conf. %d)" AccessKind.pretty kind MCPAccess.A.pretty a conf in - let doc = dprintf "%t (exp: %a)" d_msg d_exp e in + let h A.{conf; kind; node; exp; acc} = + let d_msg () = dprintf "%a with %a (conf. %d)" AccessKind.pretty kind MCPAccess.A.pretty acc conf in + let doc = dprintf "%t (exp: %a)" d_msg d_exp exp in (doc, Some (Messages.Location.Node node)) in AS.elements race_accs From 929b658cf26be63fcae41ce3be71499be60a9ebb Mon Sep 17 00:00:00 2001 From: karoliineh Date: Thu, 14 Sep 2023 21:57:53 +0300 Subject: [PATCH 0617/1312] Inline d_msg () to doc in access --- src/domains/access.ml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/domains/access.ml b/src/domains/access.ml index 05308b5ed9..83903ba355 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -521,8 +521,7 @@ let print_accesses memo grouped_accs = let race_threshold = get_int "warn.race-threshold" in let msgs race_accs = let h A.{conf; kind; node; exp; acc} = - let d_msg () = dprintf "%a with %a (conf. %d)" AccessKind.pretty kind MCPAccess.A.pretty acc conf in - let doc = dprintf "%t (exp: %a)" d_msg d_exp exp in + let doc = dprintf "%a with %a (conf. %d) (exp: %a)" AccessKind.pretty kind MCPAccess.A.pretty acc conf d_exp exp in (doc, Some (Messages.Location.Node node)) in AS.elements race_accs From 60952d933071b651630f6657b133e7625dc5a034 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Fri, 15 Sep 2023 10:34:34 +0300 Subject: [PATCH 0618/1312] Pass access as a variable instead of destructing the record --- src/analyses/raceAnalysis.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index a89ef9206a..d1d608ae06 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -225,9 +225,9 @@ struct | _ -> () - let side_access ctx Access.A.{conf; kind; node; exp; acc} ((memoroot, offset) as memo) = + let side_access ctx acc ((memoroot, offset) as memo) = if !AnalysisState.should_warn then - ctx.sideg (V.access memoroot) (G.create_access (OffsetTrie.singleton offset (`Lifted (Access.AS.singleton {conf; kind; node; exp; acc})))); + ctx.sideg (V.access memoroot) (G.create_access (OffsetTrie.singleton offset (`Lifted (Access.AS.singleton acc)))); side_vars ctx memo (** Side-effect empty access set for prefix-type_suffix race checking. *) From cb08d337d202fe2ac6154390f8973f9195c5a7ab Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 15 Sep 2023 11:03:07 +0300 Subject: [PATCH 0619/1312] Simplify some AD conversions, add TODOs --- src/analyses/commonPriv.ml | 2 +- src/analyses/poisonVariables.ml | 11 +++-------- src/analyses/taintPartialContexts.ml | 2 +- src/analyses/varEq.ml | 3 +-- src/cdomains/lockDomain.ml | 2 +- src/framework/constraints.ml | 2 +- 6 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index 199fae98b0..7d06844c1b 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -128,7 +128,7 @@ struct Lockset.empty () else let ad = ask.f Queries.MustLockset in - Q.AD.fold (fun mls acc -> Lockset.add mls acc) ad (Lockset.empty ()) + Q.AD.fold (fun mls acc -> Lockset.add mls acc) ad (Lockset.empty ()) (* TODO: use AD as Lockset *) (* TODO: reversed SetDomain.Hoare *) module MinLocksets = HoareDomain.Set_LiftTop (MustLockset) (struct let topname = "All locksets" end) (* reverse Lockset because Hoare keeps maximal, but we need minimal *) diff --git a/src/analyses/poisonVariables.ml b/src/analyses/poisonVariables.ml index ddbb6a5a40..acd687835e 100644 --- a/src/analyses/poisonVariables.ml +++ b/src/analyses/poisonVariables.ml @@ -92,11 +92,8 @@ struct | ad when Queries.AD.is_top ad && not (VS.is_empty octx.local) -> M.warn ~category:(Behavior (Undefined Other)) "reading unknown memory location, may be tainted!" | ad -> - Queries.AD.iter (function - (* Use original access state instead of current with removed written vars. *) - | Queries.AD.Addr.Addr (v,o) -> check_mval octx.local (Queries.AD.Addr.Addr (v,o)) - | _ -> () - ) ad + (* Use original access state instead of current with removed written vars. *) + Queries.AD.iter (check_mval octx.local) ad end; ctx.local | Access {ad; kind = Write; _} -> @@ -106,9 +103,7 @@ struct ctx.local | ad -> Queries.AD.fold (fun addr vs -> - match addr with - | Queries.AD.Addr.Addr _ -> rem_mval vs addr - | _ -> vs + rem_mval vs addr ) ad ctx.local end | _ -> ctx.local diff --git a/src/analyses/taintPartialContexts.ml b/src/analyses/taintPartialContexts.ml index eaafbf01f5..feb9599977 100644 --- a/src/analyses/taintPartialContexts.ml +++ b/src/analyses/taintPartialContexts.ml @@ -113,4 +113,4 @@ let conv_varset (addr_set : Spec.D.t) : VS.t = if Spec.D.is_top addr_set then VS.top () else - VS.of_list (List.filter_map (fun addr -> Spec.D.Addr.to_var_may addr) (Spec.D.elements addr_set)) + VS.of_list (Spec.D.to_var_may addr_set) diff --git a/src/analyses/varEq.ml b/src/analyses/varEq.ml index db164a313c..dcd49c9f02 100644 --- a/src/analyses/varEq.ml +++ b/src/analyses/varEq.ml @@ -441,8 +441,7 @@ struct D.top () else let taint_exp = - Queries.AD.elements tainted - |> List.filter_map Addr.to_mval + Queries.AD.to_mval tainted |> List.map Addr.Mval.to_cil_exp |> Queries.ES.of_list in diff --git a/src/cdomains/lockDomain.ml b/src/cdomains/lockDomain.ml index 0de5afc32c..4bc97b34ab 100644 --- a/src/cdomains/lockDomain.ml +++ b/src/cdomains/lockDomain.ml @@ -7,7 +7,7 @@ module IdxDom = ValueDomain.IndexDomain open GoblintCil -module Mutexes = SetDomain.ToppedSet (Addr) (struct let topname = "All mutexes" end) (* TODO HoareDomain? *) +module Mutexes = SetDomain.ToppedSet (Addr) (struct let topname = "All mutexes" end) (* TODO: AD? *) module Simple = Lattice.Reverse (Mutexes) module Priorities = IntDomain.Lifted diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 3ea62a2f4c..26fdfac606 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -755,7 +755,7 @@ struct | _ -> (* Depends on base for query. *) let ad = ctx.ask (Queries.EvalFunvar e) in - List.filter_map (fun addr -> Queries.AD.Addr.to_var addr) (Queries.AD.elements ad) + Queries.AD.to_var_may ad (* TODO: don't convert, handle UnknownPtr below *) in let one_function f = match f.vtype with From a0a501c8f7ec444a5aa40614ee6f0de28a2ec0e1 Mon Sep 17 00:00:00 2001 From: Nathan Schmidt Date: Sat, 16 Sep 2023 14:48:36 +0200 Subject: [PATCH 0620/1312] Replaced type comparison with `CilType.Typ.equal` --- src/analyses/base.ml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 44ef339d2e..f093eec9e5 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2075,8 +2075,7 @@ struct let s2_a = address_from_value s2_v in let s2_typ = AD.type_of s2_a in (* compute value in string literals domain if s1 and s2 are both string literals *) - (* TODO: comparing types structurally should not be done (use typSig instead!) *) - if s1_typ = charPtrType && s2_typ = charPtrType then + if CilType.Typ.equal s1_typ charPtrType && CilType.Typ.equal s2_typ charPtrType then begin match lv, op_addr with | Some lv_val, Some f -> (* when whished types coincide, compute result of operation op_addr, otherwise use top *) @@ -2100,7 +2099,7 @@ struct | None -> s1_a, s1_typ in begin match (get (Analyses.ask_of_ctx ctx) gs st s1_a None), get (Analyses.ask_of_ctx ctx) gs st s2_a None with | Array array_s1, Array array_s2 -> set ~ctx ~blob_destructive:true (Analyses.ask_of_ctx ctx) gs st lv_a lv_typ (op_array array_s1 array_s2) - | Array array_s1, _ when s2_typ = charPtrType -> + | Array array_s1, _ when CilType.Typ.equal s2_typ charPtrType -> let s2_null_bytes = List.map CArrays.to_null_byte_domain (AD.to_string s2_a) in let array_s2 = List.fold_left CArrays.join (CArrays.bot ()) s2_null_bytes in set ~ctx ~blob_destructive:true (Analyses.ask_of_ctx ctx) gs st lv_a lv_typ (op_array array_s1 array_s2) @@ -2110,7 +2109,7 @@ struct let s_id = ValueDomainQueries.ID.unlift (Fun.id) size in let empty_array = CArrays.make (ID.cast_to (Cilfacade.ptrdiff_ikind ()) s_id) (Int (ID.top_of IChar)) in set ~ctx (Analyses.ask_of_ctx ctx) gs st lv_a lv_typ (op_array empty_array array_s2) - | Bot , _ when s2_typ = charPtrType -> + | Bot , _ when CilType.Typ.equal s2_typ charPtrType -> (* If we have bot inside here, we assume the blob is used as a char array and create one inside *) let size = ctx.ask (Q.BlobSize s1) in let s_id = ValueDomainQueries.ID.unlift (Fun.id) size in @@ -2118,7 +2117,7 @@ struct let s2_null_bytes = List.map CArrays.to_null_byte_domain (AD.to_string s2_a) in let array_s2 = List.fold_left CArrays.join (CArrays.bot ()) s2_null_bytes in set ~ctx (Analyses.ask_of_ctx ctx) gs st lv_a lv_typ (op_array empty_array array_s2) - | _, Array array_s2 when s1_typ = charPtrType -> + | _, Array array_s2 when CilType.Typ.equal s1_typ charPtrType -> (* if s1 is string literal, str(n)cpy and str(n)cat are undefined *) if op_addr = None then (* triggers warning, function only evaluated for side-effects *) @@ -2128,7 +2127,7 @@ struct let s1_null_bytes = List.map CArrays.to_null_byte_domain (AD.to_string s1_a) in let array_s1 = List.fold_left CArrays.join (CArrays.bot ()) s1_null_bytes in set ~ctx (Analyses.ask_of_ctx ctx) gs st lv_a lv_typ (op_array array_s1 array_s2) - | vals1, _ -> + | _ -> set ~ctx (Analyses.ask_of_ctx ctx) gs st lv_a lv_typ (VD.top_value (unrollType lv_typ)) end in From fb7159c0fe7aa1e0ed887f807db7feb3c9699114 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sat, 16 Sep 2023 16:33:40 +0200 Subject: [PATCH 0621/1312] Add some setjump/longjump test for race detection --- tests/regression/68-longjmp/52-races.c | 36 +++++++++++++ tests/regression/68-longjmp/53-races-no.c | 37 ++++++++++++++ .../regression/68-longjmp/54-races-actually.c | 51 +++++++++++++++++++ .../68-longjmp/55-races-no-return.c | 51 +++++++++++++++++++ 4 files changed, 175 insertions(+) create mode 100644 tests/regression/68-longjmp/52-races.c create mode 100644 tests/regression/68-longjmp/53-races-no.c create mode 100644 tests/regression/68-longjmp/54-races-actually.c create mode 100644 tests/regression/68-longjmp/55-races-no-return.c diff --git a/tests/regression/68-longjmp/52-races.c b/tests/regression/68-longjmp/52-races.c new file mode 100644 index 0000000000..5c7ac6daa6 --- /dev/null +++ b/tests/regression/68-longjmp/52-races.c @@ -0,0 +1,36 @@ +// PARAM: --enable ana.int.interval +#include +#include +#include +#include +#include + +jmp_buf env_buffer; +int global = 0; +pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; + +void *t_fun(void *arg) { + pthread_mutex_lock(&mutex1); + global = 3; // NORACE + pthread_mutex_unlock(&mutex1); + return NULL; +} + +int bar() { + pthread_mutex_lock(&mutex1); + longjmp(env_buffer, 2); + pthread_mutex_unlock(&mutex1); + return 8; +} + +int main() { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + + if(!setjmp( env_buffer )) { + bar(); + } + + global = 5; // NORACE + pthread_mutex_unlock(&mutex1); +} diff --git a/tests/regression/68-longjmp/53-races-no.c b/tests/regression/68-longjmp/53-races-no.c new file mode 100644 index 0000000000..7247f14941 --- /dev/null +++ b/tests/regression/68-longjmp/53-races-no.c @@ -0,0 +1,37 @@ +// PARAM: --enable ana.int.interval +#include +#include +#include +#include +#include + +jmp_buf env_buffer; +int global = 0; +pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; + +void *t_fun(void *arg) { + pthread_mutex_lock(&mutex1); + global = 3; // NORACE + pthread_mutex_unlock(&mutex1); + return NULL; +} + +int bar() { + pthread_mutex_lock(&mutex1); + if(global ==3) { + longjmp(env_buffer, 2); + } + return 8; +} + +int main() { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + + if(!setjmp( env_buffer )) { + bar(); + } + + global = 5; // NORACE + pthread_mutex_unlock(&mutex1); +} diff --git a/tests/regression/68-longjmp/54-races-actually.c b/tests/regression/68-longjmp/54-races-actually.c new file mode 100644 index 0000000000..12f1f791c5 --- /dev/null +++ b/tests/regression/68-longjmp/54-races-actually.c @@ -0,0 +1,51 @@ +// PARAM: --enable ana.int.interval +#include +#include +#include +#include +#include + +jmp_buf env_buffer; +int global = 0; +pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; + +void *t_fun(void *arg) { + pthread_mutex_lock(&mutex1); + global = 3; // RACE + pthread_mutex_unlock(&mutex1); + return NULL; +} + +int bar() { + pthread_mutex_lock(&mutex1); + if(global == 3) { + longjmp(env_buffer, 2); + } else { + longjmp(env_buffer, 4); + } + return 8; +} + +int main() { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + int n = 0; + + switch(setjmp( env_buffer )) { + case 0: + bar(); + break; + case 2: + n=1; + pthread_mutex_unlock(&mutex1); + break; + default: + break; + } + + global = 5; //RACE + + if(n == 0) { + pthread_mutex_unlock(&mutex1); + } +} diff --git a/tests/regression/68-longjmp/55-races-no-return.c b/tests/regression/68-longjmp/55-races-no-return.c new file mode 100644 index 0000000000..ad0e5d4707 --- /dev/null +++ b/tests/regression/68-longjmp/55-races-no-return.c @@ -0,0 +1,51 @@ +// PARAM: --enable ana.int.interval +#include +#include +#include +#include +#include + +jmp_buf env_buffer; +int global = 0; +pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; + +void *t_fun(void *arg) { + pthread_mutex_lock(&mutex1); + global = 3; //NORACE + pthread_mutex_unlock(&mutex1); + return NULL; +} + +int bar() { + pthread_mutex_lock(&mutex1); + if(global == 7) { + longjmp(env_buffer, 2); + } else { + longjmp(env_buffer, 4); + } + return 8; +} + +int main() { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + int n = 0; + + switch(setjmp( env_buffer )) { + case 0: + bar(); + break; + case 2: + n=1; + pthread_mutex_unlock(&mutex1); + break; + default: + break; + } + + global = 5; //NORACE + + if(n == 0) { + pthread_mutex_unlock(&mutex1); + } +} From 18fe6d1b55fb54f193e1b5ce8506c937ce2d571d Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sat, 16 Sep 2023 16:47:51 +0200 Subject: [PATCH 0622/1312] 68/[52-55] Don't include `goblint.h`, it is unused --- tests/regression/68-longjmp/52-races.c | 1 - tests/regression/68-longjmp/53-races-no.c | 1 - tests/regression/68-longjmp/54-races-actually.c | 3 +-- tests/regression/68-longjmp/55-races-no-return.c | 1 - 4 files changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/regression/68-longjmp/52-races.c b/tests/regression/68-longjmp/52-races.c index 5c7ac6daa6..4cde97d954 100644 --- a/tests/regression/68-longjmp/52-races.c +++ b/tests/regression/68-longjmp/52-races.c @@ -2,7 +2,6 @@ #include #include #include -#include #include jmp_buf env_buffer; diff --git a/tests/regression/68-longjmp/53-races-no.c b/tests/regression/68-longjmp/53-races-no.c index 7247f14941..4692f6ca18 100644 --- a/tests/regression/68-longjmp/53-races-no.c +++ b/tests/regression/68-longjmp/53-races-no.c @@ -2,7 +2,6 @@ #include #include #include -#include #include jmp_buf env_buffer; diff --git a/tests/regression/68-longjmp/54-races-actually.c b/tests/regression/68-longjmp/54-races-actually.c index 12f1f791c5..62423cd884 100644 --- a/tests/regression/68-longjmp/54-races-actually.c +++ b/tests/regression/68-longjmp/54-races-actually.c @@ -2,7 +2,6 @@ #include #include #include -#include #include jmp_buf env_buffer; @@ -44,7 +43,7 @@ int main() { } global = 5; //RACE - + if(n == 0) { pthread_mutex_unlock(&mutex1); } diff --git a/tests/regression/68-longjmp/55-races-no-return.c b/tests/regression/68-longjmp/55-races-no-return.c index ad0e5d4707..850fc54fa5 100644 --- a/tests/regression/68-longjmp/55-races-no-return.c +++ b/tests/regression/68-longjmp/55-races-no-return.c @@ -2,7 +2,6 @@ #include #include #include -#include #include jmp_buf env_buffer; From 1feb75e2aa06ccacec63b39e52cfc0661b5d9a80 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 18 Sep 2023 11:31:51 +0300 Subject: [PATCH 0623/1312] Fix Cilfacade.split_anoncomp_name for empty names (closes #1171) --- src/domains/access.ml | 4 ++-- src/util/cilfacade.ml | 9 +++++---- unittest/mainTest.ml | 1 + unittest/util/cilfacadeTest.ml | 14 ++++++++++++++ 4 files changed, 22 insertions(+), 6 deletions(-) create mode 100644 unittest/util/cilfacadeTest.ml diff --git a/src/domains/access.ml b/src/domains/access.ml index 83903ba355..fa6446df16 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -16,8 +16,8 @@ let is_ignorable_type (t: typ): bool = | TComp ({ cname = "__pthread_mutex_s" | "__pthread_rwlock_arch_t" | "__jmp_buf_tag" | "_pthread_cleanup_buffer" | "__pthread_cleanup_frame" | "__cancel_jmp_buf_tag"; _}, _) -> true | TComp ({ cname; _}, _) when String.starts_with_stdlib ~prefix:"__anon" cname -> begin match Cilfacade.split_anoncomp_name cname with - | (true, ("__once_flag" | "__pthread_unwind_buf_t" | "__cancel_jmp_buf"), _) -> true (* anonstruct *) - | (false, ("pthread_mutexattr_t" | "pthread_condattr_t" | "pthread_barrierattr_t"), _) -> true (* anonunion *) + | (true, Some ("__once_flag" | "__pthread_unwind_buf_t" | "__cancel_jmp_buf"), _) -> true (* anonstruct *) + | (false, Some ("pthread_mutexattr_t" | "pthread_condattr_t" | "pthread_barrierattr_t"), _) -> true (* anonunion *) | _ -> false end | TComp ({ cname = "lock_class_key"; _ }, _) -> true diff --git a/src/util/cilfacade.ml b/src/util/cilfacade.ml index c77d2cd738..f0ac3202ce 100644 --- a/src/util/cilfacade.ml +++ b/src/util/cilfacade.ml @@ -342,22 +342,23 @@ let makeBinOp binop e1 e2 = let (_, e) = Cabs2cil.doBinOp binop e1 t1 e2 t2 in e -let anoncomp_name_regexp = Str.regexp {|^__anon\(struct\|union\)_\(.+\)_\([0-9]+\)$|} +let anoncomp_name_regexp = Str.regexp {|^__anon\(struct\|union\)\(_\(.+\)\)?_\([0-9]+\)$|} let split_anoncomp_name name = (* __anonunion_pthread_mutexattr_t_488594144 *) + (* __anonunion_50 *) if Str.string_match anoncomp_name_regexp name 0 then ( let struct_ = match Str.matched_group 1 name with | "struct" -> true | "union" -> false | _ -> assert false in - let name' = Str.matched_group 2 name in - let id = int_of_string (Str.matched_group 3 name) in + let name' = try Some (Str.matched_group 3 name) with Not_found -> None in + let id = int_of_string (Str.matched_group 4 name) in (struct_, name', id) ) else - invalid_arg "Cilfacade.split_anoncomp_name" + invalid_arg ("Cilfacade.split_anoncomp_name: " ^ name) (** Pretty-print typsig like typ, because {!d_typsig} prints with CIL constructors. *) diff --git a/unittest/mainTest.ml b/unittest/mainTest.ml index df67340309..642e495d50 100644 --- a/unittest/mainTest.ml +++ b/unittest/mainTest.ml @@ -8,6 +8,7 @@ let all_tests = ("" >::: LvalTest.test (); CompilationDatabaseTest.tests; LibraryDslTest.tests; + CilfacadeTest.tests; (* etc *) "domaintest" >::: QCheck_ounit.to_ounit2_test_list Maindomaintest.all_testsuite; IntOpsTest.tests; diff --git a/unittest/util/cilfacadeTest.ml b/unittest/util/cilfacadeTest.ml new file mode 100644 index 0000000000..482a502824 --- /dev/null +++ b/unittest/util/cilfacadeTest.ml @@ -0,0 +1,14 @@ +open Goblint_lib +open OUnit2 +open Cilfacade + +let test_split_anoncomp_name _ = + let assert_equal = assert_equal ~printer:[%show: bool * string option * int] in + assert_equal (false, Some "pthread_mutexattr_t", 488594144) (split_anoncomp_name "__anonunion_pthread_mutexattr_t_488594144"); + assert_equal (true, Some "__once_flag", 1234) (split_anoncomp_name "__anonstruct___once_flag_1234"); + assert_equal (false, None, 50) (split_anoncomp_name "__anonunion_50") + +let tests = + "cilfacadeTest" >::: [ + "split_anoncomp_name" >:: test_split_anoncomp_name; + ] From 444428467d44a3d188d034c4418d34117543b838 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Mon, 18 Sep 2023 11:36:23 +0300 Subject: [PATCH 0624/1312] Remove duplicate vsprintf and add vsnprintf --- src/analyses/libraryFunctions.ml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 2eb68dd437..912867d319 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -103,7 +103,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("vfprintf", unknown [drop "stream" [r_deep; w_deep]; drop "format" [r]; drop "vlist" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) ("vsprintf", unknown [drop "buffer" [w]; drop "format" [r]; drop "vlist" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) ("vasprintf", unknown [drop "strp" [w]; drop "format" [r]; drop "ap" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) - ("vsprintf", unknown [drop "str" [w]; drop "format" [r]; drop "ap" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) + ("vsnprintf", unknown [drop "str" [w]; drop "size" []; drop "format" [r]; drop "ap" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) ("mktime", unknown [drop "tm" [r;w]]); ("ctime", unknown ~attrs:[ThreadUnsafe] [drop "rm" [r]]); ("clearerr", unknown [drop "stream" [w]]); @@ -1089,7 +1089,6 @@ let invalidate_actions = [ "usleep", readsAll; "svc_run", writesAll;(*unsafe*) "dup", readsAll; (*safe*) - "vsnprintf", writesAllButFirst 3 readsAll; (*drop 3*) "__builtin___vsnprintf", writesAllButFirst 3 readsAll; (*drop 3*) "__builtin___vsnprintf_chk", writesAllButFirst 3 readsAll; (*drop 3*) "strcasecmp", readsAll; (*safe*) From 684cfa8c3f45afcefd95a1096e954fad27af8d91 Mon Sep 17 00:00:00 2001 From: Karoliine Holter <44437975+karoliineh@users.noreply.github.com> Date: Mon, 18 Sep 2023 11:37:22 +0300 Subject: [PATCH 0625/1312] Apply suggestions from code review Co-authored-by: Simmo Saan --- src/analyses/libraryFunctions.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 912867d319..b883c7e354 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -828,7 +828,7 @@ let pcre_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ] let zlib_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ - ("inflate", unknown [drop "strm" [r_deep]; drop "flush" []]); + ("inflate", unknown [drop "strm" [r_deep; w_deep]; drop "flush" []]); ("inflateInit2", unknown [drop "strm" [r_deep; w_deep]; drop "windowBits" []]); ("inflateInit2_", unknown [drop "strm" [r_deep; w_deep]; drop "windowBits" []; drop "version" [r]; drop "stream_size" []]); ("inflateEnd", unknown [drop "strm" [f_deep]]); @@ -837,7 +837,7 @@ let zlib_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ let liblzma_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("lzma_code", unknown [drop "strm" [r_deep; w_deep]; drop "action" []]); ("lzma_auto_decoder", unknown [drop "strm" [r_deep; w_deep]; drop "memlimit" []; drop "flags" []]); - ("lzma_end", unknown [drop "strm" [r_deep; w_deep]]); + ("lzma_end", unknown [drop "strm" [r_deep; w_deep; f_deep]]); ] let libraries = Hashtbl.of_list [ From e69d13c0cd4cf73a9bbda511627ee1844a39bbbc Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 18 Sep 2023 11:49:06 +0300 Subject: [PATCH 0626/1312] Add thread self creation non-terminating test from libaco --- tests/regression/10-synch/07-thread_self_create.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 tests/regression/10-synch/07-thread_self_create.c diff --git a/tests/regression/10-synch/07-thread_self_create.c b/tests/regression/10-synch/07-thread_self_create.c new file mode 100644 index 0000000000..473a26a25b --- /dev/null +++ b/tests/regression/10-synch/07-thread_self_create.c @@ -0,0 +1,15 @@ +// PARAM: --set ana.activated[+] thread +// Checks termination of thread analysis with a thread who is its own single parent. +#include + +void *t_fun(void *arg) { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + return NULL; +} + +int main(void) { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + return 0; +} From 2486404315e4e7d44512134637d1eaff78def99e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 18 Sep 2023 11:55:59 +0300 Subject: [PATCH 0627/1312] Fix 10-synch/07-thread_self_create --- src/analyses/threadAnalysis.ml | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/analyses/threadAnalysis.ml b/src/analyses/threadAnalysis.ml index d6a93744bc..1e679a4707 100644 --- a/src/analyses/threadAnalysis.ml +++ b/src/analyses/threadAnalysis.ml @@ -32,13 +32,21 @@ struct let rec is_not_unique ctx tid = let (rep, parents, _) = ctx.global tid in - let n = TS.cardinal parents in - (* A thread is not unique if it is - * a) repeatedly created, - * b) created in multiple threads, or - * c) created by a thread that is itself multiply created. - * Note that starting threads have empty ancestor sets! *) - rep || n > 1 || n > 0 && is_not_unique ctx (TS.choose parents) + if rep then + true (* repeatedly created *) + else ( + let n = TS.cardinal parents in + if n > 1 then + true (* created in multiple threads *) + else if n > 0 then ( + (* created by single thread *) + let parent = TS.choose parents in + (* created by itself thread-recursively or by a thread that is itself multiply created *) + T.equal tid parent || is_not_unique ctx parent (* equal check needed to avoid infinte self-recursion *) + ) + else + false (* no ancestors, starting thread *) + ) let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = let desc = LibraryFunctions.find f in From a32be380cd024523408b4e1348eab0d65da2842c Mon Sep 17 00:00:00 2001 From: karoliineh Date: Mon, 18 Sep 2023 15:06:56 +0300 Subject: [PATCH 0628/1312] Use AddressDomain in eval_funvar --- src/analyses/base.ml | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 1364c009d5..912e298143 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1097,20 +1097,15 @@ struct | Int x -> ValueDomain.ID.to_int x | _ -> None - let eval_funvar ctx fval: varinfo list = - let exception OnlyUnknown in - try - let fp = eval_fv (Analyses.ask_of_ctx ctx) ctx.global ctx.local fval in - if AD.mem Addr.UnknownPtr fp then begin - let others = AD.to_var_may fp in - if others = [] then raise OnlyUnknown; - M.warn ~category:Imprecise "Function pointer %a may contain unknown functions." d_exp fval; - dummyFunDec.svar :: others - end else - AD.to_var_may fp - with SetDomain.Unsupported _ | OnlyUnknown -> - M.warn ~category:Unsound "Unknown call to function %a." d_exp fval; - [dummyFunDec.svar] + let eval_funvar ctx fval: Queries.AD.t = + let fp = eval_fv (Analyses.ask_of_ctx ctx) ctx.global ctx.local fval in + if AD.is_top fp then begin + if AD.cardinal fp = 1 then + (M.warn ~category:Unsound "Unknown call to function %a." d_exp fval;) + else + (M.warn ~category:Imprecise "Function pointer %a may contain unknown functions." d_exp fval;) + end; + fp (** Evaluate expression as address. Avoids expensive Apron EvalInt if the Int result would be useless to us anyway. *) @@ -1204,10 +1199,7 @@ struct let query ctx (type a) (q: a Q.t): a Q.result = match q with | Q.EvalFunvar e -> - begin - let fs = eval_funvar ctx e in - List.fold_left (fun ad v -> Q.AD.join (Q.AD.of_var v) ad) (Q.AD.empty ()) fs - end + eval_funvar ctx e | Q.EvalJumpBuf e -> begin match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local e with | Address jmp_buf -> From 3879fdc2a1f3ced2cc11c15cacd36250219b6744 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Mon, 18 Sep 2023 15:12:19 +0300 Subject: [PATCH 0629/1312] Use relevants_from_ad instead of relevants_from_ls --- src/analyses/modifiedSinceLongjmp.ml | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/analyses/modifiedSinceLongjmp.ml b/src/analyses/modifiedSinceLongjmp.ml index fafbe54840..5dae8748cb 100644 --- a/src/analyses/modifiedSinceLongjmp.ml +++ b/src/analyses/modifiedSinceLongjmp.ml @@ -23,7 +23,8 @@ struct (* Only checks for v.vglob on purpose, acessing espaced locals after longjmp is UB like for any local *) not v.vglob (* *) && not (BaseUtil.is_volatile v) && v.vstorage <> Static - let relevants_from_ls ls = + let relevants_from_ad ls = + (* TODO: what about AD with both known and unknown pointers? *) if Queries.AD.is_top ls then VS.top () else @@ -33,23 +34,12 @@ struct | _ -> acc ) ls (VS.empty ()) - let relevants_from_ad ad = - (* TODO: what about AD with both known and unknown pointers? *) - if Queries.AD.is_top ad then - VS.top () - else - Queries.AD.fold (fun addr vs -> - match addr with - | Queries.AD.Addr.Addr (v,_) -> if is_relevant v then VS.add v vs else vs - | _ -> vs - ) ad (VS.empty ()) - (* transfer functions *) let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = [ctx.local, D.bot ()] (* enter with bot as opposed to IdentitySpec *) let combine_env ctx lval fexp f args fc au (f_ask: Queries.ask) = - let taintedcallee = relevants_from_ls (f_ask.f Queries.MayBeTainted) in + let taintedcallee = relevants_from_ad (f_ask.f Queries.MayBeTainted) in add_to_all_defined taintedcallee ctx.local let combine_assign ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) (f_ask:Queries.ask) : D.t = From 29179999053aa95fcdfe44b3fb83f501be9f7b07 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Mon, 18 Sep 2023 15:47:48 +0300 Subject: [PATCH 0630/1312] Use VS domain instead of AD for MustProtectedVars --- src/analyses/commonPriv.ml | 11 +++-------- src/analyses/mutexAnalysis.ml | 4 ++-- src/domains/queries.ml | 6 +++--- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index 7d06844c1b..db75455b40 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -60,15 +60,10 @@ struct ask.f (Q.MustBeProtectedBy {mutex=m; global=x; write=true; protection}) let protected_vars (ask: Q.ask): varinfo list = - let module VS = Set.Make (CilType.Varinfo) in Q.AD.fold (fun m acc -> - Q.AD.fold (fun l acc -> - match l with - | Q.AD.Addr.Addr (v,_) -> VS.add v acc (* always `NoOffset from mutex analysis *) - | _ -> acc - ) (ask.f (Q.MustProtectedVars {mutex = m; write = true})) acc - ) (ask.f Q.MustLockset) VS.empty - |> VS.elements + Q.VS.join (ask.f (Q.MustProtectedVars {mutex = m; write = true})) acc + ) (ask.f Q.MustLockset) (Q.VS.empty ()) + |> Q.VS.elements end module MutexGlobals = diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 9cc019db76..5a61976ef5 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -240,8 +240,8 @@ struct | Queries.MustProtectedVars {mutex = m; write} -> let protected = GProtected.get ~write Strong (G.protected (ctx.global (V.protected m))) in VarSet.fold (fun v acc -> - Queries.AD.join (Queries.AD.of_var v) acc - ) protected (Queries.AD.empty ()) + Queries.VS.add v acc + ) protected (Queries.VS.empty ()) | Queries.IterSysVars (Global g, f) -> f (Obj.repr (V.protecting g)) (* TODO: something about V.protected? *) | WarnGlobal g -> diff --git a/src/domains/queries.ml b/src/domains/queries.ml index e51ee90f68..3a0e493640 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -114,7 +114,7 @@ type _ t = | CreatedThreads: ConcDomain.ThreadSet.t t | MustJoinedThreads: ConcDomain.MustThreadSet.t t | ThreadsJoinedCleanly: MustBool.t t - | MustProtectedVars: mustprotectedvars -> AD.t t + | MustProtectedVars: mustprotectedvars -> VS.t t | Invariant: invariant_context -> Invariant.t t | InvariantGlobal: Obj.t -> Invariant.t t (** Argument must be of corresponding [Spec.V.t]. *) | WarnGlobal: Obj.t -> Unit.t t (** Argument must be of corresponding [Spec.V.t]. *) @@ -179,7 +179,7 @@ struct | CreatedThreads -> (module ConcDomain.ThreadSet) | MustJoinedThreads -> (module ConcDomain.MustThreadSet) | ThreadsJoinedCleanly -> (module MustBool) - | MustProtectedVars _ -> (module AD) + | MustProtectedVars _ -> (module VS) | Invariant _ -> (module Invariant) | InvariantGlobal _ -> (module Invariant) | WarnGlobal _ -> (module Unit) @@ -243,7 +243,7 @@ struct | CreatedThreads -> ConcDomain.ThreadSet.top () | MustJoinedThreads -> ConcDomain.MustThreadSet.top () | ThreadsJoinedCleanly -> MustBool.top () - | MustProtectedVars _ -> AD.top () + | MustProtectedVars _ -> VS.top () | Invariant _ -> Invariant.top () | InvariantGlobal _ -> Invariant.top () | WarnGlobal _ -> Unit.top () From c9c3980b6eea0022a3bd95b15ad05563cfa0c85b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 18 Sep 2023 15:51:16 +0300 Subject: [PATCH 0631/1312] Add missing library functions for nnn --- src/analyses/libraryFunctions.ml | 43 ++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 137a3103a5..7b92ff8a86 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -117,12 +117,14 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("wcrtomb", unknown ~attrs:[ThreadUnsafe] [drop "s" [w]; drop "wc" []; drop "ps" [r_deep; w_deep]]); ("wcstombs", unknown ~attrs:[ThreadUnsafe] [drop "dst" [w]; drop "src" [r]; drop "size" []]); ("wcsrtombs", unknown ~attrs:[ThreadUnsafe] [drop "dst" [w]; drop "src" [r_deep; w]; drop "size" []; drop "ps" [r_deep; w_deep]]); + ("mbstowcs", unknown [drop "dest" [w]; drop "src" [r]; drop "n" []]); ("abs", unknown [drop "j" []]); ("localtime_r", unknown [drop "timep" [r]; drop "result" [w]]); ("strpbrk", unknown [drop "s" [r]; drop "accept" [r]]); ("_setjmp", special [__ "env" [w]] @@ fun env -> Setjmp { env }); (* only has one underscore *) ("setjmp", special [__ "env" [w]] @@ fun env -> Setjmp { env }); ("longjmp", special [__ "env" [r]; __ "value" []] @@ fun env value -> Longjmp { env; value }); + ("atexit", unknown [drop "function" [s]]); ] (** C POSIX library functions. @@ -246,6 +248,8 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("timer_gettime", unknown [drop "timerid" []; drop "curr_value" [w_deep]]); ("timer_getoverrun", unknown [drop "timerid" []]); ("lstat", unknown [drop "pathname" [r]; drop "statbuf" [w]]); + ("fstat", unknown [drop "fd" []; drop "buf" [w]]); + ("fstatat", unknown [drop "dirfd" []; drop "pathname" [r]; drop "buf" [w]; drop "flags" []]); ("getpwnam", unknown [drop "name" [r]]); ("chdir", unknown [drop "path" [r]]); ("closedir", unknown [drop "dirp" [r]]); @@ -292,6 +296,23 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("getaddrinfo", unknown [drop "node" [r]; drop "service" [r]; drop "hints" [r_deep]; drop "res" [w]]); (* only write res non-deep because it doesn't write to existing fields of res *) ("fnmatch", unknown [drop "pattern" [r]; drop "string" [r]; drop "flags" []]); ("realpath", unknown [drop "path" [r]; drop "resolved_path" [w]]); + ("memccpy", special [__ "dest" [w]; __ "src" [r]; drop "c" []; drop "n" []] @@ fun dest src -> Memcpy {dest; src}); + ("dprintf", unknown (drop "fd" [] :: drop "format" [r] :: VarArgs (drop' [r]))); + ("mkdtemp", unknown [drop "template" [r; w]]); + ("mkstemp", unknown [drop "template" [r; w]]); + ("regcomp", unknown [drop "preg" [w_deep]; drop "regex" [r]; drop "cflags" []]); + ("regexec", unknown [drop "preg" [r_deep]; drop "string" [r]; drop "nmatch" []; drop "pmatch" [w_deep]; drop "eflags" []]); + ("regfree", unknown [drop "preg" [f_deep]]); + ("ffs", unknown [drop "i" []]); + ("_exit", special [drop "status" []] Abort); + ("execvp", unknown [drop "file" [r]; drop "argv" [r_deep]]); + ("statvfs", unknown [drop "path" [r]; drop "buf" [w]]); + ("readlink", unknown [drop "path" [r]; drop "buf" [w]; drop "bufsz" []]); + ("wcswidth", unknown [drop "s" [r]; drop "n" []]); + ("link", unknown [drop "oldpath" [r]; drop "newpath" [r]]); + ("renameat", unknown [drop "olddirfd" []; drop "oldpath" [r]; drop "newdirfd" []; drop "newpath" [r]]); + ("posix_fadvise", unknown [drop "fd" []; drop "offset" []; drop "len" []; drop "advice" []]); + ("getppid", unknown []); ] (** Pthread functions. *) @@ -454,6 +475,9 @@ let glibc_desc_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("fopencookie", unknown [drop "cookie" []; drop "mode" [r]; drop "io_funcs" [s_deep]]); (* doesn't access cookie but passes it to io_funcs *) ("mempcpy", special [__ "dest" [w]; __ "src" [r]; drop "n" []] @@ fun dest src -> Memcpy { dest; src }); ("__builtin___mempcpy_chk", special [__ "dest" [w]; __ "src" [r]; drop "n" []; drop "os" []] @@ fun dest src -> Memcpy { dest; src }); + ("rawmemchr", unknown [drop "s" [r]; drop "c" []]); + ("memrchr", unknown [drop "s" [r]; drop "c" []; drop "n" []]); + ("memmem", unknown [drop "haystack" [r]; drop "haystacklen" []; drop "needle" [r]; drop "needlelen" [r]]); ] let linux_userspace_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ @@ -470,6 +494,12 @@ let linux_userspace_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__xpg_basename", unknown [drop "path" [r]]); ("ptrace", unknown (drop "request" [] :: VarArgs (drop' [r_deep; w_deep]))); (* man page has 4 arguments, but header has varargs and real-world programs may call with <4 *) ("madvise", unknown [drop "addr" []; drop "length" []; drop "advice" []]); + ("inotify_init1", unknown [drop "flags" []]); + ("inotify_add_watch", unknown [drop "fd" []; drop "pathname" [r]; drop "mask" []]); + ("inotify_rm_watch", unknown [drop "fd" []; drop "wd" []]); + ("fts_open", unknown [drop "path_argv" [r_deep]; drop "options" []; drop "compar" [s]]); (* TODO: use Call instead of Spawn *) + ("fts_read", unknown [drop "ftsp" [r_deep; w_deep]]); + ("fts_close", unknown [drop "ftsp" [f_deep]]); ] let big_kernel_lock = AddrOf (Cil.var (Cilfacade.create_var (makeGlobalVar "[big kernel lock]" intType))) @@ -801,10 +831,14 @@ let ncurses_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("wattrset", unknown [drop "win" [r_deep; w_deep]; drop "attrs" []]); ("endwin", unknown []); ("wgetch", unknown [drop "win" [r_deep; w_deep]]); + ("wget_wch", unknown [drop "win" [r_deep; w_deep]; drop "wch" [w]]); + ("unget_wch", unknown [drop "wch" []]); ("wmove", unknown [drop "win" [r_deep; w_deep]; drop "y" []; drop "x" []]); ("waddch", unknown [drop "win" [r_deep; w_deep]; drop "ch" []]); + ("waddnstr", unknown [drop "win" [r_deep; w_deep]; drop "str" [r]; drop "n" []]); ("waddnwstr", unknown [drop "win" [r_deep; w_deep]; drop "wstr" [r]; drop "n" []]); ("wattr_on", unknown [drop "win" [r_deep; w_deep]; drop "attrs" []; drop "opts" []]); (* opts argument currently not used *) + ("wattr_off", unknown [drop "win" [r_deep; w_deep]; drop "attrs" []; drop "opts" []]); (* opts argument currently not used *) ("wrefresh", unknown [drop "win" [r_deep; w_deep]]); ("mvprintw", unknown (drop "win" [r_deep; w_deep] :: drop "y" [] :: drop "x" [] :: drop "fmt" [r] :: VarArgs (drop' [r]))); ("initscr", unknown []); @@ -813,10 +847,19 @@ let ncurses_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("start_color", unknown []); ("use_default_colors", unknown []); ("wclear", unknown [drop "win" [r_deep; w_deep]]); + ("wclrtoeol", unknown [drop "win" [r_deep; w_deep]]); ("can_change_color", unknown []); ("init_color", unknown [drop "color" []; drop "red" []; drop "green" []; drop "blue" []]); ("init_pair", unknown [drop "pair" []; drop "f" [r]; drop "b" [r]]); ("wbkgd", unknown [drop "win" [r_deep; w_deep]; drop "ch" []]); + ("keyname", unknown [drop "c" []]); + ("newterm", unknown [drop "type" [r]; drop "outfd" [r_deep; w_deep]; drop "infd" [r_deep; w_deep]]); + ("cbreak", unknown []); + ("nonl", unknown []); + ("keypad", unknown [drop "win" [r_deep; w_deep]; drop "bf" []]); + ("set_escdelay", unknown [drop "size" []]); + ("printw", unknown (drop "fmt" [r] :: VarArgs (drop' [r]))); + ("werase", unknown [drop "win" [r_deep; w_deep]]); ] let pcre_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ From ffc5c6d47b41d3389f203320bca03c55f5c5ef77 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 18 Sep 2023 17:19:01 +0300 Subject: [PATCH 0632/1312] Fix return_on_success for some pthread locking functions --- src/analyses/libraryFunctions.ml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 7b92ff8a86..6b024a5770 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -343,16 +343,16 @@ let pthread_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("pthread_mutexattr_destroy", unknown [drop "attr" [f]]); ("pthread_rwlock_init", unknown [drop "rwlock" [w]; drop "attr" [r]]); ("pthread_rwlock_destroy", unknown [drop "rwlock" [f]]); - ("pthread_rwlock_rdlock", special [__ "rwlock" []] @@ fun rwlock -> Lock {lock = rwlock; try_ = get_bool "sem.lock.fail"; write = false; return_on_success = true}); - ("pthread_rwlock_tryrdlock", special [__ "rwlock" []] @@ fun rwlock -> Lock {lock = rwlock; try_ = get_bool "sem.lock.fail"; write = false; return_on_success = true}); - ("pthread_rwlock_wrlock", special [__ "rwlock" []] @@ fun rwlock -> Lock {lock = rwlock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true}); + ("pthread_rwlock_rdlock", special [__ "rwlock" []] @@ fun rwlock -> Lock {lock = rwlock; try_ = get_bool "sem.lock.fail"; write = false; return_on_success = false}); + ("pthread_rwlock_tryrdlock", special [__ "rwlock" []] @@ fun rwlock -> Lock {lock = rwlock; try_ = true; write = false; return_on_success = false}); + ("pthread_rwlock_wrlock", special [__ "rwlock" []] @@ fun rwlock -> Lock {lock = rwlock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = false}); ("pthread_rwlock_trywrlock", special [__ "rwlock" []] @@ fun rwlock -> Lock {lock = rwlock; try_ = true; write = true; return_on_success = false}); ("pthread_rwlock_unlock", special [__ "rwlock" []] @@ fun rwlock -> Unlock rwlock); ("pthread_rwlockattr_init", unknown [drop "attr" [w]]); ("pthread_rwlockattr_destroy", unknown [drop "attr" [f]]); ("pthread_spin_init", unknown [drop "lock" [w]; drop "pshared" []]); ("pthread_spin_destroy", unknown [drop "lock" [f]]); - ("pthread_spin_lock", special [__ "lock" []] @@ fun lock -> Lock {lock = lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true}); + ("pthread_spin_lock", special [__ "lock" []] @@ fun lock -> Lock {lock = lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = false}); ("pthread_spin_trylock", special [__ "lock" []] @@ fun lock -> Lock {lock = lock; try_ = true; write = true; return_on_success = false}); ("pthread_spin_unlock", special [__ "lock" []] @@ fun lock -> Unlock lock); ("pthread_attr_init", unknown [drop "attr" [w]]); From 2d91b05500729f7fe0124754cc9e7ec6f9a37ce6 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 18 Sep 2023 17:30:51 +0300 Subject: [PATCH 0633/1312] Make pthread_join invalidate precise --- src/analyses/libraryFunctions.ml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 6b024a5770..b032c7ce86 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -319,6 +319,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ let pthread_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("pthread_create", special [__ "thread" [w]; drop "attr" [r]; __ "start_routine" [s]; __ "arg" []] @@ fun thread start_routine arg -> ThreadCreate { thread; start_routine; arg }); (* For precision purposes arg is not considered accessed here. Instead all accesses (if any) come from actually analyzing start_routine. *) ("pthread_exit", special [__ "retval" []] @@ fun retval -> ThreadExit { ret_val = retval }); (* Doesn't dereference the void* itself, but just passes to pthread_join. *) + ("pthread_join", special [__ "thread" []; __ "retval" [w]] @@ fun thread retval -> ThreadJoin {thread; ret_var = retval}); ("pthread_cond_init", unknown [drop "cond" [w]; drop "attr" [r]]); ("__pthread_cond_init", unknown [drop "cond" [w]; drop "attr" [r]]); ("pthread_cond_signal", special [__ "cond" []] @@ fun cond -> Signal cond); @@ -928,11 +929,6 @@ let classify fn exps: categories = `Unknown fn in match fn with - | "pthread_join" -> - begin match exps with - | [id; ret_var] -> `ThreadJoin (id, ret_var) - | _ -> strange_arguments () - end | "kmalloc" | "__kmalloc" | "usb_alloc_urb" | "__builtin_alloca" -> begin match exps with | size::_ -> `Malloc size From f27a6e7ced33f44ae9f61b37e82f0427b3440cf5 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 18 Sep 2023 17:42:28 +0300 Subject: [PATCH 0634/1312] Convert remaining old library function classify --- src/analyses/libraryFunctions.ml | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index b032c7ce86..99187f2ccc 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -64,6 +64,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__builtin_strcmp", special [__ "s1" [r]; __ "s2" [r]] @@ fun s1 s2 -> Strcmp { s1; s2; n = None; }); ("strncmp", special [__ "s1" [r]; __ "s2" [r]; __ "n" []] @@ fun s1 s2 n -> Strcmp { s1; s2; n = Some n; }); ("malloc", special [__ "size" []] @@ fun size -> Malloc size); + ("calloc", special [__ "n" []; __ "size" []] @@ fun n size -> Calloc {count = n; size}); ("realloc", special [__ "ptr" [r; f]; __ "size" []] @@ fun ptr size -> Realloc { ptr; size }); ("free", special [__ "ptr" [f]] @@ fun ptr -> Free ptr); ("abort", special [] Abort); @@ -434,6 +435,7 @@ let gcc_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__sync_fetch_and_add", unknown (drop "ptr" [r; w] :: drop "value" [] :: VarArgs (drop' []))); ("__sync_fetch_and_sub", unknown (drop "ptr" [r; w] :: drop "value" [] :: VarArgs (drop' []))); ("__builtin_va_copy", unknown [drop "dest" [w]; drop "src" [r]]); + ("__builtin_alloca", special [__ "size" []] @@ fun size -> Malloc size); ] let glibc_desc_list: (string * LibraryDesc.t) list = LibraryDsl.[ @@ -561,6 +563,10 @@ let linux_kernel_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__cmpxchg_wrong_size", special [] Abort); ("__xadd_wrong_size", special [] Abort); ("__put_user_bad", special [] Abort); + ("kmalloc", special [__ "size" []; drop "flags" []] @@ fun size -> Malloc size); + ("__kmalloc", special [__ "size" []; drop "flags" []] @@ fun size -> Malloc size); + ("kzalloc", special [__ "size" []; drop "flags" []] @@ fun size -> Calloc {count = Cil.one; size}); + ("usb_alloc_urb", special [__ "iso_packets" []; drop "mem_flags" []] @@ fun iso_packets -> Malloc iso_packets); (* TODO: iso_packets is size in bytes? *) ] (** Goblint functions. *) @@ -924,27 +930,7 @@ type categories = [ let classify fn exps: categories = - let strange_arguments () = - M.warn ~category:Program "%s arguments are strange!" fn; - `Unknown fn - in - match fn with - | "kmalloc" | "__kmalloc" | "usb_alloc_urb" | "__builtin_alloca" -> - begin match exps with - | size::_ -> `Malloc size - | _ -> strange_arguments () - end - | "kzalloc" -> - begin match exps with - | size::_ -> `Calloc (Cil.one, size) - | _ -> strange_arguments () - end - | "calloc" -> - begin match exps with - | n::size::_ -> `Calloc (n, size) - | _ -> strange_arguments () - end - | x -> `Unknown x + fn module Invalidate = @@ -1075,7 +1061,6 @@ let invalidate_actions = [ "sigaddset", writesAll;(*unsafe*) "raise", writesAll;(*unsafe*) "_strlen", readsAll;(*safe*) - "__builtin_alloca", readsAll;(*safe*) "dlopen", readsAll;(*safe*) "dlsym", readsAll;(*safe*) "dlclose", readsAll;(*safe*) From 4e83af9270b32e17f5c55ed0c7e1f7adffc93e01 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 18 Sep 2023 17:49:30 +0300 Subject: [PATCH 0635/1312] Remove old library function classify --- src/analyses/libraryDesc.ml | 25 ++--------------------- src/analyses/libraryFunctions.ml | 35 +++++++------------------------- 2 files changed, 9 insertions(+), 51 deletions(-) diff --git a/src/analyses/libraryDesc.ml b/src/analyses/libraryDesc.ml index 94de4fbf82..5f9f6f90f9 100644 --- a/src/analyses/libraryDesc.ml +++ b/src/analyses/libraryDesc.ml @@ -126,31 +126,10 @@ type t = { attrs: attr list; (** Attributes of function. *) } -let special_of_old classify_name = fun args -> - match classify_name args with - | `Malloc e -> Malloc e - | `Calloc (count, size) -> Calloc { count; size; } - | `Realloc (ptr, size) -> Realloc { ptr; size; } - | `Lock (try_, write, return_on_success) -> - begin match args with - | [lock] -> Lock { lock ; try_; write; return_on_success; } - | [] -> failwith "lock has no arguments" - | _ -> failwith "lock has multiple arguments" - end - | `Unlock -> - begin match args with - | [arg] -> Unlock arg - | [] -> failwith "unlock has no arguments" - | _ -> failwith "unlock has multiple arguments" - end - | `ThreadCreate (thread, start_routine, arg) -> ThreadCreate { thread; start_routine; arg; } - | `ThreadJoin (thread, ret_var) -> ThreadJoin { thread; ret_var; } - | `Unknown _ -> Unknown - -let of_old ?(attrs: attr list=[]) (old_accesses: Accesses.old) (classify_name): t = { +let of_old ?(attrs: attr list=[]) (old_accesses: Accesses.old): t = { attrs; accs = Accesses.of_old old_accesses; - special = special_of_old classify_name; + special = fun _ -> Unknown; } module MathPrintable = struct diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 99187f2ccc..7f5d7fc8d8 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -918,21 +918,6 @@ let activated_library_descs: (string, LibraryDesc.t) Hashtbl.t ResettableLazy.t let reset_lazy () = ResettableLazy.reset activated_library_descs -type categories = [ - | `Malloc of exp - | `Calloc of exp * exp - | `Realloc of exp * exp - | `Lock of bool * bool * bool (* try? * write? * return on success *) - | `Unlock - | `ThreadCreate of exp * exp * exp (* id * f * x *) - | `ThreadJoin of exp * exp (* id * ret_var *) - | `Unknown of string ] - - -let classify fn exps: categories = - fn - - module Invalidate = struct [@@@warning "-unused-value-declaration"] (* some functions are not used below *) @@ -1221,7 +1206,7 @@ let is_safe_uncalled fn_name = List.exists (fun r -> Str.string_match r fn_name 0) kernel_safe_uncalled_regex -let unknown_desc ~f name = (* TODO: remove name argument, unknown function shouldn't have classify *) +let unknown_desc f = let old_accesses (kind: AccessKind.t) args = match kind with | Write when GobConfig.get_bool "sem.unknown_function.invalidate.args" -> args | Write -> [] @@ -1239,16 +1224,10 @@ let unknown_desc ~f name = (* TODO: remove name argument, unknown function shoul else [] in - let classify_name args = - match classify name args with - | `Unknown _ as category -> - (* TODO: remove hack when all classify are migrated *) - if not (CilType.Varinfo.equal f dummyFunDec.svar) && not (use_special f.vname) then - M.error ~category:Imprecise ~tags:[Category Unsound] "Function definition missing for %s" f.vname; - category - | category -> category - in - LibraryDesc.of_old ~attrs old_accesses classify_name + (* TODO: remove hack when all classify are migrated *) + if not (CilType.Varinfo.equal f dummyFunDec.svar) && not (use_special f.vname) then + M.error ~category:Imprecise ~tags:[Category Unsound] "Function definition missing for %s" f.vname; + LibraryDesc.of_old ~attrs old_accesses let find f = let name = f.vname in @@ -1257,9 +1236,9 @@ let find f = | None -> match get_invalidate_action name with | Some old_accesses -> - LibraryDesc.of_old old_accesses (classify name) + LibraryDesc.of_old old_accesses | None -> - unknown_desc ~f name + unknown_desc f let is_special fv = From 0f59ac967b1fd37bbea7c6c4c2a785a136f36c03 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 19 Sep 2023 13:50:10 +0300 Subject: [PATCH 0636/1312] Fix Cilfacade.pretty_typsig_like_typ forgetting pointers from name on base type --- src/util/cilfacade.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/cilfacade.ml b/src/util/cilfacade.ml index f0ac3202ce..eb7330aa19 100644 --- a/src/util/cilfacade.ml +++ b/src/util/cilfacade.ml @@ -377,7 +377,7 @@ let rec pretty_typsig_like_typ (nameOpt: Pretty.doc option) () ts = | _ -> pa in match ts with - | TSBase t -> dn_type () t + | TSBase t -> defaultCilPrinter#pType nameOpt () t | TSComp (cstruct, cname, a) -> let su = if cstruct then "struct" else "union" in text (su ^ " " ^ cname ^ " ") From c5136d1cdf945b9990376476bbd0881ee2f817ad Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 19 Sep 2023 14:28:13 +0300 Subject: [PATCH 0637/1312] Add tests for library function races via NULL --- .../regression/05-lval_ls/20-race-null-void.c | 54 ++++++++++++++++++ .../regression/05-lval_ls/21-race-null-type.c | 55 ++++++++++++++++++ .../05-lval_ls/22-race-null-void-deep.c | 56 +++++++++++++++++++ .../05-lval_ls/23-race-null-type-deep.c | 54 ++++++++++++++++++ 4 files changed, 219 insertions(+) create mode 100644 tests/regression/05-lval_ls/20-race-null-void.c create mode 100644 tests/regression/05-lval_ls/21-race-null-type.c create mode 100644 tests/regression/05-lval_ls/22-race-null-void-deep.c create mode 100644 tests/regression/05-lval_ls/23-race-null-type-deep.c diff --git a/tests/regression/05-lval_ls/20-race-null-void.c b/tests/regression/05-lval_ls/20-race-null-void.c new file mode 100644 index 0000000000..1950ada73e --- /dev/null +++ b/tests/regression/05-lval_ls/20-race-null-void.c @@ -0,0 +1,54 @@ +#include +#include + +void *t_fun(void *arg) { + void **top; + free(top); // RACE + return NULL; +} + +int main(void) { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + + int r; // rand + int zero = 0; // IntDomain zero + void *null; + __goblint_assume(null == NULL); // AddressDomain NULL + int one = 1; // IntDomain one + void *unknown; + __goblint_assume(unknown != NULL); // AddressDomain unknown + void *top; + switch (r) { + case 0: + pthread_join(id, NULL); // NORACE + break; + case 1: + pthread_join(id, 0); // NORACE + break; + case 2: + pthread_join(id, zero); // NORACE + break; + case 3: + pthread_join(id, 1); // RACE + break; + case 4: + pthread_join(id, one); // RACE + break; + case 5: + pthread_join(id, r); // RACE + break; + case 6: + pthread_join(id, null); // NORACE + break; + case 7: + pthread_join(id, unknown); // RACE + break; + case 8: + pthread_join(id, top); // RACE + break; + default: + break; + } + return 0; +} diff --git a/tests/regression/05-lval_ls/21-race-null-type.c b/tests/regression/05-lval_ls/21-race-null-type.c new file mode 100644 index 0000000000..6b5e6e42fd --- /dev/null +++ b/tests/regression/05-lval_ls/21-race-null-type.c @@ -0,0 +1,55 @@ +// PARAM: --enable ana.race.direct-arithmetic +#include +#include + +void *t_fun(void *arg) { + void *top; + time(top); // RACE + return NULL; +} + +int main(void) { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + + int r; // rand + int zero = 0; // IntDomain zero + void *null; + __goblint_assume(null == NULL); // AddressDomain NULL + int one = 1; // IntDomain one + void *unknown; + __goblint_assume(unknown != NULL); // AddressDomain unknown + void *top; + switch (r) { + case 0: + time(NULL); // NORACE + break; + case 1: + time(0); // NORACE + break; + case 2: + time(zero); // NORACE + break; + case 3: + time(1); // RACE + break; + case 4: + time(one); // RACE + break; + case 5: + time(r); // RACE + break; + case 6: + time(null); // NORACE + break; + case 7: + time(unknown); // RACE + break; + case 8: + time(top); // RACE + break; + default: + break; + } + return 0; +} diff --git a/tests/regression/05-lval_ls/22-race-null-void-deep.c b/tests/regression/05-lval_ls/22-race-null-void-deep.c new file mode 100644 index 0000000000..7e99f286b6 --- /dev/null +++ b/tests/regression/05-lval_ls/22-race-null-void-deep.c @@ -0,0 +1,56 @@ +#include +#include + +pthread_key_t key; + +void *t_fun(void *arg) { + void *top; + pthread_setspecific(key, top); // RACE + return NULL; +} + +int main(void) { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + + int r; // rand + int zero = 0; // IntDomain zero + void *null; + __goblint_assume(null == NULL); // AddressDomain NULL + int one = 1; // IntDomain one + void *unknown; + __goblint_assume(unknown != NULL); // AddressDomain unknown + void *top; + switch (r) { + case 0: + pthread_setspecific(key, NULL); // NORACE + break; + case 1: + pthread_setspecific(key, 0); // NORACE + break; + case 2: + pthread_setspecific(key, zero); // NORACE + break; + case 3: + pthread_setspecific(key, 1); // RACE + break; + case 4: + pthread_setspecific(key, one); // RACE + break; + case 5: + pthread_setspecific(key, r); // RACE + break; + case 6: + pthread_setspecific(key, null); // NORACE + break; + case 7: + pthread_setspecific(key, unknown); // RACE + break; + case 8: + pthread_setspecific(key, top); // RACE + break; + default: + break; + } + return 0; +} diff --git a/tests/regression/05-lval_ls/23-race-null-type-deep.c b/tests/regression/05-lval_ls/23-race-null-type-deep.c new file mode 100644 index 0000000000..6f29964d1e --- /dev/null +++ b/tests/regression/05-lval_ls/23-race-null-type-deep.c @@ -0,0 +1,54 @@ +#include +#include + +void *t_fun(void *arg) { + void *top; + fclose(top); // RACE + return NULL; +} + +int main(void) { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + + int r; // rand + int zero = 0; // IntDomain zero + void *null; + __goblint_assume(null == NULL); // AddressDomain NULL + int one = 1; // IntDomain one + void *unknown; + __goblint_assume(unknown != NULL); // AddressDomain unknown + void *top; + switch (r) { + case 0: + feof(NULL); // NORACE + break; + case 1: + feof(0); // NORACE + break; + case 2: + feof(zero); // NORACE + break; + case 3: + feof(1); // RACE + break; + case 4: + feof(one); // RACE + break; + case 5: + feof(r); // RACE + break; + case 6: + feof(null); // NORACE + break; + case 7: + feof(unknown); // RACE + break; + case 8: + feof(top); // RACE + break; + default: + break; + } + return 0; +} From a4772d64f1d1532b2783ba56ef3f12cc65ad6e4d Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 19 Sep 2023 15:06:26 +0300 Subject: [PATCH 0638/1312] Add some missing library functions for shairport --- src/analyses/libraryFunctions.ml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 7f5d7fc8d8..85a0388eba 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -299,6 +299,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("realpath", unknown [drop "path" [r]; drop "resolved_path" [w]]); ("memccpy", special [__ "dest" [w]; __ "src" [r]; drop "c" []; drop "n" []] @@ fun dest src -> Memcpy {dest; src}); ("dprintf", unknown (drop "fd" [] :: drop "format" [r] :: VarArgs (drop' [r]))); + ("vdprintf", unknown [drop "fd" []; drop "format" [r]; drop "ap" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) ("mkdtemp", unknown [drop "template" [r; w]]); ("mkstemp", unknown [drop "template" [r; w]]); ("regcomp", unknown [drop "preg" [w_deep]; drop "regex" [r]; drop "cflags" []]); @@ -314,6 +315,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("renameat", unknown [drop "olddirfd" []; drop "oldpath" [r]; drop "newdirfd" []; drop "newpath" [r]]); ("posix_fadvise", unknown [drop "fd" []; drop "offset" []; drop "len" []; drop "advice" []]); ("getppid", unknown []); + ("lockf", unknown [drop "fd" []; drop "cmd" []; drop "len" []]); ] (** Pthread functions. *) @@ -321,6 +323,7 @@ let pthread_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("pthread_create", special [__ "thread" [w]; drop "attr" [r]; __ "start_routine" [s]; __ "arg" []] @@ fun thread start_routine arg -> ThreadCreate { thread; start_routine; arg }); (* For precision purposes arg is not considered accessed here. Instead all accesses (if any) come from actually analyzing start_routine. *) ("pthread_exit", special [__ "retval" []] @@ fun retval -> ThreadExit { ret_val = retval }); (* Doesn't dereference the void* itself, but just passes to pthread_join. *) ("pthread_join", special [__ "thread" []; __ "retval" [w]] @@ fun thread retval -> ThreadJoin {thread; ret_var = retval}); + ("pthread_kill", unknown [drop "thread" []; drop "sig" []]); ("pthread_cond_init", unknown [drop "cond" [w]; drop "attr" [r]]); ("__pthread_cond_init", unknown [drop "cond" [w]; drop "attr" [r]]); ("pthread_cond_signal", special [__ "cond" []] @@ fun cond -> Signal cond); @@ -481,6 +484,8 @@ let glibc_desc_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("rawmemchr", unknown [drop "s" [r]; drop "c" []]); ("memrchr", unknown [drop "s" [r]; drop "c" []; drop "n" []]); ("memmem", unknown [drop "haystack" [r]; drop "haystacklen" []; drop "needle" [r]; drop "needlelen" [r]]); + ("getifaddrs", unknown [drop "ifap" [w]]); + ("freeifaddrs", unknown [drop "ifa" [f_deep]]); ] let linux_userspace_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ From 83978e953dbbf7d6a8c920d071b380bbd1441637 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 19 Sep 2023 17:22:41 +0300 Subject: [PATCH 0639/1312] Fix ReachableFrom not adding back unknown pointer Regression from f7b38a0. --- src/analyses/base.ml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 71e2661997..c225da2939 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1287,7 +1287,11 @@ struct | Address a -> let a' = AD.remove Addr.UnknownPtr a in (* run reachable_vars without unknown just to be safe: TODO why? *) let addrs = reachable_vars (Analyses.ask_of_ctx ctx) [a'] ctx.global ctx.local in - List.fold_left (AD.join) (AD.empty ()) addrs + let addrs' = List.fold_left (AD.join) (AD.empty ()) addrs in + if AD.may_be_unknown a then + AD.add UnknownPtr addrs' (* add unknown back *) + else + addrs' | _ -> AD.empty () end | Q.ReachableUkTypes e -> begin From c458db248993021a753772beead0754697f2f73a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 19 Sep 2023 17:44:44 +0300 Subject: [PATCH 0640/1312] Add Int cases to MayPointTo and ReachableFrom queries --- src/analyses/base.ml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index c225da2939..4e7235de2b 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1270,6 +1270,7 @@ struct match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local e with | Address a -> a | Bot -> Queries.Result.bot q (* TODO: remove *) + | Int i -> AD.of_int i | _ -> Queries.Result.top q end | Q.EvalThread e -> begin @@ -1292,6 +1293,7 @@ struct AD.add UnknownPtr addrs' (* add unknown back *) else addrs' + | Int i -> AD.of_int i | _ -> AD.empty () end | Q.ReachableUkTypes e -> begin From 204594e695c92d106e6e9e628109228f20712339 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 19 Sep 2023 17:45:22 +0300 Subject: [PATCH 0641/1312] Fix ReachableFrom precision loss with int argument in 68-longjmp/41-poison-rec --- src/analyses/base.ml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 4e7235de2b..0e6ad8f516 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1293,7 +1293,12 @@ struct AD.add UnknownPtr addrs' (* add unknown back *) else addrs' - | Int i -> AD.of_int i + | Int i -> + begin match Cilfacade.typeOf e with + | t when Cil.isPointerType t -> AD.of_int i (* integer used as pointer *) + | _ + | exception Cilfacade.TypeOfError _ -> AD.empty () (* avoid unknown pointer result for non-pointer expression *) + end | _ -> AD.empty () end | Q.ReachableUkTypes e -> begin From d0e9064924f4548d4ac5fc277d5644cbbddcfcf3 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 19 Sep 2023 21:49:05 +0200 Subject: [PATCH 0642/1312] Fix `trace` calls outside of `if tracing` --- src/solvers/sLRphased.ml | 8 ++++---- src/solvers/sLRterm.ml | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/solvers/sLRphased.ml b/src/solvers/sLRphased.ml index f4c3389f1d..c120a7bc6c 100644 --- a/src/solvers/sLRphased.ml +++ b/src/solvers/sLRphased.ml @@ -73,7 +73,7 @@ module Make = let effects = ref Set.empty in let side y d = assert (not (S.Dom.is_bot d)); - trace "sol" "SIDE: Var: %a\nVal: %a\n" S.Var.pretty_trace y S.Dom.pretty d; + if tracing then trace "sol" "SIDE: Var: %a\nVal: %a\n" S.Var.pretty_trace y S.Dom.pretty d; let first = not (Set.mem y !effects) in effects := Set.add y !effects; if first then ( @@ -109,11 +109,11 @@ module Make = if wpx then if b then let nar = narrow old tmp in - trace "sol" "NARROW: Var: %a\nOld: %a\nNew: %a\nWiden: %a\n" S.Var.pretty_trace x S.Dom.pretty old S.Dom.pretty tmp S.Dom.pretty nar; + if tracing then trace "sol" "NARROW: Var: %a\nOld: %a\nNew: %a\nWiden: %a\n" S.Var.pretty_trace x S.Dom.pretty old S.Dom.pretty tmp S.Dom.pretty nar; nar else let wid = S.Dom.widen old (S.Dom.join old tmp) in - trace "sol" "WIDEN: Var: %a\nOld: %a\nNew: %a\nWiden: %a\n" S.Var.pretty_trace x S.Dom.pretty old S.Dom.pretty tmp S.Dom.pretty wid; + if tracing then trace "sol" "WIDEN: Var: %a\nOld: %a\nNew: %a\nWiden: %a\n" S.Var.pretty_trace x S.Dom.pretty old S.Dom.pretty tmp S.Dom.pretty wid; wid else tmp @@ -163,7 +163,7 @@ module Make = and sides x = let w = try HM.find set x with Not_found -> VS.empty in let v = Enum.fold (fun d z -> try S.Dom.join d (HPM.find rho' (z,x)) with Not_found -> d) (S.Dom.bot ()) (VS.enum w) - in trace "sol" "SIDES: Var: %a\nVal: %a\n" S.Var.pretty_trace x S.Dom.pretty v; v + in if tracing then trace "sol" "SIDES: Var: %a\nVal: %a\n" S.Var.pretty_trace x S.Dom.pretty v; v and eq x get set = eval_rhs_event x; match S.system x with diff --git a/src/solvers/sLRterm.ml b/src/solvers/sLRterm.ml index d4f4671c46..eb11447d11 100644 --- a/src/solvers/sLRterm.ml +++ b/src/solvers/sLRterm.ml @@ -64,14 +64,14 @@ module SLR3term = HM.replace rho x (S.Dom.bot ()); HM.replace infl x (VS.add x VS.empty); let c = if side then count_side else count in - trace "sol" "INIT: Var: %a with prio %d\n" S.Var.pretty_trace x !c; + if tracing then trace "sol" "INIT: Var: %a with prio %d\n" S.Var.pretty_trace x !c; HM.replace key x !c; decr c end in let sides x = let w = try HM.find set x with Not_found -> VS.empty in let v = Enum.fold (fun d z -> try S.Dom.join d (HPM.find rho' (z,x)) with Not_found -> d) (S.Dom.bot ()) (VS.enum w) in - trace "sol" "SIDES: Var: %a\nVal: %a\n" S.Var.pretty_trace x S.Dom.pretty v; v + if tracing then trace "sol" "SIDES: Var: %a\nVal: %a\n" S.Var.pretty_trace x S.Dom.pretty v; v in let rec iterate b_old prio = if H.size !q = 0 || min_key q > prio then () @@ -122,7 +122,7 @@ module SLR3term = ) *) (* if S.Dom.is_bot d then print_endline "BOT" else *) - trace "sol" "SIDE: Var: %a\nVal: %a\n" S.Var.pretty_trace y S.Dom.pretty d; + if tracing then trace "sol" "SIDE: Var: %a\nVal: %a\n" S.Var.pretty_trace y S.Dom.pretty d; let first = not (Set.mem y !effects) in effects := Set.add y !effects; if first then ( @@ -156,17 +156,17 @@ module SLR3term = if wpx then if S.Dom.leq tmp old then ( let nar = narrow old tmp in - trace "sol" "NARROW1: Var: %a\nOld: %a\nNew: %a\nNarrow: %a" S.Var.pretty_trace x S.Dom.pretty old S.Dom.pretty tmp S.Dom.pretty nar; + if tracing then trace "sol" "NARROW1: Var: %a\nOld: %a\nNew: %a\nNarrow: %a" S.Var.pretty_trace x S.Dom.pretty old S.Dom.pretty tmp S.Dom.pretty nar; nar, true ) else if b_old then ( let nar = narrow old tmp in - trace "sol" "NARROW2: Var: %a\nOld: %a\nNew: %a\nNarrow: %a" S.Var.pretty_trace x S.Dom.pretty old S.Dom.pretty tmp S.Dom.pretty nar; + if tracing then trace "sol" "NARROW2: Var: %a\nOld: %a\nNew: %a\nNarrow: %a" S.Var.pretty_trace x S.Dom.pretty old S.Dom.pretty tmp S.Dom.pretty nar; nar, true ) else ( let wid = S.Dom.widen old (S.Dom.join old tmp) in - trace "sol" "WIDEN: Var: %a\nOld: %a\nNew: %a\nWiden: %a" S.Var.pretty_trace x S.Dom.pretty old S.Dom.pretty tmp S.Dom.pretty wid; + if tracing then trace "sol" "WIDEN: Var: %a\nOld: %a\nNew: %a\nWiden: %a" S.Var.pretty_trace x S.Dom.pretty old S.Dom.pretty tmp S.Dom.pretty wid; wid, false ) else From f7851e89bdc9438007ecd4cd4405d9584559ea6d Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 20 Sep 2023 10:05:46 +0300 Subject: [PATCH 0643/1312] Move memccpy to c_descs_list for C23 --- src/analyses/libraryFunctions.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 85a0388eba..ee69e03faf 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -12,9 +12,10 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("memset", special [__ "dest" [w]; __ "ch" []; __ "count" []] @@ fun dest ch count -> Memset { dest; ch; count; }); ("__builtin_memset", special [__ "dest" [w]; __ "ch" []; __ "count" []] @@ fun dest ch count -> Memset { dest; ch; count; }); ("__builtin___memset_chk", special [__ "dest" [w]; __ "ch" []; __ "count" []; drop "os" []] @@ fun dest ch count -> Memset { dest; ch; count; }); - ("memcpy", special [__ "dest" [w]; __ "src" [r]; drop "n" []] @@ fun dest src -> Memcpy { dest; src }); + ("memcpy", special [__ "dest" [w]; __ "src" [r]; drop "n" []] @@ fun dest src -> Memcpy { dest; src }); (* TODO: use n *) ("__builtin_memcpy", special [__ "dest" [w]; __ "src" [r]; drop "n" []] @@ fun dest src -> Memcpy { dest; src }); ("__builtin___memcpy_chk", special [__ "dest" [w]; __ "src" [r]; drop "n" []; drop "os" []] @@ fun dest src -> Memcpy { dest; src }); + ("memccpy", special [__ "dest" [w]; __ "src" [r]; drop "c" []; drop "n" []] @@ fun dest src -> Memcpy {dest; src}); (* C23 *) (* TODO: use n and c *) ("memmove", special [__ "dest" [w]; __ "src" [r]; drop "count" []] @@ fun dest src -> Memcpy { dest; src }); ("__builtin_memmove", special [__ "dest" [w]; __ "src" [r]; drop "count" []] @@ fun dest src -> Memcpy { dest; src }); ("__builtin___memmove_chk", special [__ "dest" [w]; __ "src" [r]; drop "count" []; drop "os" []] @@ fun dest src -> Memcpy { dest; src }); @@ -297,7 +298,6 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("getaddrinfo", unknown [drop "node" [r]; drop "service" [r]; drop "hints" [r_deep]; drop "res" [w]]); (* only write res non-deep because it doesn't write to existing fields of res *) ("fnmatch", unknown [drop "pattern" [r]; drop "string" [r]; drop "flags" []]); ("realpath", unknown [drop "path" [r]; drop "resolved_path" [w]]); - ("memccpy", special [__ "dest" [w]; __ "src" [r]; drop "c" []; drop "n" []] @@ fun dest src -> Memcpy {dest; src}); ("dprintf", unknown (drop "fd" [] :: drop "format" [r] :: VarArgs (drop' [r]))); ("vdprintf", unknown [drop "fd" []; drop "format" [r]; drop "ap" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) ("mkdtemp", unknown [drop "template" [r; w]]); From d1d8d66dd1daeb3fce807cd2ac6cb8bc9b055a1f Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 20 Sep 2023 10:07:37 +0300 Subject: [PATCH 0644/1312] Use unknown_exp for usb_alloc_urb size --- src/analyses/libraryFunctions.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index ee69e03faf..c3ca48da93 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -571,7 +571,7 @@ let linux_kernel_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("kmalloc", special [__ "size" []; drop "flags" []] @@ fun size -> Malloc size); ("__kmalloc", special [__ "size" []; drop "flags" []] @@ fun size -> Malloc size); ("kzalloc", special [__ "size" []; drop "flags" []] @@ fun size -> Calloc {count = Cil.one; size}); - ("usb_alloc_urb", special [__ "iso_packets" []; drop "mem_flags" []] @@ fun iso_packets -> Malloc iso_packets); (* TODO: iso_packets is size in bytes? *) + ("usb_alloc_urb", special [__ "iso_packets" []; drop "mem_flags" []] @@ fun iso_packets -> Malloc MyCFG.unknown_exp); ] (** Goblint functions. *) From 71035d2900bd2c714378236456f2f91fc39d0221 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 20 Sep 2023 10:22:52 +0300 Subject: [PATCH 0645/1312] Add test for atexit soundness --- tests/regression/41-stdlib/07-atexit.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 tests/regression/41-stdlib/07-atexit.c diff --git a/tests/regression/41-stdlib/07-atexit.c b/tests/regression/41-stdlib/07-atexit.c new file mode 100644 index 0000000000..4551400175 --- /dev/null +++ b/tests/regression/41-stdlib/07-atexit.c @@ -0,0 +1,13 @@ +#include +#include + +void bye() +{ + __goblint_check(1); // reachable +} + +int main() +{ + atexit(bye); + return 0; +} From 89e007478b4d2d5b96181f42e946fff6f68d7a1f Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 20 Sep 2023 18:39:39 +0200 Subject: [PATCH 0646/1312] Make HeapVar support stack allocation, update IsHeapVar and add IsDynamicallyAlloced --- src/domains/queries.ml | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 0388ce2995..5ddf417699 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -99,8 +99,9 @@ type _ t = | IterVars: itervar -> Unit.t t | PathQuery: int * 'a t -> 'a t (** Query only one path under witness lifter. *) | DYojson: FlatYojson.t t (** Get local state Yojson of one path under [PathQuery]. *) - | HeapVar: VI.t t + | HeapVar: {on_stack: bool} -> VI.t t (* If on_stack is [true], then alloca() or a similar function was called *) | IsHeapVar: varinfo -> MayBool.t t (* TODO: is may or must? *) + | IsDynamicallyAlloced: varinfo -> MayBool.t t (* [true] if heap var represents dynamically alloced memory, [false] if it represents the result of an alloca() call *) | IsMultiple: varinfo -> MustBool.t t (* For locals: Is another copy of this local variable reachable via pointers? *) (* For dynamically allocated memory: Does this abstract variable corrrespond to a unique heap location? *) @@ -152,6 +153,7 @@ struct | MayBePublicWithout _ -> (module MayBool) | MayBeThreadReturn -> (module MayBool) | IsHeapVar _ -> (module MayBool) + | IsDynamicallyAlloced _ -> (module MayBool) | MustBeProtectedBy _ -> (module MustBool) | MustBeAtomic -> (module MustBool) | MustBeSingleThreaded _ -> (module MustBool) @@ -163,7 +165,7 @@ struct | BlobSize _ -> (module ID) | CurrentThreadId -> (module ThreadIdDomain.ThreadLifted) | ThreadCreateIndexedNode -> (module ThreadNodeLattice) - | HeapVar -> (module VI) + | HeapVar _ -> (module VI) | EvalStr _ -> (module SD) | IterPrevVars _ -> (module Unit) | IterVars _ -> (module Unit) @@ -216,6 +218,7 @@ struct | MayBePublicWithout _ -> MayBool.top () | MayBeThreadReturn -> MayBool.top () | IsHeapVar _ -> MayBool.top () + | IsDynamicallyAlloced _ -> MayBool.top () | MutexType _ -> MutexAttrDomain.top () | MustBeProtectedBy _ -> MustBool.top () | MustBeAtomic -> MustBool.top () @@ -228,7 +231,7 @@ struct | BlobSize _ -> ID.top () | CurrentThreadId -> ThreadIdDomain.ThreadLifted.top () | ThreadCreateIndexedNode -> ThreadNodeLattice.top () - | HeapVar -> VI.top () + | HeapVar _ -> VI.top () | EvalStr _ -> SD.top () | IterPrevVars _ -> Unit.top () | IterVars _ -> Unit.top () @@ -288,7 +291,7 @@ struct | Any (PartAccess _) -> 23 | Any (IterPrevVars _) -> 24 | Any (IterVars _) -> 25 - | Any HeapVar -> 29 + | Any (HeapVar _) -> 29 | Any (IsHeapVar _) -> 30 | Any (IsMultiple _) -> 31 | Any (EvalThread _) -> 32 @@ -313,6 +316,7 @@ struct | Any ThreadCreateIndexedNode -> 51 | Any ThreadsJoinedCleanly -> 52 | Any (TmpSpecial _) -> 53 + | Any (IsDynamicallyAlloced _) -> 54 let rec compare a b = let r = Stdlib.compare (order a) (order b) in @@ -347,6 +351,7 @@ struct else compare (Any q1) (Any q2) | Any (IsHeapVar v1), Any (IsHeapVar v2) -> CilType.Varinfo.compare v1 v2 + | Any (IsDynamicallyAlloced v1), Any (IsDynamicallyAlloced v2) -> CilType.Varinfo.compare v1 v2 | Any (IsMultiple v1), Any (IsMultiple v2) -> CilType.Varinfo.compare v1 v2 | Any (EvalThread e1), Any (EvalThread e2) -> CilType.Exp.compare e1 e2 | Any (EvalJumpBuf e1), Any (EvalJumpBuf e2) -> CilType.Exp.compare e1 e2 @@ -387,6 +392,7 @@ struct | Any (IterVars i) -> 0 | Any (PathQuery (i, q)) -> 31 * i + hash (Any q) | Any (IsHeapVar v) -> CilType.Varinfo.hash v + | Any (IsDynamicallyAlloced v) -> CilType.Varinfo.hash v | Any (IsMultiple v) -> CilType.Varinfo.hash v | Any (EvalThread e) -> CilType.Exp.hash e | Any (EvalJumpBuf e) -> CilType.Exp.hash e @@ -434,8 +440,9 @@ struct | Any (IterPrevVars i) -> Pretty.dprintf "IterPrevVars _" | Any (IterVars i) -> Pretty.dprintf "IterVars _" | Any (PathQuery (i, q)) -> Pretty.dprintf "PathQuery (%d, %a)" i pretty (Any q) - | Any HeapVar -> Pretty.dprintf "HeapVar" + | Any (HeapVar {on_stack = on_stack}) -> Pretty.dprintf "HeapVar %b" on_stack | Any (IsHeapVar v) -> Pretty.dprintf "IsHeapVar %a" CilType.Varinfo.pretty v + | Any (IsDynamicallyAlloced v) -> Pretty.dprintf "IsDynamicallyAlloced %a" CilType.Varinfo.pretty v | Any (IsMultiple v) -> Pretty.dprintf "IsMultiple %a" CilType.Varinfo.pretty v | Any (EvalThread e) -> Pretty.dprintf "EvalThread %a" CilType.Exp.pretty e | Any (EvalJumpBuf e) -> Pretty.dprintf "EvalJumpBuf %a" CilType.Exp.pretty e From 9a06189b7cb843aeb9d645f59c3d08b86d2c8f6c Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 20 Sep 2023 18:41:08 +0200 Subject: [PATCH 0647/1312] Implement answer for IsDynamicallyAlloced --- src/analyses/wrapperFunctionAnalysis.ml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/analyses/wrapperFunctionAnalysis.ml b/src/analyses/wrapperFunctionAnalysis.ml index d9bbdb6197..99fac46d6c 100644 --- a/src/analyses/wrapperFunctionAnalysis.ml +++ b/src/analyses/wrapperFunctionAnalysis.ml @@ -133,7 +133,7 @@ module MallocWrapper : MCPSpec = struct let query (ctx: (D.t, G.t, C.t, V.t) ctx) (type a) (q: a Q.t): a Q.result = let wrapper_node, counter = ctx.local in match q with - | Q.HeapVar -> + | Q.HeapVar {on_stack = on_stack} -> let node = match wrapper_node with | `Lifted wrapper_node -> wrapper_node | _ -> node_for_ctx ctx @@ -141,9 +141,12 @@ module MallocWrapper : MCPSpec = struct let count = UniqueCallCounter.find (`Lifted node) counter in let var = NodeVarinfoMap.to_varinfo (ctx.ask Q.CurrentThreadId, node, count) in var.vdecl <- UpdateCil.getLoc node; (* TODO: does this do anything bad for incremental? *) + if on_stack then var.vattr <- addAttribute (Attr ("stack_alloca", [])) var.vattr; (* If the call was for stack allocation, add an attr to mark the heap var *) `Lifted var | Q.IsHeapVar v -> NodeVarinfoMap.mem_varinfo v + | Q.IsDynamicallyAlloced v -> + NodeVarinfoMap.mem_varinfo v && not @@ hasAttribute "stack_alloca" v.vattr | Q.IsMultiple v -> begin match NodeVarinfoMap.from_varinfo v with | Some (_, _, c) -> UniqueCount.is_top c || not (ctx.ask Q.MustBeUniqueThread) From d8c59651549d3689a675579c1d9851aeeaa5cf56 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 20 Sep 2023 18:41:29 +0200 Subject: [PATCH 0648/1312] Update usage of HeapVar and IsHeapVar --- src/analyses/base.ml | 20 +++++++++++--------- src/analyses/mallocFresh.ml | 2 +- src/analyses/memLeak.ml | 4 ++-- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 71e2661997..1212ceb9f4 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -150,8 +150,8 @@ struct let longjmp_return = ref dummyFunDec.svar - let heap_var ctx = - let info = match (ctx.ask Q.HeapVar) with + let heap_var on_stack ctx = + let info = match (ctx.ask (Q.HeapVar {on_stack = on_stack})) with | `Lifted vinfo -> vinfo | _ -> failwith("Ran without a malloc analysis.") in info @@ -1254,7 +1254,7 @@ struct | Address a -> (* If there's a non-heap var or an offset in the lval set, we answer with bottom *) if AD.exists (function - | Addr (v,o) -> (not @@ ctx.ask (Queries.IsHeapVar v)) || o <> `NoOffset + | Addr (v,o) -> (not @@ ctx.ask (Queries.IsHeapVar v)) || (ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsDynamicallyAlloced v)) || o <> `NoOffset | _ -> false) a then Queries.Result.bot q else ( @@ -1995,7 +1995,7 @@ struct let check_invalid_mem_dealloc ctx special_fn ptr = let has_non_heap_var = AD.exists (function - | Addr (v,_) -> not (ctx.ask (Q.IsHeapVar v)) + | Addr (v,_) -> not (ctx.ask (Q.IsHeapVar v)) || (ctx.ask (Q.IsHeapVar v) && not @@ ctx.ask (Q.IsDynamicallyAlloced v)) | _ -> false) in let has_non_zero_offset = AD.exists (function @@ -2263,13 +2263,14 @@ struct | Unknown, "__goblint_assume_join" -> let id = List.hd args in Priv.thread_join ~force:true (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) id st - | Malloc size, _ -> begin + | Malloc size, fname -> begin match lv with | Some lv -> + let is_stack_alloc = fname = "alloc" || fname = "__builtin_alloca" in let heap_var = if (get_bool "sem.malloc.fail") - then AD.join (AD.of_var (heap_var ctx)) AD.null_ptr - else AD.of_var (heap_var ctx) + then AD.join (AD.of_var (heap_var is_stack_alloc ctx)) AD.null_ptr + else AD.of_var (heap_var is_stack_alloc ctx) in (* ignore @@ printf "malloc will allocate %a bytes\n" ID.pretty (eval_int ctx.ask gs st size); *) set_many ~ctx (Analyses.ask_of_ctx ctx) gs st [(heap_var, TVoid [], Blob (VD.bot (), eval_int (Analyses.ask_of_ctx ctx) gs st size, true)); @@ -2279,7 +2280,7 @@ struct | Calloc { count = n; size }, _ -> begin match lv with | Some lv -> (* array length is set to one, as num*size is done when turning into `Calloc *) - let heap_var = heap_var ctx in + let heap_var = heap_var false ctx in let add_null addr = if get_bool "sem.malloc.fail" then AD.join addr AD.null_ptr (* calloc can fail and return NULL *) @@ -2322,7 +2323,7 @@ struct let p_addr_get = get ask gs st p_addr' None in (* implicitly includes join of malloc value (VD.bot) *) let size_int = eval_int ask gs st size in let heap_val:value = Blob (p_addr_get, size_int, true) in (* copy old contents with new size *) - let heap_addr = AD.of_var (heap_var ctx) in + let heap_addr = AD.of_var (heap_var false ctx) in let heap_addr' = if get_bool "sem.malloc.fail" then AD.join heap_addr AD.null_ptr @@ -2563,6 +2564,7 @@ struct | MayBeThreadReturn | PartAccess _ | IsHeapVar _ + | IsDynamicallyAlloced _ | IsMultiple _ | CreatedThreads | MustJoinedThreads -> diff --git a/src/analyses/mallocFresh.ml b/src/analyses/mallocFresh.ml index 2c2b99a075..2eed772527 100644 --- a/src/analyses/mallocFresh.ml +++ b/src/analyses/mallocFresh.ml @@ -43,7 +43,7 @@ struct | Malloc _ | Calloc _ | Realloc _ -> - begin match ctx.ask HeapVar with + begin match ctx.ask (HeapVar {on_stack = false}) with | `Lifted var -> D.add var ctx.local | _ -> ctx.local end diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index 1bff7611a4..5b38ab79eb 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -44,7 +44,7 @@ struct | Realloc _ -> (* Warn about multi-threaded programs as soon as we encounter a dynamic memory allocation function *) warn_for_multi_threaded ctx; - begin match ctx.ask Queries.HeapVar with + begin match ctx.ask (Queries.HeapVar {on_stack = false}) with | `Lifted var -> D.add var state | _ -> state end @@ -53,7 +53,7 @@ struct | ad when not (Queries.AD.is_top ad) && Queries.AD.cardinal ad = 1 -> (* Note: Need to always set "ana.malloc.unique_address_count" to a value > 0 *) begin match Queries.AD.choose ad with - | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsMultiple v) -> D.remove v state (* Unique pointed to heap vars *) + | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsHeapVar v) && ctx.ask (Queries.IsDynamicallyAlloced v) && not @@ ctx.ask (Queries.IsMultiple v) -> D.remove v state (* Unique pointed to heap vars *) | _ -> state end | _ -> state From 954be03ad32fcb685d2e04f50849df537b6c38f9 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 20 Sep 2023 18:55:54 +0200 Subject: [PATCH 0649/1312] Add "alloca" as a library function --- src/analyses/libraryFunctions.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 137a3103a5..326dedf434 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -64,6 +64,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__builtin_strcmp", special [__ "s1" [r]; __ "s2" [r]] @@ fun s1 s2 -> Strcmp { s1; s2; n = None; }); ("strncmp", special [__ "s1" [r]; __ "s2" [r]; __ "n" []] @@ fun s1 s2 n -> Strcmp { s1; s2; n = Some n; }); ("malloc", special [__ "size" []] @@ fun size -> Malloc size); + ("alloca", special [__ "size" []] @@ fun size -> Malloc size); (* TODO: Maybe define a new special type [Alloca], just like [Malloc]? *) ("realloc", special [__ "ptr" [r; f]; __ "size" []] @@ fun ptr size -> Realloc { ptr; size }); ("free", special [__ "ptr" [f]] @@ fun ptr -> Free ptr); ("abort", special [] Abort); From aa97af86c23911bd05822149a7d9c9fc4f819ae2 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 21 Sep 2023 09:38:24 +0200 Subject: [PATCH 0650/1312] Add Alloca special function type --- src/analyses/base.ml | 16 ++++++++++++---- src/analyses/libraryDesc.ml | 1 + src/analyses/libraryFunctions.ml | 3 ++- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 1212ceb9f4..2138a51d41 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2263,14 +2263,22 @@ struct | Unknown, "__goblint_assume_join" -> let id = List.hd args in Priv.thread_join ~force:true (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) id st - | Malloc size, fname -> begin + | Alloca size, _ -> begin + match lv with + | Some lv -> + let heap_var = AD.of_var (heap_var true ctx) in + (* ignore @@ printf "alloca will allocate %a bytes\n" ID.pretty (eval_int ctx.ask gs st size); *) + set_many ~ctx (Analyses.ask_of_ctx ctx) gs st [(heap_var, TVoid [], Blob (VD.bot (), eval_int (Analyses.ask_of_ctx ctx) gs st size, true)); + (eval_lv (Analyses.ask_of_ctx ctx) gs st lv, (Cilfacade.typeOfLval lv), Address heap_var)] + | _ -> st + end + | Malloc size, _ -> begin match lv with | Some lv -> - let is_stack_alloc = fname = "alloc" || fname = "__builtin_alloca" in let heap_var = if (get_bool "sem.malloc.fail") - then AD.join (AD.of_var (heap_var is_stack_alloc ctx)) AD.null_ptr - else AD.of_var (heap_var is_stack_alloc ctx) + then AD.join (AD.of_var (heap_var false ctx)) AD.null_ptr + else AD.of_var (heap_var false ctx) in (* ignore @@ printf "malloc will allocate %a bytes\n" ID.pretty (eval_int ctx.ask gs st size); *) set_many ~ctx (Analyses.ask_of_ctx ctx) gs st [(heap_var, TVoid [], Blob (VD.bot (), eval_int (Analyses.ask_of_ctx ctx) gs st size, true)); diff --git a/src/analyses/libraryDesc.ml b/src/analyses/libraryDesc.ml index 94de4fbf82..7cf68aa561 100644 --- a/src/analyses/libraryDesc.ml +++ b/src/analyses/libraryDesc.ml @@ -43,6 +43,7 @@ type math = (** Type of special function, or {!Unknown}. *) (* Use inline record if not single {!Cil.exp} argument. *) type special = + | Alloca of Cil.exp | Malloc of Cil.exp | Calloc of { count: Cil.exp; size: Cil.exp; } | Realloc of { ptr: Cil.exp; size: Cil.exp; } diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 326dedf434..0cc27f3523 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -63,8 +63,8 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("strtok", unknown ~attrs:[ThreadUnsafe] [drop "str" [r; w]; drop "delim" [r]]); ("__builtin_strcmp", special [__ "s1" [r]; __ "s2" [r]] @@ fun s1 s2 -> Strcmp { s1; s2; n = None; }); ("strncmp", special [__ "s1" [r]; __ "s2" [r]; __ "n" []] @@ fun s1 s2 n -> Strcmp { s1; s2; n = Some n; }); + ("alloca", special [__ "size" []] @@ fun size -> Alloca size); ("malloc", special [__ "size" []] @@ fun size -> Malloc size); - ("alloca", special [__ "size" []] @@ fun size -> Malloc size); (* TODO: Maybe define a new special type [Alloca], just like [Malloc]? *) ("realloc", special [__ "ptr" [r; f]; __ "size" []] @@ fun ptr size -> Realloc { ptr; size }); ("free", special [__ "ptr" [f]] @@ fun ptr -> Free ptr); ("abort", special [] Abort); @@ -1037,6 +1037,7 @@ let invalidate_actions = [ "sigaddset", writesAll;(*unsafe*) "raise", writesAll;(*unsafe*) "_strlen", readsAll;(*safe*) + "alloca", readsAll;(*safe*) "__builtin_alloca", readsAll;(*safe*) "dlopen", readsAll;(*safe*) "dlsym", readsAll;(*safe*) From 6e9bb2f121114a9c91bf1bb19557bbcfd0d3ffca Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 21 Sep 2023 09:39:42 +0200 Subject: [PATCH 0651/1312] Classify __builtin_alloca as Alloca and not as Malloc --- src/analyses/libraryFunctions.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 0cc27f3523..224b54907f 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -64,6 +64,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__builtin_strcmp", special [__ "s1" [r]; __ "s2" [r]] @@ fun s1 s2 -> Strcmp { s1; s2; n = None; }); ("strncmp", special [__ "s1" [r]; __ "s2" [r]; __ "n" []] @@ fun s1 s2 n -> Strcmp { s1; s2; n = Some n; }); ("alloca", special [__ "size" []] @@ fun size -> Alloca size); + ("__builtin_alloca", special [__ "size" []] @@ fun size -> Alloca size); ("malloc", special [__ "size" []] @@ fun size -> Malloc size); ("realloc", special [__ "ptr" [r; f]; __ "size" []] @@ fun ptr size -> Realloc { ptr; size }); ("free", special [__ "ptr" [f]] @@ fun ptr -> Free ptr); @@ -891,7 +892,7 @@ let classify fn exps: categories = | [id; ret_var] -> `ThreadJoin (id, ret_var) | _ -> strange_arguments () end - | "kmalloc" | "__kmalloc" | "usb_alloc_urb" | "__builtin_alloca" -> + | "kmalloc" | "__kmalloc" | "usb_alloc_urb" -> begin match exps with | size::_ -> `Malloc size | _ -> strange_arguments () From d9df607e99ea78600b81f879955208ec00391bcb Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 21 Sep 2023 09:49:48 +0200 Subject: [PATCH 0652/1312] Implement and use IsHeapVar and IsDynamicallyAlloced right --- src/analyses/base.ml | 8 ++++---- src/analyses/memLeak.ml | 2 +- src/analyses/useAfterFree.ml | 4 ++-- src/analyses/wrapperFunctionAnalysis.ml | 4 ++-- src/domains/queries.ml | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 2138a51d41..7ee49603e9 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1254,7 +1254,7 @@ struct | Address a -> (* If there's a non-heap var or an offset in the lval set, we answer with bottom *) if AD.exists (function - | Addr (v,o) -> (not @@ ctx.ask (Queries.IsHeapVar v)) || (ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsDynamicallyAlloced v)) || o <> `NoOffset + | Addr (v,o) -> (not @@ ctx.ask (Queries.IsDynamicallyAlloced v)) || (ctx.ask (Queries.IsDynamicallyAlloced v) && not @@ ctx.ask (Queries.IsHeapVar v)) || o <> `NoOffset | _ -> false) a then Queries.Result.bot q else ( @@ -1383,7 +1383,7 @@ struct let t = match t_override with | Some t -> t | None -> - if a.f (Q.IsHeapVar x) then + if a.f (Q.IsDynamicallyAlloced x) then (* the vtype of heap vars will be TVoid, so we need to trust the pointer we got to this to be of the right type *) (* i.e. use the static type of the pointer here *) lval_type @@ -1429,7 +1429,7 @@ struct (* Optimization to avoid evaluating integer values when setting them. The case when invariant = true requires the old_value to be sound for the meet. Allocated blocks are representend by Blobs with additional information, so they need to be looked-up. *) - let old_value = if not invariant && Cil.isIntegralType x.vtype && not (a.f (IsHeapVar x)) && offs = `NoOffset then begin + let old_value = if not invariant && Cil.isIntegralType x.vtype && not (a.f (IsDynamicallyAlloced x)) && offs = `NoOffset then begin VD.bot_value ~varAttr:x.vattr lval_type end else Priv.read_global a priv_getg st x @@ -1995,7 +1995,7 @@ struct let check_invalid_mem_dealloc ctx special_fn ptr = let has_non_heap_var = AD.exists (function - | Addr (v,_) -> not (ctx.ask (Q.IsHeapVar v)) || (ctx.ask (Q.IsHeapVar v) && not @@ ctx.ask (Q.IsDynamicallyAlloced v)) + | Addr (v,_) -> not (ctx.ask (Q.IsDynamicallyAlloced v)) || (ctx.ask (Q.IsDynamicallyAlloced v) && not @@ ctx.ask (Q.IsHeapVar v)) | _ -> false) in let has_non_zero_offset = AD.exists (function diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index 5b38ab79eb..abf0deb954 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -53,7 +53,7 @@ struct | ad when not (Queries.AD.is_top ad) && Queries.AD.cardinal ad = 1 -> (* Note: Need to always set "ana.malloc.unique_address_count" to a value > 0 *) begin match Queries.AD.choose ad with - | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsHeapVar v) && ctx.ask (Queries.IsDynamicallyAlloced v) && not @@ ctx.ask (Queries.IsMultiple v) -> D.remove v state (* Unique pointed to heap vars *) + | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsDynamicallyAlloced v) && ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsMultiple v) -> D.remove v state (* Unique pointed to heap vars *) | _ -> state end | _ -> state diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index 0aafbd1ad4..8c70322553 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -91,7 +91,7 @@ struct let pointed_to_heap_vars = Queries.AD.fold (fun addr vars -> match addr with - | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsHeapVar v) -> v :: vars + | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsDynamicallyAlloced v) -> v :: vars | _ -> vars ) ad [] in @@ -185,7 +185,7 @@ struct let pointed_to_heap_vars = Queries.AD.fold (fun addr state -> match addr with - | Queries.AD.Addr.Addr (var,_) when ctx.ask (Queries.IsHeapVar var) -> D.add var state + | Queries.AD.Addr.Addr (var,_) when ctx.ask (Queries.IsDynamicallyAlloced var) -> D.add var state | _ -> state ) ad (D.empty ()) in diff --git a/src/analyses/wrapperFunctionAnalysis.ml b/src/analyses/wrapperFunctionAnalysis.ml index 99fac46d6c..43d472d0e3 100644 --- a/src/analyses/wrapperFunctionAnalysis.ml +++ b/src/analyses/wrapperFunctionAnalysis.ml @@ -144,9 +144,9 @@ module MallocWrapper : MCPSpec = struct if on_stack then var.vattr <- addAttribute (Attr ("stack_alloca", [])) var.vattr; (* If the call was for stack allocation, add an attr to mark the heap var *) `Lifted var | Q.IsHeapVar v -> - NodeVarinfoMap.mem_varinfo v - | Q.IsDynamicallyAlloced v -> NodeVarinfoMap.mem_varinfo v && not @@ hasAttribute "stack_alloca" v.vattr + | Q.IsDynamicallyAlloced v -> + NodeVarinfoMap.mem_varinfo v | Q.IsMultiple v -> begin match NodeVarinfoMap.from_varinfo v with | Some (_, _, c) -> UniqueCount.is_top c || not (ctx.ask Q.MustBeUniqueThread) diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 5ddf417699..a5d1c727ab 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -101,7 +101,7 @@ type _ t = | DYojson: FlatYojson.t t (** Get local state Yojson of one path under [PathQuery]. *) | HeapVar: {on_stack: bool} -> VI.t t (* If on_stack is [true], then alloca() or a similar function was called *) | IsHeapVar: varinfo -> MayBool.t t (* TODO: is may or must? *) - | IsDynamicallyAlloced: varinfo -> MayBool.t t (* [true] if heap var represents dynamically alloced memory, [false] if it represents the result of an alloca() call *) + | IsDynamicallyAlloced: varinfo -> MayBool.t t (* [true] if heap var represents dynamically alloced memory *) | IsMultiple: varinfo -> MustBool.t t (* For locals: Is another copy of this local variable reachable via pointers? *) (* For dynamically allocated memory: Does this abstract variable corrrespond to a unique heap location? *) From 4c84a102c2093066b28032cd0dc0cd4a653034c9 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 21 Sep 2023 12:50:44 +0200 Subject: [PATCH 0653/1312] CI run fix: remove alloca from invalidate_actions --- src/analyses/libraryFunctions.ml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 224b54907f..800d3e4902 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -1038,7 +1038,6 @@ let invalidate_actions = [ "sigaddset", writesAll;(*unsafe*) "raise", writesAll;(*unsafe*) "_strlen", readsAll;(*safe*) - "alloca", readsAll;(*safe*) "__builtin_alloca", readsAll;(*safe*) "dlopen", readsAll;(*safe*) "dlsym", readsAll;(*safe*) From 8336d28999086a3ae18b8c6cbd976ea916bde07d Mon Sep 17 00:00:00 2001 From: sallto <68823230+sallto@users.noreply.github.com> Date: Sat, 23 Sep 2023 16:38:26 +0200 Subject: [PATCH 0654/1312] fix: copying of files to GobView in projects with subdirectories (#1143) * fix: macos command line file * fix: special paths for macos and non-english languages special paths are os and language dependant. ex. -> for macos * fix: prevent recursive directory copies when using gobview * fix: correct counter for gobview files Counter for unique file names was broken by skipping directory entries * fix: remove unnecessary recursive copying --------- Co-authored-by: Michael Schwarz --- src/maingoblint.ml | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/maingoblint.ml b/src/maingoblint.ml index 8944d87ea0..155faa0e76 100644 --- a/src/maingoblint.ml +++ b/src/maingoblint.ml @@ -417,10 +417,9 @@ let parse_preprocessed preprocessed = let goblint_cwd = GobFpath.cwd () in let get_ast_and_record_deps (preprocessed_file, task_opt) = - let transform_file (path_str, system_header) = match path_str with - | "" | "" -> + let transform_file (path_str, system_header) = if Str.string_match (Str.regexp "<.+>") path_str 0 then (path_str, system_header) (* ignore special "paths" *) - | _ -> + else let path = Fpath.v path_str in let path' = if get_bool "pre.transform-paths" then ( let cwd_opt = @@ -584,19 +583,19 @@ let do_gobview cilfile = let file_dir = Fpath.(run_dir / "files") in GobSys.mkdir_or_exists file_dir; let file_loc = Hashtbl.create 113 in - let counter = ref 0 in - let copy path = + let copy (path, i) = let name, ext = Fpath.split_ext (Fpath.base path) in - let unique_name = Fpath.add_ext ext (Fpath.add_ext (string_of_int !counter) name) in - counter := !counter + 1; + let unique_name = Fpath.add_ext ext (Fpath.add_ext (string_of_int i) name) in let dest = Fpath.(file_dir // unique_name) in let gobview_path = match Fpath.relativize ~root:run_dir dest with | Some p -> Fpath.to_string p | None -> failwith "The gobview directory should be a prefix of the paths of c files copied to the gobview directory" in Hashtbl.add file_loc (Fpath.to_string path) gobview_path; - FileUtil.cp [Fpath.to_string path] (Fpath.to_string dest) in + FileUtil.cp [Fpath.to_string path] (Fpath.to_string dest) + in let source_paths = Preprocessor.FpathH.to_list Preprocessor.dependencies |> List.concat_map (fun (_, m) -> Fpath.Map.fold (fun p _ acc -> p::acc) m []) in - List.iter copy source_paths; + let source_file_paths = List.filteri_map (fun i e -> if Fpath.is_file_path e then Some (e, i) else None) source_paths in + List.iter copy source_file_paths; Serialize.marshal file_loc (Fpath.(run_dir / "file_loc.marshalled")); (* marshal timing statistics *) let stats = Fpath.(run_dir / "stats.marshalled") in From fa77d12fd4012fdeae4928c049b09ba18565cb47 Mon Sep 17 00:00:00 2001 From: Nathan Schmidt Date: Sat, 23 Sep 2023 17:45:35 +0200 Subject: [PATCH 0655/1312] Solve failure `Queries.ID.unlift` --- src/analyses/base.ml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 420612ba1a..3810a92277 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2097,13 +2097,17 @@ struct | Bot, Array array_s2 -> (* If we have bot inside here, we assume the blob is used as a char array and create one inside *) let size = ctx.ask (Q.BlobSize s1) in - let s_id = ValueDomainQueries.ID.unlift (Fun.id) size in + let s_id = + try ValueDomainQueries.ID.unlift (Fun.id) size + with Failure _ -> ID.top_of ILong in let empty_array = CArrays.make (ID.cast_to (Cilfacade.ptrdiff_ikind ()) s_id) (Int (ID.top_of IChar)) in set ~ctx (Analyses.ask_of_ctx ctx) gs st lv_a lv_typ (op_array empty_array array_s2) | Bot , _ when CilType.Typ.equal s2_typ charPtrType -> (* If we have bot inside here, we assume the blob is used as a char array and create one inside *) let size = ctx.ask (Q.BlobSize s1) in - let s_id = ValueDomainQueries.ID.unlift (Fun.id) size in + let s_id = + try ValueDomainQueries.ID.unlift (Fun.id) size + with Failure _ -> ID.top_of ILong in let empty_array = CArrays.make (ID.cast_to (Cilfacade.ptrdiff_ikind ()) s_id) (Int (ID.top_of IChar)) in let s2_null_bytes = List.map CArrays.to_null_byte_domain (AD.to_string s2_a) in let array_s2 = List.fold_left CArrays.join (CArrays.bot ()) s2_null_bytes in From 34c2037190aff2e3117f1bb2f3d46b2978430a5b Mon Sep 17 00:00:00 2001 From: Nathan Schmidt Date: Sat, 23 Sep 2023 20:59:23 +0200 Subject: [PATCH 0656/1312] Draft for new regression tests --- .../73-strings/09-dynamic_char_arrays.c | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 tests/regression/73-strings/09-dynamic_char_arrays.c diff --git a/tests/regression/73-strings/09-dynamic_char_arrays.c b/tests/regression/73-strings/09-dynamic_char_arrays.c new file mode 100644 index 0000000000..58f9eba1e1 --- /dev/null +++ b/tests/regression/73-strings/09-dynamic_char_arrays.c @@ -0,0 +1,92 @@ +// PARAM: --disable ana.base.limit-string-addresses --enable ana.int.interval --enable ana.base.arrays.nullbytes + +#include +#include +#include + +int main () { + example1(); + example2(); + example3(); + example4(); + + return 0; +} + +void example1() { + size_t i; + if (rand()) + i = 0; + else + i = 1; + + char* s1 = malloc(50); + s1 = "goblint"; // must null at 7, may nulls starting from 7 + __goblint_check(s1[i] != '\0'); + + char* s2 = malloc(6); + s2 = "\0\0\0\0\0"; // NOWARN: all must and may nulls + __goblint_check(s2[i] == '\0'); + + strcpy(s1, s2); // must null at 0 and 7, mays nulls at 0 and starting from 7 + __goblint_check(s1[i] == '\0'); // UNKNOWN + + s1[i] = 'a'; // must null at 7, mays nulls at 0 and starting from 7 + + size_t len = strlen(s1); + __goblint_check(len >= 0); + __goblint_check(len > 0); // UNKNOWN + __goblint_check(len <= 7); + + s2[0] = 'a'; // all must and may null >= 1 + __goblint_check(s2[i] == '\0'); // UNKNOWN +} + +void example2() { + char* s1 = malloc(50); + for (size_t i = 0; i < 50; i++) + s1[i] = '\0'; + __goblint_check(s1[0] == '\0'); // UNKNOWN: no must nulls, all may nulls + + char* s2 = malloc(50); + for (size_t i = 0; i < 50; i++) + s2[i] = 'a'; + __goblint_check(s2[10] != '\0'); // no must and may nulls + + strcpy(s1, s2); // WARN: no must and may nulls + strcpy(s2, "definite buffer overflow"); // WARN + + s2[49] = '\0'; // must and may null at 49 + + strncpy(s1, s2, 10); // WARN +} + +void example3() { + char* s1 = malloc(10); // no must null, all may nulls + char* s2 = malloc(10); // no must null, all may nulls + strncpy(s1, s2, 4); // WARN: no must null, all may nulls + __goblint_check(s1[3] == '\0'); // UNKNOWN + + s1[0] = 'a'; + s1[1] = 'b'; // no must null, may nulls >= 2 + + strcat(s1, s2); // WARN: no must null, may nulls >= 2 + __goblint_check(s1[1] != '\0'); + __goblint_check(s1[2] == '\0'); // UNKNOWN + + int cmp = strncmp(s1, s2, 0); + __goblint_check(cmp == 0); +} + +void example4() { + size_t size; + if (rand()) + size = 15; + else + size = 20; + + char* s = malloc(size); + + s[17] = '\0'; // no must nulls, may null at 17 + __goblint_check(s[17] == '\0'); // UNKNOWN! +} \ No newline at end of file From e0d9a2add72e00d06e57a7dc85e5693c76495c2f Mon Sep 17 00:00:00 2001 From: Nathan Schmidt Date: Sat, 23 Sep 2023 22:43:12 +0200 Subject: [PATCH 0657/1312] Updated regression tests --- tests/regression/73-strings/05-char_arrays.c | 97 +++++++++++++++++++ .../73-strings/09-dynamic_char_arrays.c | 92 ------------------ 2 files changed, 97 insertions(+), 92 deletions(-) delete mode 100644 tests/regression/73-strings/09-dynamic_char_arrays.c diff --git a/tests/regression/73-strings/05-char_arrays.c b/tests/regression/73-strings/05-char_arrays.c index c86a0b1ebc..edb5a2ab57 100644 --- a/tests/regression/73-strings/05-char_arrays.c +++ b/tests/regression/73-strings/05-char_arrays.c @@ -15,6 +15,11 @@ int main() { example8(); example9(); example10(); + example11(); + example12(); + example13(); + example14(); + example15(); return 0; } @@ -231,3 +236,95 @@ void example10() { i = strncmp(s1, s2, 10); // WARN __goblint_check(i != 0); // UNKNOWN } + +void example11() { + size_t i; + if (rand()) + i = 0; + else + i = 1; + + char s1[50] = "goblint"; // must null at 7, may nulls starting from 7 + __goblint_check(s1[i] != '\0'); + + char s2[6] = "\0\0\0\0\0"; // all must and may nulls + __goblint_check(s2[i] == '\0'); + + strcpy(s1, s2); // must null at 0 and 7, mays nulls at 0 and starting from 7 + __goblint_check(s1[i] == '\0'); // UNKNOWN + + s1[i] = 'a'; // must null at 7, mays nulls at 0 and starting from 7 + + size_t len = strlen(s1); + __goblint_check(len >= 0); + __goblint_check(len > 0); // UNKNOWN + __goblint_check(len <= 7); + + s2[0] = 'a'; // all must and may null >= 1 + __goblint_check(s2[i] == '\0'); // UNKNOWN +} + +void example12() { + char s1[50]; + for (size_t i = 0; i < 50; i++) + s1[i] = '\0'; + __goblint_check(s1[0] == '\0'); // no must null, all may nulls + __goblint_check(s1[1] == '\0'); // known by trivial array domain + + char s2[5]; + s2[0] = 'a'; s2[1] = 'a'; s2[2] = 'a'; s2[3] = 'a'; s2[4] ='a'; + __goblint_check(s2[10] != '\0'); // no must null and may nulls + + strcpy(s1, s2); // WARN: no must nulls, may nulls >= 5 + strcpy(s2, "definite buffer overflow"); // WARN + + s2[4] = '\0'; // must and may null at 4 + + strncpy(s1, s2, 4); // WARN +} + +void example13() { + char s1[10]; // no must null, all may nulls + char s2[10]; // no must null, all may nulls + strncpy(s1, s2, 4); // WARN: no must null, all may nulls + __goblint_check(s1[3] == '\0'); // UNKNOWN + + s1[0] = 'a'; + s1[1] = 'b'; // no must null, may nulls >= 2 + + strcat(s1, s2); // WARN: no must null, may nulls >= 2 + __goblint_check(s1[1] != '\0'); + __goblint_check(s1[2] == '\0'); // UNKNOWN + + int cmp = strncmp(s1, s2, 0); + __goblint_check(cmp == 0); +} + +void example14() { + size_t size; + if (rand()) + size = 15; + else + size = 20; + + char* s = malloc(size); + + strcpy(s, ""); // must null at 0, all may null + + strcat(s, "123456789012345678"); // WARN +} + +example15() { + char* s1 = malloc(8); + strcpy(s1, "goblint"); // must and may null at 7 + + char s2[42] = "static"; // must null at 6, may null >= 6 + + strcat(s2, s1); // must null at 13, may null >= 13 + __goblint_check(s2[12] != '\0'); + __goblint_check(s2[13] == '\0'); + __goblint_check(s2[14] == '\0'); // UNKNOWN + + char* s3 = strstr(s1, s2); + __goblint_check(s3 == NULL); +} diff --git a/tests/regression/73-strings/09-dynamic_char_arrays.c b/tests/regression/73-strings/09-dynamic_char_arrays.c deleted file mode 100644 index 58f9eba1e1..0000000000 --- a/tests/regression/73-strings/09-dynamic_char_arrays.c +++ /dev/null @@ -1,92 +0,0 @@ -// PARAM: --disable ana.base.limit-string-addresses --enable ana.int.interval --enable ana.base.arrays.nullbytes - -#include -#include -#include - -int main () { - example1(); - example2(); - example3(); - example4(); - - return 0; -} - -void example1() { - size_t i; - if (rand()) - i = 0; - else - i = 1; - - char* s1 = malloc(50); - s1 = "goblint"; // must null at 7, may nulls starting from 7 - __goblint_check(s1[i] != '\0'); - - char* s2 = malloc(6); - s2 = "\0\0\0\0\0"; // NOWARN: all must and may nulls - __goblint_check(s2[i] == '\0'); - - strcpy(s1, s2); // must null at 0 and 7, mays nulls at 0 and starting from 7 - __goblint_check(s1[i] == '\0'); // UNKNOWN - - s1[i] = 'a'; // must null at 7, mays nulls at 0 and starting from 7 - - size_t len = strlen(s1); - __goblint_check(len >= 0); - __goblint_check(len > 0); // UNKNOWN - __goblint_check(len <= 7); - - s2[0] = 'a'; // all must and may null >= 1 - __goblint_check(s2[i] == '\0'); // UNKNOWN -} - -void example2() { - char* s1 = malloc(50); - for (size_t i = 0; i < 50; i++) - s1[i] = '\0'; - __goblint_check(s1[0] == '\0'); // UNKNOWN: no must nulls, all may nulls - - char* s2 = malloc(50); - for (size_t i = 0; i < 50; i++) - s2[i] = 'a'; - __goblint_check(s2[10] != '\0'); // no must and may nulls - - strcpy(s1, s2); // WARN: no must and may nulls - strcpy(s2, "definite buffer overflow"); // WARN - - s2[49] = '\0'; // must and may null at 49 - - strncpy(s1, s2, 10); // WARN -} - -void example3() { - char* s1 = malloc(10); // no must null, all may nulls - char* s2 = malloc(10); // no must null, all may nulls - strncpy(s1, s2, 4); // WARN: no must null, all may nulls - __goblint_check(s1[3] == '\0'); // UNKNOWN - - s1[0] = 'a'; - s1[1] = 'b'; // no must null, may nulls >= 2 - - strcat(s1, s2); // WARN: no must null, may nulls >= 2 - __goblint_check(s1[1] != '\0'); - __goblint_check(s1[2] == '\0'); // UNKNOWN - - int cmp = strncmp(s1, s2, 0); - __goblint_check(cmp == 0); -} - -void example4() { - size_t size; - if (rand()) - size = 15; - else - size = 20; - - char* s = malloc(size); - - s[17] = '\0'; // no must nulls, may null at 17 - __goblint_check(s[17] == '\0'); // UNKNOWN! -} \ No newline at end of file From 5ebd1a1a9271e4f183bc59940a7f2da2713cfd12 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 24 Sep 2023 11:14:46 +0200 Subject: [PATCH 0658/1312] Bot in string_manipulation: correct ik right away --- src/analyses/base.ml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 3810a92277..d0f9dcc03e 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2096,19 +2096,21 @@ struct set ~ctx ~blob_destructive:true (Analyses.ask_of_ctx ctx) gs st lv_a lv_typ (op_array array_s1 array_s2) | Bot, Array array_s2 -> (* If we have bot inside here, we assume the blob is used as a char array and create one inside *) + let ptrdiff_ik = Cilfacade.ptrdiff_ikind () in let size = ctx.ask (Q.BlobSize s1) in - let s_id = - try ValueDomainQueries.ID.unlift (Fun.id) size - with Failure _ -> ID.top_of ILong in - let empty_array = CArrays.make (ID.cast_to (Cilfacade.ptrdiff_ikind ()) s_id) (Int (ID.top_of IChar)) in + let s_id = + try ValueDomainQueries.ID.unlift (ID.cast_to ptrdiff_ik) size + with Failure _ -> ID.top_of ptrdiff_ik in + let empty_array = CArrays.make s_id (Int (ID.top_of IChar)) in set ~ctx (Analyses.ask_of_ctx ctx) gs st lv_a lv_typ (op_array empty_array array_s2) | Bot , _ when CilType.Typ.equal s2_typ charPtrType -> (* If we have bot inside here, we assume the blob is used as a char array and create one inside *) + let ptrdiff_ik = Cilfacade.ptrdiff_ikind () in let size = ctx.ask (Q.BlobSize s1) in - let s_id = - try ValueDomainQueries.ID.unlift (Fun.id) size - with Failure _ -> ID.top_of ILong in - let empty_array = CArrays.make (ID.cast_to (Cilfacade.ptrdiff_ikind ()) s_id) (Int (ID.top_of IChar)) in + let s_id = + try ValueDomainQueries.ID.unlift (ID.cast_to ptrdiff_ik) size + with Failure _ -> ID.top_of ptrdiff_ik in + let empty_array = CArrays.make s_id (Int (ID.top_of IChar)) in let s2_null_bytes = List.map CArrays.to_null_byte_domain (AD.to_string s2_a) in let array_s2 = List.fold_left CArrays.join (CArrays.bot ()) s2_null_bytes in set ~ctx (Analyses.ask_of_ctx ctx) gs st lv_a lv_typ (op_array empty_array array_s2) From d0a90d83e943992aca1cd1756d9bcd723df25d74 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 24 Sep 2023 11:49:13 +0200 Subject: [PATCH 0659/1312] Escape `\0` in XML for g2html compatibility --- src/util/xmlUtil.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/util/xmlUtil.ml b/src/util/xmlUtil.ml index e33be1b215..c0eaa074e9 100644 --- a/src/util/xmlUtil.ml +++ b/src/util/xmlUtil.ml @@ -11,4 +11,5 @@ let escape (x:string):string = Str.global_replace (Str.regexp "\"") """ |> Str.global_replace (Str.regexp "'") "'" |> Str.global_replace (Str.regexp "[\x0b\001\x0c\x0f\x0e\x05]") "" |> (* g2html just cannot handle from some kernel benchmarks, even when escaped... *) - Str.global_replace (Str.regexp "[\x1b]") "" (* g2html cannot handle from chrony *) + Str.global_replace (Str.regexp "[\x1b]") "" |> (* g2html cannot handle from chrony *) + Str.global_replace (Str.regexp "\x00") "\\\\0" (* produces \\0, is needed if an example contains \0 *) From 2f7c07fa498b1be95b81dbf293aa892dfa0bc31f Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 24 Sep 2023 12:00:48 +0200 Subject: [PATCH 0660/1312] Add problematic example --- tests/regression/73-strings/09-malloc.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 tests/regression/73-strings/09-malloc.c diff --git a/tests/regression/73-strings/09-malloc.c b/tests/regression/73-strings/09-malloc.c new file mode 100644 index 0000000000..118db6f0e6 --- /dev/null +++ b/tests/regression/73-strings/09-malloc.c @@ -0,0 +1,16 @@ +// PARAM: --disable ana.base.limit-string-addresses --enable ana.int.interval --enable ana.base.arrays.nullbytes + +#include +#include +#include + +int main () { + char* s1 = malloc(50); + s1[0] = 'a'; + + char s2[50]; + s2[0] = 'a'; + + int len1 = strlen(s1); //WARN + int len2 = strlen(s2); //WARN +} From 48d0e5dec19cddd3a0e78febc562b26126ad8446 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 24 Sep 2023 12:05:44 +0200 Subject: [PATCH 0661/1312] Make also fail in the CI --- tests/regression/73-strings/09-malloc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/regression/73-strings/09-malloc.c b/tests/regression/73-strings/09-malloc.c index 118db6f0e6..913ec821c0 100644 --- a/tests/regression/73-strings/09-malloc.c +++ b/tests/regression/73-strings/09-malloc.c @@ -1,5 +1,4 @@ // PARAM: --disable ana.base.limit-string-addresses --enable ana.int.interval --enable ana.base.arrays.nullbytes - #include #include #include @@ -11,6 +10,7 @@ int main () { char s2[50]; s2[0] = 'a'; - int len1 = strlen(s1); //WARN - int len2 = strlen(s2); //WARN + // Use size_t to avoid integer warnings hiding the lack of string warnings + size_t len1 = strlen(s1); //WARN + size_t len2 = strlen(s2); //WARN } From 1116ef622f21fb53e82780317c19088c9bad25de Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 24 Sep 2023 14:22:35 +0200 Subject: [PATCH 0662/1312] No shortcut `meet` and `narrow` w/ int refinement --- src/domains/lattice.ml | 9 +++++++-- .../regression/38-int-refinements/06-narrow.c | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 tests/regression/38-int-refinements/06-narrow.c diff --git a/src/domains/lattice.ml b/src/domains/lattice.ml index 4cdaa8fb9f..841d1d61b7 100644 --- a/src/domains/lattice.ml +++ b/src/domains/lattice.ml @@ -151,10 +151,15 @@ end module HConsed (Base:S) = struct include Printable.HConsed (Base) + + (* We do refine int values on narrow and meet {!IntDomain.IntDomTupleImpl}, which can lead to fixpoint issues *) + (* see https://github.com/goblint/analyzer/issues/1005 *) + let int_refine_active = GobConfig.get_string "ana.int.refinement" <> "never" + let lift_f2 f x y = f (unlift x) (unlift y) - let narrow x y = if x.BatHashcons.tag == y.BatHashcons.tag then x else lift (lift_f2 Base.narrow x y) + let narrow x y = if (not int_refine_active) && x.BatHashcons.tag == y.BatHashcons.tag then x else lift (lift_f2 Base.narrow x y) let widen x y = if x.BatHashcons.tag == y.BatHashcons.tag then x else lift (lift_f2 Base.widen x y) - let meet x y = if x.BatHashcons.tag == y.BatHashcons.tag then x else lift (lift_f2 Base.meet x y) + let meet x y = if (not int_refine_active) && x.BatHashcons.tag == y.BatHashcons.tag then x else lift (lift_f2 Base.meet x y) let join x y = if x.BatHashcons.tag == y.BatHashcons.tag then x else lift (lift_f2 Base.join x y) let leq x y = (x.BatHashcons.tag == y.BatHashcons.tag) || lift_f2 Base.leq x y let is_top = lift_f Base.is_top diff --git a/tests/regression/38-int-refinements/06-narrow.c b/tests/regression/38-int-refinements/06-narrow.c new file mode 100644 index 0000000000..513e9dde60 --- /dev/null +++ b/tests/regression/38-int-refinements/06-narrow.c @@ -0,0 +1,18 @@ +// PARAM: --set ana.int.refinement fixpoint --enable ana.int.interval +// FIXPOINT +#include + +int g = 0; + +void main() +{ + int i = 0; + while (1) { + i++; + for (int j=0; j < 10; j++) { + if (i > 100) g = 1; + } + if (i>9) i=0; + } + return; +} From 2f691be58f2367cdf2f8fb51e7fcf947b0571df3 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 24 Sep 2023 14:23:42 +0200 Subject: [PATCH 0663/1312] Make comment a bit longer --- src/domains/lattice.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/domains/lattice.ml b/src/domains/lattice.ml index 841d1d61b7..3e1207b8b1 100644 --- a/src/domains/lattice.ml +++ b/src/domains/lattice.ml @@ -152,7 +152,7 @@ module HConsed (Base:S) = struct include Printable.HConsed (Base) - (* We do refine int values on narrow and meet {!IntDomain.IntDomTupleImpl}, which can lead to fixpoint issues *) + (* We do refine int values on narrow and meet {!IntDomain.IntDomTupleImpl}, which can lead to fixpoint issues if we assume x op x = x *) (* see https://github.com/goblint/analyzer/issues/1005 *) let int_refine_active = GobConfig.get_string "ana.int.refinement" <> "never" From 08d56aa0d8e6e13b1ff87fd11d3bdffc8218611a Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 24 Sep 2023 16:51:17 +0200 Subject: [PATCH 0664/1312] Add argument to `threadenter` --- src/analyses/abortUnless.ml | 2 +- src/analyses/accessAnalysis.ml | 2 +- src/analyses/activeLongjmp.ml | 2 +- src/analyses/activeSetjmp.ml | 2 +- src/analyses/apron/relationAnalysis.apron.ml | 2 +- src/analyses/base.ml | 2 +- src/analyses/condVars.ml | 2 +- src/analyses/expsplit.ml | 2 +- src/analyses/extractPthread.ml | 2 +- src/analyses/fileUse.ml | 2 +- src/analyses/locksetAnalysis.ml | 2 +- src/analyses/mCP.ml | 12 ++++----- src/analyses/mallocFresh.ml | 2 +- src/analyses/malloc_null.ml | 2 +- src/analyses/modifiedSinceLongjmp.ml | 2 +- src/analyses/mutexTypeAnalysis.ml | 2 +- src/analyses/poisonVariables.ml | 2 +- src/analyses/pthreadSignals.ml | 2 +- src/analyses/region.ml | 2 +- src/analyses/spec.ml | 2 +- src/analyses/stackTrace.ml | 4 +-- src/analyses/symbLocks.ml | 2 +- src/analyses/taintPartialContexts.ml | 2 +- src/analyses/termination.ml | 2 +- src/analyses/threadAnalysis.ml | 2 +- src/analyses/threadEscape.ml | 2 +- src/analyses/threadFlag.ml | 2 +- src/analyses/threadId.ml | 8 +++--- src/analyses/threadReturn.ml | 2 +- src/analyses/tmpSpecial.ml | 2 +- src/analyses/tutorials/taint.ml | 2 +- src/analyses/tutorials/unitAnalysis.ml | 2 +- src/analyses/uninit.ml | 2 +- src/analyses/useAfterFree.ml | 4 +-- src/analyses/varEq.ml | 2 +- src/analyses/vla.ml | 2 +- src/analyses/wrapperFunctionAnalysis.ml | 2 +- src/framework/analyses.ml | 6 ++--- src/framework/constraints.ml | 27 ++++++++++---------- src/framework/control.ml | 6 ++--- src/framework/resultQuery.ml | 6 ++--- src/util/wideningTokens.ml | 2 +- src/witness/observerAnalysis.ml | 2 +- src/witness/witnessConstraints.ml | 4 +-- 44 files changed, 74 insertions(+), 73 deletions(-) diff --git a/src/analyses/abortUnless.ml b/src/analyses/abortUnless.ml index 813d999ac3..1c77803c7e 100644 --- a/src/analyses/abortUnless.ml +++ b/src/analyses/abortUnless.ml @@ -65,7 +65,7 @@ struct false let startstate v = false - let threadenter ctx lval f args = [false] + let threadenter ?(multiple=false) ctx lval f args = [false] let threadspawn ctx lval f args fctx = false let exitstate v = false end diff --git a/src/analyses/accessAnalysis.ml b/src/analyses/accessAnalysis.ml index e99aefa0e5..bd1ca528a7 100644 --- a/src/analyses/accessAnalysis.ml +++ b/src/analyses/accessAnalysis.ml @@ -54,7 +54,7 @@ struct (** We just lift start state, global and dependency functions: *) let startstate v = () - let threadenter ctx lval f args = [()] + let threadenter ?(multiple=false) ctx lval f args = [()] let exitstate v = () let context fd d = () diff --git a/src/analyses/activeLongjmp.ml b/src/analyses/activeLongjmp.ml index 9c9868e32f..43da8c6512 100644 --- a/src/analyses/activeLongjmp.ml +++ b/src/analyses/activeLongjmp.ml @@ -26,7 +26,7 @@ struct (* Initial values don't really matter: overwritten at longjmp call. *) let startstate v = D.bot () - let threadenter ctx lval f args = [D.bot ()] + let threadenter ?(multiple=false) ctx lval f args = [D.bot ()] let exitstate v = D.top () let query ctx (type a) (q: a Queries.t): a Queries.result = diff --git a/src/analyses/activeSetjmp.ml b/src/analyses/activeSetjmp.ml index 069111d3ba..a69bf4db95 100644 --- a/src/analyses/activeSetjmp.ml +++ b/src/analyses/activeSetjmp.ml @@ -25,7 +25,7 @@ struct | _ -> ctx.local let startstate v = D.bot () - let threadenter ctx lval f args = [D.bot ()] + let threadenter ?(multiple=false) ctx lval f args = [D.bot ()] let exitstate v = D.top () let query ctx (type a) (q: a Queries.t): a Queries.result = diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index 46c620f390..d56064a42f 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -647,7 +647,7 @@ struct (* Thread transfer functions. *) - let threadenter ctx lval f args = + let threadenter ?(multiple=false) ctx lval f args = let st = ctx.local in match Cilfacade.find_varinfo_fundec f with | fd -> diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 71e2661997..cb29cbc0ab 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2503,7 +2503,7 @@ struct in combine_one ctx.local after - let threadenter ctx (lval: lval option) (f: varinfo) (args: exp list): D.t list = + let threadenter ?(multiple=false) ctx (lval: lval option) (f: varinfo) (args: exp list): D.t list = match Cilfacade.find_varinfo_fundec f with | fd -> [make_entry ~thread:true ctx fd args] diff --git a/src/analyses/condVars.ml b/src/analyses/condVars.ml index 04b148dd02..3a2cc5798d 100644 --- a/src/analyses/condVars.ml +++ b/src/analyses/condVars.ml @@ -155,7 +155,7 @@ struct ctx.local let startstate v = D.bot () - let threadenter ctx lval f args = [D.bot ()] + let threadenter ?(multiple=false) ctx lval f args = [D.bot ()] let threadspawn ctx lval f args fctx = ctx.local let exitstate v = D.bot () end diff --git a/src/analyses/expsplit.ml b/src/analyses/expsplit.ml index f121d0380e..9c610a96bf 100644 --- a/src/analyses/expsplit.ml +++ b/src/analyses/expsplit.ml @@ -84,7 +84,7 @@ struct in emit_splits ctx d - let threadenter ctx lval f args = [ctx.local] + let threadenter ?(multiple=false) ctx lval f args = [ctx.local] let threadspawn ctx lval f args fctx = emit_splits_ctx ctx diff --git a/src/analyses/extractPthread.ml b/src/analyses/extractPthread.ml index 60e389fedf..591704cc70 100644 --- a/src/analyses/extractPthread.ml +++ b/src/analyses/extractPthread.ml @@ -1238,7 +1238,7 @@ module Spec : Analyses.MCPSpec = struct (Ctx.top ()) - let threadenter ctx lval f args = + let threadenter ?(multiple=false) ctx lval f args = let d : D.t = ctx.local in let tasks = ctx.global tasks_var in (* TODO: optimize finding *) diff --git a/src/analyses/fileUse.ml b/src/analyses/fileUse.ml index a9088a4bb2..b12953c71c 100644 --- a/src/analyses/fileUse.ml +++ b/src/analyses/fileUse.ml @@ -287,7 +287,7 @@ struct | _ -> m let startstate v = D.bot () - let threadenter ctx lval f args = [D.bot ()] + let threadenter ?(multiple=false) ctx lval f args = [D.bot ()] let threadspawn ctx lval f args fctx = ctx.local let exitstate v = D.bot () end diff --git a/src/analyses/locksetAnalysis.ml b/src/analyses/locksetAnalysis.ml index 2e9e08f03d..56fe960a47 100644 --- a/src/analyses/locksetAnalysis.ml +++ b/src/analyses/locksetAnalysis.ml @@ -18,7 +18,7 @@ struct module C = D let startstate v = D.empty () - let threadenter ctx lval f args = [D.empty ()] + let threadenter ?(multiple=false) ctx lval f args = [D.empty ()] let exitstate v = D.empty () end diff --git a/src/analyses/mCP.ml b/src/analyses/mCP.ml index 1b6a7e5a1d..e305e9c7f6 100644 --- a/src/analyses/mCP.ml +++ b/src/analyses/mCP.ml @@ -140,9 +140,9 @@ struct f ((k,v::a')::a) b in f [] xs - let do_spawns ctx (xs:(varinfo * (lval option * exp list)) list) = + let do_spawns ctx (xs:(varinfo * (lval option * exp list * bool)) list) = let spawn_one v d = - List.iter (fun (lval, args) -> ctx.spawn lval v args) d + List.iter (fun (lval, args, multiple) -> ctx.spawn ~multiple lval v args) d in if not (get_bool "exp.single-threaded") then iter (uncurry spawn_one) @@ group_assoc_eq Basetype.Variables.equal xs @@ -322,8 +322,8 @@ struct and outer_ctx tfname ?spawns ?sides ?emits ctx = let spawn = match spawns with - | Some spawns -> (fun l v a -> spawns := (v,(l,a)) :: !spawns) - | None -> (fun v d -> failwith ("Cannot \"spawn\" in " ^ tfname ^ " context.")) + | Some spawns -> (fun ?(multiple=false) l v a -> spawns := (v,(l,a,multiple)) :: !spawns) + | None -> (fun ?(multiple=false) v d -> failwith ("Cannot \"spawn\" in " ^ tfname ^ " context.")) in let sideg = match sides with | Some sides -> (fun v g -> sides := (v, (!WideningTokens.side_tokens, g)) :: !sides) @@ -565,13 +565,13 @@ struct let d = do_emits ctx !emits d q in if q then raise Deadcode else d - let threadenter (ctx:(D.t, G.t, C.t, V.t) ctx) lval f a = + let threadenter ?(multiple=false) (ctx:(D.t, G.t, C.t, V.t) ctx) lval f a = let sides = ref [] in let emits = ref [] in let ctx'' = outer_ctx "threadenter" ~sides ~emits ctx in let f (n,(module S:MCPSpec),d) = let ctx' : (S.D.t, S.G.t, S.C.t, S.V.t) ctx = inner_ctx "threadenter" ctx'' n d in - map (fun d -> (n, repr d)) @@ S.threadenter ctx' lval f a + map (fun d -> (n, repr d)) @@ (S.threadenter ~multiple) ctx' lval f a in let css = map f @@ spec_list ctx.local in do_sideg ctx !sides; diff --git a/src/analyses/mallocFresh.ml b/src/analyses/mallocFresh.ml index 2c2b99a075..861e4958bd 100644 --- a/src/analyses/mallocFresh.ml +++ b/src/analyses/mallocFresh.ml @@ -52,7 +52,7 @@ struct | None -> ctx.local | Some lval -> assign_lval (Analyses.ask_of_ctx ctx) lval ctx.local - let threadenter ctx lval f args = + let threadenter ?(multiple=false) ctx lval f args = [D.empty ()] let threadspawn ctx lval f args fctx = diff --git a/src/analyses/malloc_null.ml b/src/analyses/malloc_null.ml index 4d5871cb80..2d90112636 100644 --- a/src/analyses/malloc_null.ml +++ b/src/analyses/malloc_null.ml @@ -215,7 +215,7 @@ struct let name () = "malloc_null" let startstate v = D.empty () - let threadenter ctx lval f args = [D.empty ()] + let threadenter ?(multiple=false) ctx lval f args = [D.empty ()] let threadspawn ctx lval f args fctx = ctx.local let exitstate v = D.empty () diff --git a/src/analyses/modifiedSinceLongjmp.ml b/src/analyses/modifiedSinceLongjmp.ml index 0375bd3f74..d9c8f5102c 100644 --- a/src/analyses/modifiedSinceLongjmp.ml +++ b/src/analyses/modifiedSinceLongjmp.ml @@ -63,7 +63,7 @@ struct ctx.local let startstate v = D.bot () - let threadenter ctx lval f args = [D.bot ()] + let threadenter ?(multiple=false) ctx lval f args = [D.bot ()] let exitstate v = D.top () let query ctx (type a) (q: a Queries.t): a Queries.result = diff --git a/src/analyses/mutexTypeAnalysis.ml b/src/analyses/mutexTypeAnalysis.ml index 806c35f464..7051173bd0 100644 --- a/src/analyses/mutexTypeAnalysis.ml +++ b/src/analyses/mutexTypeAnalysis.ml @@ -65,7 +65,7 @@ struct | _ -> ctx.local let startstate v = D.bot () - let threadenter ctx lval f args = [D.top ()] + let threadenter ?(multiple=false) ctx lval f args = [D.top ()] let threadspawn ctx lval f args fctx = ctx.local let exitstate v = D.top () diff --git a/src/analyses/poisonVariables.ml b/src/analyses/poisonVariables.ml index 1bd4b6d544..8c79626cc9 100644 --- a/src/analyses/poisonVariables.ml +++ b/src/analyses/poisonVariables.ml @@ -61,7 +61,7 @@ struct VS.join au ctx.local let startstate v = D.bot () - let threadenter ctx lval f args = [D.bot ()] + let threadenter ?(multiple=false) ctx lval f args = [D.bot ()] let exitstate v = D.top () let event ctx e octx = diff --git a/src/analyses/pthreadSignals.ml b/src/analyses/pthreadSignals.ml index 0b776282e8..83455965ec 100644 --- a/src/analyses/pthreadSignals.ml +++ b/src/analyses/pthreadSignals.ml @@ -73,7 +73,7 @@ struct | _ -> ctx.local let startstate v = Signals.empty () - let threadenter ctx lval f args = [ctx.local] + let threadenter ?(multiple=false) ctx lval f args = [ctx.local] let exitstate v = Signals.empty () end diff --git a/src/analyses/region.ml b/src/analyses/region.ml index 6d2ae246c3..9d68221fcd 100644 --- a/src/analyses/region.ml +++ b/src/analyses/region.ml @@ -175,7 +175,7 @@ struct let startstate v = `Lifted (RegMap.bot ()) - let threadenter ctx lval f args = + let threadenter ?(multiple=false) ctx lval f args = [`Lifted (RegMap.bot ())] let threadspawn ctx lval f args fctx = ctx.local diff --git a/src/analyses/spec.ml b/src/analyses/spec.ml index 54ffcd2697..e5434eb264 100644 --- a/src/analyses/spec.ml +++ b/src/analyses/spec.ml @@ -487,7 +487,7 @@ struct let startstate v = D.bot () - let threadenter ctx lval f args = [D.bot ()] + let threadenter ?(multiple=false) ctx lval f args = [D.bot ()] let threadspawn ctx lval f args fctx = ctx.local let exitstate v = D.bot () end diff --git a/src/analyses/stackTrace.ml b/src/analyses/stackTrace.ml index 4dc62f1873..3d70c50856 100644 --- a/src/analyses/stackTrace.ml +++ b/src/analyses/stackTrace.ml @@ -21,7 +21,7 @@ struct ctx.local (* keep local as opposed to IdentitySpec *) let startstate v = D.bot () - let threadenter ctx lval f args = [D.bot ()] + let threadenter ?(multiple=false) ctx lval f args = [D.bot ()] let exitstate v = D.top () end @@ -45,7 +45,7 @@ struct let startstate v = D.bot () let exitstate v = D.top () - let threadenter ctx lval f args = + let threadenter ?(multiple=false) ctx lval f args = [D.push !Tracing.current_loc ctx.local] end diff --git a/src/analyses/symbLocks.ml b/src/analyses/symbLocks.ml index d8cebf51d2..b99ef93039 100644 --- a/src/analyses/symbLocks.ml +++ b/src/analyses/symbLocks.ml @@ -29,7 +29,7 @@ struct let name () = "symb_locks" let startstate v = D.top () - let threadenter ctx lval f args = [D.top ()] + let threadenter ?(multiple=false) ctx lval f args = [D.top ()] let threadspawn ctx lval f args fctx = ctx.local let exitstate v = D.top () diff --git a/src/analyses/taintPartialContexts.ml b/src/analyses/taintPartialContexts.ml index d053cd103b..25e981dcbf 100644 --- a/src/analyses/taintPartialContexts.ml +++ b/src/analyses/taintPartialContexts.ml @@ -101,7 +101,7 @@ struct d let startstate v = D.bot () - let threadenter ctx lval f args = + let threadenter ?(multiple=false) ctx lval f args = [D.bot ()] let threadspawn ctx lval f args fctx = match lval with diff --git a/src/analyses/termination.ml b/src/analyses/termination.ml index 6da9225d3f..5e5e0d36f1 100644 --- a/src/analyses/termination.ml +++ b/src/analyses/termination.ml @@ -217,7 +217,7 @@ struct (* | _ -> ctx.local *) let startstate v = D.bot () - let threadenter ctx lval f args = [D.bot ()] + let threadenter ?(multiple=false) ctx lval f args = [D.bot ()] let exitstate v = D.bot () end diff --git a/src/analyses/threadAnalysis.ml b/src/analyses/threadAnalysis.ml index 1e679a4707..26e6702c25 100644 --- a/src/analyses/threadAnalysis.ml +++ b/src/analyses/threadAnalysis.ml @@ -84,7 +84,7 @@ struct | _ -> Queries.Result.top q let startstate v = D.bot () - let threadenter ctx lval f args = [D.bot ()] + let threadenter ?(multiple=false) ctx lval f args = [D.bot ()] let threadspawn ctx lval f args fctx = let creator = ThreadId.get_current (Analyses.ask_of_ctx ctx) in let tid = ThreadId.get_current_unlift (Analyses.ask_of_ctx fctx) in diff --git a/src/analyses/threadEscape.ml b/src/analyses/threadEscape.ml index 9ed62e7422..0674ebf3d1 100644 --- a/src/analyses/threadEscape.ml +++ b/src/analyses/threadEscape.ml @@ -150,7 +150,7 @@ struct let startstate v = D.bot () let exitstate v = D.bot () - let threadenter ctx lval f args = + let threadenter ?(multiple=false) ctx lval f args = [D.bot ()] let threadspawn ctx lval f args fctx = diff --git a/src/analyses/threadFlag.ml b/src/analyses/threadFlag.ml index f2ebf82be1..f3b132918a 100644 --- a/src/analyses/threadFlag.ml +++ b/src/analyses/threadFlag.ml @@ -58,7 +58,7 @@ struct let access ctx _ = is_currently_multi (Analyses.ask_of_ctx ctx) - let threadenter ctx lval f args = + let threadenter ?(multiple=false) ctx lval f args = if not (has_ever_been_multi (Analyses.ask_of_ctx ctx)) then ctx.emit Events.EnterMultiThreaded; [create_tid f] diff --git a/src/analyses/threadId.ml b/src/analyses/threadId.ml index 4acf88a7ef..f1de1dfdcb 100644 --- a/src/analyses/threadId.ml +++ b/src/analyses/threadId.ml @@ -56,9 +56,9 @@ struct Hashtbl.replace !tids tid (); (N.bot (), `Lifted (tid), (TD.bot (), TD.bot ())) - let create_tid (_, current, (td, _)) ((node, index): Node.t * int option) v = + let create_tid ?(multiple=false) (_, current, (td, _)) ((node, index): Node.t * int option) v = match current with - | `Lifted current -> + | `Lifted current when not multiple -> let+ tid = Thread.threadenter (current, td) node index v in if GobConfig.get_bool "dbg.print_tids" then Hashtbl.replace !tids tid (); @@ -133,9 +133,9 @@ struct | `Lifted node, count -> node, Some count | (`Bot | `Top), _ -> ctx.prev_node, None - let threadenter ctx lval f args:D.t list = + let threadenter ?(multiple=false) ctx lval f args:D.t list = let n, i = indexed_node_for_ctx ctx in - let+ tid = create_tid ctx.local (n, i) f in + let+ tid = create_tid ~multiple ctx.local (n, i) f in (`Lifted (f, n, i), tid, (TD.bot (), TD.bot ())) let threadspawn ctx lval f args fctx = diff --git a/src/analyses/threadReturn.ml b/src/analyses/threadReturn.ml index 470c4ceaa8..176a4d3465 100644 --- a/src/analyses/threadReturn.ml +++ b/src/analyses/threadReturn.ml @@ -28,7 +28,7 @@ struct ctx.local (* keep local as opposed to IdentitySpec *) let startstate v = true - let threadenter ctx lval f args = [true] + let threadenter ?(multiple=false) ctx lval f args = [true] let exitstate v = D.top () let query (ctx: (D.t, _, _, _) ctx) (type a) (x: a Queries.t): a Queries.result = diff --git a/src/analyses/tmpSpecial.ml b/src/analyses/tmpSpecial.ml index 2d38972d7a..f3d092e59e 100644 --- a/src/analyses/tmpSpecial.ml +++ b/src/analyses/tmpSpecial.ml @@ -88,7 +88,7 @@ struct | _ -> Queries.Result.top q let startstate v = D.bot () - let threadenter ctx lval f args = [D.bot ()] + let threadenter ?(multiple=false) ctx lval f args = [D.bot ()] let threadspawn ctx lval f args fctx = ctx.local let exitstate v = D.bot () end diff --git a/src/analyses/tutorials/taint.ml b/src/analyses/tutorials/taint.ml index 3067449e31..166ce2c3f6 100644 --- a/src/analyses/tutorials/taint.ml +++ b/src/analyses/tutorials/taint.ml @@ -129,7 +129,7 @@ struct (* You may leave these alone *) let startstate v = D.bot () - let threadenter ctx lval f args = [D.top ()] + let threadenter ?(multiple=false) ctx lval f args = [D.top ()] let threadspawn ctx lval f args fctx = ctx.local let exitstate v = D.top () end diff --git a/src/analyses/tutorials/unitAnalysis.ml b/src/analyses/tutorials/unitAnalysis.ml index d3b8c69bfd..b5fb4d6367 100644 --- a/src/analyses/tutorials/unitAnalysis.ml +++ b/src/analyses/tutorials/unitAnalysis.ml @@ -39,7 +39,7 @@ struct ctx.local let startstate v = D.bot () - let threadenter ctx lval f args = [D.top ()] + let threadenter ?(multiple=false) ctx lval f args = [D.top ()] let threadspawn ctx lval f args fctx = ctx.local let exitstate v = D.top () end diff --git a/src/analyses/uninit.ml b/src/analyses/uninit.ml index f8759d9134..abdcd67aaa 100644 --- a/src/analyses/uninit.ml +++ b/src/analyses/uninit.ml @@ -25,7 +25,7 @@ struct let name () = "uninit" let startstate v : D.t = D.empty () - let threadenter ctx lval f args = [D.empty ()] + let threadenter ?(multiple=false) ctx lval f args = [D.empty ()] let threadspawn ctx lval f args fctx = ctx.local let exitstate v : D.t = D.empty () diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index 0aafbd1ad4..6033c689e1 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -196,7 +196,7 @@ struct end | _ -> state - let threadenter ctx lval f args = [ctx.local] + let threadenter ?(multiple=false) ctx lval f args = [ctx.local] let threadspawn ctx lval f args fctx = ctx.local let startstate v = D.bot () @@ -205,4 +205,4 @@ struct end let _ = - MCP.register_analysis (module Spec : MCPSpec) \ No newline at end of file + MCP.register_analysis (module Spec : MCPSpec) diff --git a/src/analyses/varEq.ml b/src/analyses/varEq.ml index 634a684c7c..7bd3453b8a 100644 --- a/src/analyses/varEq.ml +++ b/src/analyses/varEq.ml @@ -43,7 +43,7 @@ struct let name () = "var_eq" let startstate v = D.top () - let threadenter ctx lval f args = [D.top ()] + let threadenter ?(multiple=false) ctx lval f args = [D.top ()] let threadspawn ctx lval f args fctx = ctx.local let exitstate v = D.top () diff --git a/src/analyses/vla.ml b/src/analyses/vla.ml index 865f22b20a..8bd0168de0 100644 --- a/src/analyses/vla.ml +++ b/src/analyses/vla.ml @@ -33,7 +33,7 @@ struct ctx.local || Cilfacade.isVLAType v.vtype let startstate v = D.bot () - let threadenter ctx lval f args = [D.top ()] + let threadenter ?(multiple=false) ctx lval f args = [D.top ()] let exitstate v = D.top () end diff --git a/src/analyses/wrapperFunctionAnalysis.ml b/src/analyses/wrapperFunctionAnalysis.ml index d9bbdb6197..89242e044e 100644 --- a/src/analyses/wrapperFunctionAnalysis.ml +++ b/src/analyses/wrapperFunctionAnalysis.ml @@ -87,7 +87,7 @@ struct let startstate v = D.bot () - let threadenter ctx lval f args = + let threadenter ?(multiple=false) ctx lval f args = (* The new thread receives a fresh counter *) [D.bot ()] diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index df3346af93..54a3a18f1a 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -342,7 +342,7 @@ type ('d,'g,'c,'v) ctx = ; edge : MyCFG.edge ; local : 'd ; global : 'v -> 'g - ; spawn : lval option -> varinfo -> exp list -> unit + ; spawn : ?multiple:bool -> lval option -> varinfo -> exp list -> unit ; split : 'd -> Events.t list -> unit ; sideg : 'v -> 'g -> unit } @@ -444,7 +444,7 @@ sig val paths_as_set : (D.t, G.t, C.t, V.t) ctx -> D.t list (** Returns initial state for created thread. *) - val threadenter : (D.t, G.t, C.t, V.t) ctx -> lval option -> varinfo -> exp list -> D.t list + val threadenter : ?multiple:bool -> (D.t, G.t, C.t, V.t) ctx -> lval option -> varinfo -> exp list -> D.t list (** Updates the local state of the creator thread using initial state of created thread. *) val threadspawn : (D.t, G.t, C.t, V.t) ctx -> lval option -> varinfo -> exp list -> (D.t, G.t, C.t, V.t) ctx -> D.t @@ -696,7 +696,7 @@ struct let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) = ctx.local - let threadenter ctx lval f args = [ctx.local] + let threadenter ?(multiple=false) ctx lval f args = [ctx.local] let threadspawn ctx lval f args fctx = ctx.local end diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 740d1f85a9..a7683fb6b3 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -83,8 +83,8 @@ struct let combine_assign ctx r fe f args fc es f_ask = D.lift @@ S.combine_assign (conv ctx) r fe f args fc (D.unlift es) f_ask - let threadenter ctx lval f args = - List.map D.lift @@ S.threadenter (conv ctx) lval f args + let threadenter ?(multiple=false) ctx lval f args = + List.map D.lift @@ (S.threadenter ~multiple) (conv ctx) lval f args let threadspawn ctx lval f args fctx = D.lift @@ S.threadspawn (conv ctx) lval f args (conv fctx) @@ -167,8 +167,8 @@ struct let combine_assign ctx r fe f args fc es f_ask = S.combine_assign (conv ctx) r fe f args (Option.map C.unlift fc) es f_ask - let threadenter ctx lval f args = - S.threadenter (conv ctx) lval f args + let threadenter ?(multiple=false) ctx lval f args = + S.threadenter ~multiple (conv ctx) lval f args let threadspawn ctx lval f args fctx = S.threadspawn (conv ctx) lval f args (conv fctx) @@ -249,7 +249,7 @@ struct let combine_env' ctx r fe f args fc es f_ask = lift_fun ctx (lift ctx) S.combine_env (fun p -> p r fe f args fc (fst es) f_ask) let combine_assign' ctx r fe f args fc es f_ask = lift_fun ctx (lift ctx) S.combine_assign (fun p -> p r fe f args fc (fst es) f_ask) - let threadenter ctx lval f args = lift_fun ctx (List.map lift_start_level) S.threadenter ((|>) args % (|>) f % (|>) lval) + let threadenter ?(multiple=false) ctx lval f args = lift_fun ctx (List.map lift_start_level) (S.threadenter ~multiple) ((|>) args % (|>) f % (|>) lval) let threadspawn ctx lval f args fctx = lift_fun ctx (lift ctx) S.threadspawn ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval) let leq0 = function @@ -394,7 +394,7 @@ struct let event ctx e octx = lift_fun ctx S.event ((|>) (conv octx) % (|>) e) - let threadenter ctx lval f args = S.threadenter (conv ctx) lval f args |> List.map (fun d -> (d, snd ctx.local)) + let threadenter ?(multiple=false) ctx lval f args = S.threadenter ~multiple (conv ctx) lval f args |> List.map (fun d -> (d, snd ctx.local)) let threadspawn ctx lval f args fctx = lift_fun ctx S.threadspawn ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval) let enter ctx r f args = @@ -485,7 +485,7 @@ struct let combine_env ctx r fe f args fc es f_ask = lift_fun ctx D.lift S.combine_env (fun p -> p r fe f args fc (D.unlift es) f_ask) `Bot let combine_assign ctx r fe f args fc es f_ask = lift_fun ctx D.lift S.combine_assign (fun p -> p r fe f args fc (D.unlift es) f_ask) `Bot - let threadenter ctx lval f args = lift_fun ctx (List.map D.lift) S.threadenter ((|>) args % (|>) f % (|>) lval) [] + let threadenter ?(multiple=false) ctx lval f args = lift_fun ctx (List.map D.lift) (S.threadenter ~multiple) ((|>) args % (|>) f % (|>) lval) [] let threadspawn ctx lval f args fctx = lift_fun ctx D.lift S.threadspawn ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval) `Bot let event (ctx:(D.t,G.t,C.t,V.t) ctx) (e:Events.t) (octx:(D.t,G.t,C.t,V.t) ctx):D.t = lift_fun ctx D.lift S.event ((|>) (conv octx) % (|>) e) `Bot @@ -581,7 +581,7 @@ struct ; split = (fun (d:D.t) es -> assert (List.is_empty es); r := d::!r) ; sideg = (fun g d -> sideg (GVar.spec g) (G.create_spec d)) } - and spawn lval f args = + and spawn ?(multiple=false) lval f args = (* TODO: adjust ctx node/edge? *) (* TODO: don't repeat for all paths that spawn same *) let ds = S.threadenter ctx lval f args in @@ -898,7 +898,7 @@ struct ; edge = MyCFG.Skip ; local = S.startstate Cil.dummyFunDec.svar (* bot and top both silently raise and catch Deadcode in DeadcodeLifter *) ; global = (fun g -> G.spec (getg (GVar.spec g))) - ; spawn = (fun v d -> failwith "Cannot \"spawn\" in query context.") + ; spawn = (fun ?(multiple=false) v d -> failwith "Cannot \"spawn\" in query context.") ; split = (fun d es -> failwith "Cannot \"split\" in query context.") ; sideg = (fun v g -> failwith "Cannot \"split\" in query context.") } @@ -1262,9 +1262,10 @@ struct let fd1 = D.choose octx.local in map ctx Spec.event (fun h -> h e (conv octx fd1)) - let threadenter ctx lval f args = + let threadenter ?(multiple=false) ctx lval f args = let g xs ys = (List.map (fun y -> D.singleton y) ys) @ xs in - fold' ctx Spec.threadenter (fun h -> h lval f args) g [] + fold' ctx (Spec.threadenter ~multiple) (fun h -> h lval f args) g [] + let threadspawn ctx lval f args fctx = let fd1 = D.choose fctx.local in map ctx Spec.threadspawn (fun h -> h lval f args (conv fctx fd1)) @@ -1448,7 +1449,7 @@ struct let combine_env ctx = S.combine_env (conv ctx) let combine_assign ctx = S.combine_assign (conv ctx) let special ctx = S.special (conv ctx) - let threadenter ctx = S.threadenter (conv ctx) + let threadenter ?(multiple=false) ctx = S.threadenter ~multiple (conv ctx) let threadspawn ctx lv f args fctx = S.threadspawn (conv ctx) lv f args (conv fctx) let sync ctx = S.sync (conv ctx) let skip ctx = S.skip (conv ctx) @@ -1684,7 +1685,7 @@ struct List.iter handle_path (S.paths_as_set conv_ctx); S.D.bot () | _ -> S.special conv_ctx lv f args - let threadenter ctx = S.threadenter (conv ctx) + let threadenter ?(multiple=false) ctx = S.threadenter ~multiple (conv ctx) let threadspawn ctx lv f args fctx = S.threadspawn (conv ctx) lv f args (conv fctx) let sync ctx = S.sync (conv ctx) let skip ctx = S.skip (conv ctx) diff --git a/src/framework/control.ml b/src/framework/control.ml index 5cefc1a7de..72890be4b4 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -280,7 +280,7 @@ struct ; edge = MyCFG.Skip ; local = Spec.D.top () ; global = (fun g -> EQSys.G.spec (getg (EQSys.GVar.spec g))) - ; spawn = (fun _ -> failwith "Global initializers should never spawn threads. What is going on?") + ; spawn = (fun ?(multiple=false) _ -> failwith "Global initializers should never spawn threads. What is going on?") ; split = (fun _ -> failwith "Global initializers trying to split paths.") ; sideg = (fun g d -> sideg (EQSys.GVar.spec g) (EQSys.G.create_spec d)) } @@ -385,7 +385,7 @@ struct ; edge = MyCFG.Skip ; local = st ; global = (fun g -> EQSys.G.spec (getg (EQSys.GVar.spec g))) - ; spawn = (fun _ -> failwith "Bug1: Using enter_func for toplevel functions with 'otherstate'.") + ; spawn = (fun ?(multiple=false) _ -> failwith "Bug1: Using enter_func for toplevel functions with 'otherstate'.") ; split = (fun _ -> failwith "Bug2: Using enter_func for toplevel functions with 'otherstate'.") ; sideg = (fun g d -> sideg (EQSys.GVar.spec g) (EQSys.G.create_spec d)) } @@ -417,7 +417,7 @@ struct ; edge = MyCFG.Skip ; local = st ; global = (fun g -> EQSys.G.spec (getg (EQSys.GVar.spec g))) - ; spawn = (fun _ -> failwith "Bug1: Using enter_func for toplevel functions with 'otherstate'.") + ; spawn = (fun ?(multiple=false) _ -> failwith "Bug1: Using enter_func for toplevel functions with 'otherstate'.") ; split = (fun _ -> failwith "Bug2: Using enter_func for toplevel functions with 'otherstate'.") ; sideg = (fun g d -> sideg (EQSys.GVar.spec g) (EQSys.G.create_spec d)) } diff --git a/src/framework/resultQuery.ml b/src/framework/resultQuery.ml index ce5839ef30..c676c41c14 100644 --- a/src/framework/resultQuery.ml +++ b/src/framework/resultQuery.ml @@ -18,7 +18,7 @@ struct ; edge = MyCFG.Skip ; local = local ; global = (fun g -> try EQSys.G.spec (GHT.find gh (EQSys.GVar.spec g)) with Not_found -> Spec.G.bot ()) (* see 29/29 on why fallback is needed *) - ; spawn = (fun v d -> failwith "Cannot \"spawn\" in witness context.") + ; spawn = (fun ?(multiple=false) v d -> failwith "Cannot \"spawn\" in witness context.") ; split = (fun d es -> failwith "Cannot \"split\" in witness context.") ; sideg = (fun v g -> failwith "Cannot \"sideg\" in witness context.") } @@ -37,7 +37,7 @@ struct ; edge = MyCFG.Skip ; local = local ; global = (fun g -> try EQSys.G.spec (GHT.find gh (EQSys.GVar.spec g)) with Not_found -> Spec.G.bot ()) (* TODO: how can be missing? *) - ; spawn = (fun v d -> failwith "Cannot \"spawn\" in witness context.") + ; spawn = (fun ?(multiple=false) v d -> failwith "Cannot \"spawn\" in witness context.") ; split = (fun d es -> failwith "Cannot \"split\" in witness context.") ; sideg = (fun v g -> failwith "Cannot \"sideg\" in witness context.") } @@ -57,7 +57,7 @@ struct ; edge = MyCFG.Skip ; local = Spec.startstate GoblintCil.dummyFunDec.svar (* bot and top both silently raise and catch Deadcode in DeadcodeLifter *) (* TODO: is this startstate bad? *) ; global = (fun v -> EQSys.G.spec (try GHT.find gh (EQSys.GVar.spec v) with Not_found -> EQSys.G.bot ())) (* TODO: how can be missing? *) - ; spawn = (fun v d -> failwith "Cannot \"spawn\" in query context.") + ; spawn = (fun ?(multiple=false) v d -> failwith "Cannot \"spawn\" in query context.") ; split = (fun d es -> failwith "Cannot \"split\" in query context.") ; sideg = (fun v g -> failwith "Cannot \"split\" in query context.") } diff --git a/src/util/wideningTokens.ml b/src/util/wideningTokens.ml index 75f0e4f81d..c88f3f00c1 100644 --- a/src/util/wideningTokens.ml +++ b/src/util/wideningTokens.ml @@ -179,7 +179,7 @@ struct let combine_env ctx r fe f args fc es f_ask = lift_fun ctx lift' S.combine_env (fun p -> p r fe f args fc (D.unlift es) f_ask) (* TODO: use tokens from es *) let combine_assign ctx r fe f args fc es f_ask = lift_fun ctx lift' S.combine_assign (fun p -> p r fe f args fc (D.unlift es) f_ask) (* TODO: use tokens from es *) - let threadenter ctx lval f args = lift_fun ctx (fun l ts -> List.map (Fun.flip lift' ts) l) S.threadenter ((|>) args % (|>) f % (|>) lval) + let threadenter ?(multiple=false) ctx lval f args = lift_fun ctx (fun l ts -> List.map (Fun.flip lift' ts) l) (S.threadenter ~multiple) ((|>) args % (|>) f % (|>) lval ) let threadspawn ctx lval f args fctx = lift_fun ctx lift' S.threadspawn ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval) let event ctx e octx = lift_fun ctx lift' S.event ((|>) (conv octx) % (|>) e) end diff --git a/src/witness/observerAnalysis.ml b/src/witness/observerAnalysis.ml index c8d8563909..3c702d199f 100644 --- a/src/witness/observerAnalysis.ml +++ b/src/witness/observerAnalysis.ml @@ -76,7 +76,7 @@ struct step_ctx ctx let startstate v = `Lifted Automaton.initial - let threadenter ctx lval f args = [D.top ()] + let threadenter ?(multiple=false) ctx lval f args = [D.top ()] let threadspawn ctx lval f args fctx = ctx.local let exitstate v = D.top () end diff --git a/src/witness/witnessConstraints.ml b/src/witness/witnessConstraints.ml index 2ce16a5997..ad32713fa8 100644 --- a/src/witness/witnessConstraints.ml +++ b/src/witness/witnessConstraints.ml @@ -199,7 +199,7 @@ struct let r = Dom.bindings a in List.map (fun (x,v) -> (Dom.singleton x v, b)) r - let threadenter ctx lval f args = + let threadenter ?(multiple=false) ctx lval f args = let g xs x' ys = let ys' = List.map (fun y -> let yr = step ctx.prev_node (ctx.context ()) x' (ThreadEntry (lval, f, args)) (nosync x') in (* threadenter called on before-sync state *) @@ -208,7 +208,7 @@ struct in ys' @ xs in - fold' ctx Spec.threadenter (fun h -> h lval f args) g [] + fold' ctx (Spec.threadenter ~multiple) (fun h -> h lval f args) g [] let threadspawn ctx lval f args fctx = let fd1 = Dom.choose_key (fst fctx.local) in map ctx Spec.threadspawn (fun h -> h lval f args (conv fctx fd1)) From a32513936348d54c724697aba3943c409585f46a Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 24 Sep 2023 17:28:01 +0200 Subject: [PATCH 0665/1312] Spawn non-unique threads --- src/analyses/base.ml | 14 +++++++------- src/analyses/threadAnalysis.ml | 8 +++++++- src/framework/constraints.ml | 2 +- tests/regression/40-threadid/09-multiple.c | 15 +++++++++++++++ .../regression/40-threadid/10-multiple-thread.c | 16 ++++++++++++++++ 5 files changed, 46 insertions(+), 9 deletions(-) create mode 100644 tests/regression/40-threadid/09-multiple.c create mode 100644 tests/regression/40-threadid/10-multiple-thread.c diff --git a/src/analyses/base.ml b/src/analyses/base.ml index cb29cbc0ab..3b6be2eff8 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1902,7 +1902,7 @@ struct - let forkfun (ctx:(D.t, G.t, C.t, V.t) Analyses.ctx) (lv: lval option) (f: varinfo) (args: exp list) : (lval option * varinfo * exp list) list = + let forkfun (ctx:(D.t, G.t, C.t, V.t) Analyses.ctx) (lv: lval option) (f: varinfo) (args: exp list) : (lval option * varinfo * exp list) list * bool = let create_thread lval arg v = try (* try to get function declaration *) @@ -1943,7 +1943,7 @@ struct else start_funvars in - List.filter_map (create_thread (Some (Mem id, NoOffset)) (Some ptc_arg)) start_funvars_with_unknown + List.filter_map (create_thread (Some (Mem id, NoOffset)) (Some ptc_arg)) start_funvars_with_unknown, false end | _, _ when get_bool "sem.unknown_function.spawn" -> (* TODO: Remove sem.unknown_function.spawn check because it is (and should be) really done in LibraryFunctions. @@ -1956,9 +1956,9 @@ struct let deep_flist = collect_invalidate ~deep:true (Analyses.ask_of_ctx ctx) ctx.global ctx.local deep_args in let flist = shallow_flist @ deep_flist in let addrs = List.concat_map AD.to_var_may flist in - if addrs <> [] then M.debug ~category:Analyzer "Spawning functions from unknown function: %a" (d_list ", " CilType.Varinfo.pretty) addrs; - List.filter_map (create_thread None None) addrs - | _, _ -> [] + if addrs <> [] then M.debug ~category:Analyzer "Spawning non-unique functions from unknown function: %a" (d_list ", " CilType.Varinfo.pretty) addrs; + List.filter_map (create_thread None None) addrs, true + | _, _ -> [], false let assert_fn ctx e refine = (* make the state meet the assertion in the rest of the code *) @@ -2024,9 +2024,9 @@ struct let addr = eval_lv (Analyses.ask_of_ctx ctx) ctx.global ctx.local lval in (addr, AD.type_of addr) in - let forks = forkfun ctx lv f args in + let forks, multiple = forkfun ctx lv f args in if M.tracing then if not (List.is_empty forks) then M.tracel "spawn" "Base.special %s: spawning functions %a\n" f.vname (d_list "," CilType.Varinfo.pretty) (List.map BatTuple.Tuple3.second forks); - List.iter (BatTuple.Tuple3.uncurry ctx.spawn) forks; + List.iter (BatTuple.Tuple3.uncurry (ctx.spawn ~multiple)) forks; let st: store = ctx.local in let gs = ctx.global in let desc = LF.find f in diff --git a/src/analyses/threadAnalysis.ml b/src/analyses/threadAnalysis.ml index 26e6702c25..740cca3a53 100644 --- a/src/analyses/threadAnalysis.ml +++ b/src/analyses/threadAnalysis.ml @@ -84,7 +84,13 @@ struct | _ -> Queries.Result.top q let startstate v = D.bot () - let threadenter ?(multiple=false) ctx lval f args = [D.bot ()] + + let threadenter ?(multiple=false) ctx lval f args = + if multiple then + (let tid = ThreadId.get_current_unlift (Analyses.ask_of_ctx ctx) in + ctx.sideg tid (true, TS.bot (), false)); + [D.bot ()] + let threadspawn ctx lval f args fctx = let creator = ThreadId.get_current (Analyses.ask_of_ctx ctx) in let tid = ThreadId.get_current_unlift (Analyses.ask_of_ctx fctx) in diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index a7683fb6b3..62b8d46efa 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -584,7 +584,7 @@ struct and spawn ?(multiple=false) lval f args = (* TODO: adjust ctx node/edge? *) (* TODO: don't repeat for all paths that spawn same *) - let ds = S.threadenter ctx lval f args in + let ds = S.threadenter ~multiple ctx lval f args in List.iter (fun d -> spawns := (lval, f, args, d) :: !spawns; match Cilfacade.find_varinfo_fundec f with diff --git a/tests/regression/40-threadid/09-multiple.c b/tests/regression/40-threadid/09-multiple.c new file mode 100644 index 0000000000..5510e5ae07 --- /dev/null +++ b/tests/regression/40-threadid/09-multiple.c @@ -0,0 +1,15 @@ +#include +#include + +int myglobal; + +void *t_fun(void *arg) { + myglobal=40; //RACE + return NULL; +} + +int main(void) { + // This should spawn a non-unique thread + unknown(t_fun); + return 0; +} diff --git a/tests/regression/40-threadid/10-multiple-thread.c b/tests/regression/40-threadid/10-multiple-thread.c new file mode 100644 index 0000000000..0024d268ec --- /dev/null +++ b/tests/regression/40-threadid/10-multiple-thread.c @@ -0,0 +1,16 @@ +// PARAM: --set ana.activated[+] thread +#include +#include + +int myglobal; + +void *t_fun(void *arg) { + myglobal=40; //RACE + return NULL; +} + +int main(void) { + // This should spawn a non-unique thread + unknown(t_fun); + return 0; +} From 70f267b1bb50c66a0e0332982506de963c6f619e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 25 Sep 2023 10:36:12 +0300 Subject: [PATCH 0666/1312] Add more categories to unsound/imprecise call messages --- src/analyses/base.ml | 6 +++--- src/framework/constraints.ml | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 71e2661997..aac369fed3 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1104,12 +1104,12 @@ struct if AD.mem Addr.UnknownPtr fp then begin let others = AD.to_var_may fp in if others = [] then raise OnlyUnknown; - M.warn ~category:Imprecise "Function pointer %a may contain unknown functions." d_exp fval; + M.warn ~category:Imprecise ~tags:[Category Call] "Function pointer %a may contain unknown functions." d_exp fval; dummyFunDec.svar :: others end else AD.to_var_may fp with SetDomain.Unsupported _ | OnlyUnknown -> - M.warn ~category:Unsound "Unknown call to function %a." d_exp fval; + M.warn ~category:Imprecise ~tags:[Category Call] "Unknown call to function %a." d_exp fval; [dummyFunDec.svar] (** Evaluate expression as address. @@ -1970,7 +1970,7 @@ struct end let special_unknown_invalidate ctx ask gs st f args = - (if CilType.Varinfo.equal f dummyFunDec.svar then M.warn ~category:Imprecise "Unknown function ptr called"); + (if CilType.Varinfo.equal f dummyFunDec.svar then M.warn ~category:Imprecise ~tags:[Category Call] "Unknown function ptr called"); let desc = LF.find f in let shallow_addrs = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = false } args in let deep_addrs = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = true } args in diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 740d1f85a9..288b7ad0ea 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -776,16 +776,16 @@ struct end else begin let geq = if var_arg then ">=" else "" in - M.warn ~tags:[CWE 685] "Potential call to function %a with wrong number of arguments (expected: %s%d, actual: %d). This call will be ignored." CilType.Varinfo.pretty f geq p_length arg_length; + M.warn ~category:Unsound ~tags:[Category Call; CWE 685] "Potential call to function %a with wrong number of arguments (expected: %s%d, actual: %d). This call will be ignored." CilType.Varinfo.pretty f geq p_length arg_length; None end | _ -> - M.warn ~category:Call "Something that is not a function (%a) is called." CilType.Varinfo.pretty f; + M.warn ~category:Call "Something that is not a function (%a) is called." CilType.Varinfo.pretty f; None in let funs = List.filter_map one_function functions in if [] = funs then begin - M.warn ~category:Unsound "No suitable function to be called at call site. Continuing with state before call."; + M.warn ~category:Unsound ~tags:[Category Call] "No suitable function to be called at call site. Continuing with state before call."; d (* because LevelSliceLifter *) end else common_joins ctx funs !r !spawns From 1947aaeb407353d1c3c8bd111e9d60ee41c90a16 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 25 Sep 2023 11:00:53 +0300 Subject: [PATCH 0667/1312] Add test for pthread locking function return values --- .../04-mutex/58-pthread-lock-return.c | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 tests/regression/04-mutex/58-pthread-lock-return.c diff --git a/tests/regression/04-mutex/58-pthread-lock-return.c b/tests/regression/04-mutex/58-pthread-lock-return.c new file mode 100644 index 0000000000..0ae96b60ae --- /dev/null +++ b/tests/regression/04-mutex/58-pthread-lock-return.c @@ -0,0 +1,118 @@ +// PARAM: --disable sem.lock.fail +#include + +int g_mutex = 0; +pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + +int g_rwlock = 0; +pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; + +// OS X has no spinlock +#ifndef __APPLE__ +int g_spin = 0; +pthread_spinlock_t spin; +#endif + +void *t_fun(void *arg) { + if (!pthread_mutex_lock(&mutex)) { + __goblint_check(1); // reachable + g_mutex++; // NORACE + pthread_mutex_unlock(&mutex); + } + else { + __goblint_check(0); // NOWARN (unreachable) + } + + if (!pthread_mutex_trylock(&mutex)) { + __goblint_check(1); // reachable + g_mutex++; // NORACE + pthread_mutex_unlock(&mutex); + } + else { + __goblint_check(1); // reachable + } + + if (!pthread_rwlock_wrlock(&mutex)) { + __goblint_check(1); // reachable + g_rwlock++; // NORACE + pthread_rwlock_unlock(&mutex); + } + else { + __goblint_check(0); // NOWARN (unreachable) + } + + if (!pthread_rwlock_trywrlock(&mutex)) { + __goblint_check(1); // reachable + g_rwlock++; // NORACE + pthread_rwlock_unlock(&mutex); + } + else { + __goblint_check(1); // reachable + } + + if (!pthread_rwlock_rdlock(&mutex)) { + __goblint_check(1); // reachable + g_rwlock++; // NORACE + pthread_rwlock_unlock(&mutex); + } + else { + __goblint_check(0); // NOWARN (unreachable) + } + + if (!pthread_rwlock_tryrdlock(&mutex)) { + __goblint_check(1); // reachable + g_rwlock++; // NORACE + pthread_rwlock_unlock(&mutex); + } + else { + __goblint_check(1); // reachable + } + +#ifndef __APPLE__ + if (!pthread_spin_lock(&spin)) { + __goblint_check(1); // reachable + g_spin++; // NORACE + pthread_spin_unlock(&spin); + } + else { + __goblint_check(0); // NOWARN (unreachable) + } + + if (!pthread_spin_trylock(&spin)) { + __goblint_check(1); // reachable + g_spin++; // NORACE + pthread_spin_unlock(&spin); + } + else { + __goblint_check(1); // reachable + } +#endif + + return NULL; +} + +int main() { +#ifndef __APPLE__ + pthread_spin_init(&spin, PTHREAD_PROCESS_PRIVATE); +#endif + + pthread_t id; + pthread_create(&id, NULL, &t_fun, NULL); + + pthread_mutex_lock(&mutex); + g_mutex++; // NORACE + pthread_mutex_unlock(&mutex); + + pthread_rwlock_wrlock(&mutex); + g_rwlock++; // NORACE + pthread_rwlock_unlock(&mutex); + +#ifndef __APPLE__ + pthread_spin_lock(&spin); + g_spin++; // NORACE + pthread_spin_unlock(&spin); +#endif + + pthread_join(id, NULL); + return 0; +} From 4bc0303cbccf0de5772e12ee4be2f6d5021ed470 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 25 Sep 2023 11:26:59 +0300 Subject: [PATCH 0668/1312] Add atexit test with spawn disabled --- tests/regression/41-stdlib/08-atexit-no-spawn.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 tests/regression/41-stdlib/08-atexit-no-spawn.c diff --git a/tests/regression/41-stdlib/08-atexit-no-spawn.c b/tests/regression/41-stdlib/08-atexit-no-spawn.c new file mode 100644 index 0000000000..7f25f42183 --- /dev/null +++ b/tests/regression/41-stdlib/08-atexit-no-spawn.c @@ -0,0 +1,14 @@ +// PARAM: --disable sem.unknown_function.spawn +#include +#include + +void bye() +{ + __goblint_check(0); // NOWARN (unreachable) +} + +int main() +{ + atexit(bye); + return 0; +} From 16077e3b146d464072004eaf0cd1480a2a7b99d1 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 25 Sep 2023 13:27:33 +0300 Subject: [PATCH 0669/1312] Add region interprocedural fixpoint error test --- .../09-regions/40-zstd-thread-pool-region.c | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 tests/regression/09-regions/40-zstd-thread-pool-region.c diff --git a/tests/regression/09-regions/40-zstd-thread-pool-region.c b/tests/regression/09-regions/40-zstd-thread-pool-region.c new file mode 100644 index 0000000000..13baf5ec3f --- /dev/null +++ b/tests/regression/09-regions/40-zstd-thread-pool-region.c @@ -0,0 +1,34 @@ +// SKIP PARAM: --set ana.activated[+] region +// FIXPOINT +#include +#include +#include + +typedef struct POOL_job_s { + void *opaque; +} POOL_job; + +typedef struct POOL_ctx_s { + POOL_job *queue; +} POOL_ctx; + +POOL_ctx* ctx_global; + +POOL_ctx* POOL_create(size_t numThreads, size_t queueSize) +{ + POOL_ctx* ctx_create; + ctx_create = (POOL_ctx*)malloc(sizeof(POOL_ctx)); + ctx_create->queue = (POOL_job*)malloc(queueSize * sizeof(POOL_job)); + + int r; // rand + if (r) + ctx_global = ctx_create; // pretend escape + return ctx_create; +} + +int main() { + while (1) { + POOL_ctx *ctx_main; + ctx_main = POOL_create(20, 10); + } +} From ccaffc592932a7c6ca219aadc28d6f7b74188fcf Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 25 Sep 2023 13:07:31 +0200 Subject: [PATCH 0670/1312] Turn optional argument into named argument --- src/analyses/abortUnless.ml | 2 +- src/analyses/accessAnalysis.ml | 2 +- src/analyses/activeLongjmp.ml | 2 +- src/analyses/activeSetjmp.ml | 2 +- src/analyses/apron/relationAnalysis.apron.ml | 2 +- src/analyses/base.ml | 2 +- src/analyses/condVars.ml | 2 +- src/analyses/expsplit.ml | 2 +- src/analyses/extractPthread.ml | 2 +- src/analyses/fileUse.ml | 2 +- src/analyses/locksetAnalysis.ml | 2 +- src/analyses/mCP.ml | 2 +- src/analyses/mallocFresh.ml | 2 +- src/analyses/malloc_null.ml | 2 +- src/analyses/modifiedSinceLongjmp.ml | 2 +- src/analyses/mutexTypeAnalysis.ml | 2 +- src/analyses/poisonVariables.ml | 2 +- src/analyses/pthreadSignals.ml | 2 +- src/analyses/region.ml | 2 +- src/analyses/spec.ml | 2 +- src/analyses/stackTrace.ml | 4 ++-- src/analyses/symbLocks.ml | 2 +- src/analyses/taintPartialContexts.ml | 2 +- src/analyses/termination.ml | 2 +- src/analyses/threadAnalysis.ml | 2 +- src/analyses/threadEscape.ml | 2 +- src/analyses/threadFlag.ml | 2 +- src/analyses/threadId.ml | 2 +- src/analyses/threadReturn.ml | 2 +- src/analyses/tmpSpecial.ml | 2 +- src/analyses/tutorials/taint.ml | 2 +- src/analyses/tutorials/unitAnalysis.ml | 2 +- src/analyses/uninit.ml | 2 +- src/analyses/useAfterFree.ml | 2 +- src/analyses/varEq.ml | 2 +- src/analyses/vla.ml | 2 +- src/analyses/wrapperFunctionAnalysis.ml | 2 +- src/framework/analyses.ml | 4 ++-- src/framework/constraints.ml | 20 ++++++++++---------- src/framework/control.ml | 2 +- src/util/wideningTokens.ml | 2 +- src/witness/observerAnalysis.ml | 2 +- src/witness/witnessConstraints.ml | 2 +- 43 files changed, 54 insertions(+), 54 deletions(-) diff --git a/src/analyses/abortUnless.ml b/src/analyses/abortUnless.ml index 1c77803c7e..5c24e61f7c 100644 --- a/src/analyses/abortUnless.ml +++ b/src/analyses/abortUnless.ml @@ -65,7 +65,7 @@ struct false let startstate v = false - let threadenter ?(multiple=false) ctx lval f args = [false] + let threadenter ctx ~multiple lval f args = [false] let threadspawn ctx lval f args fctx = false let exitstate v = false end diff --git a/src/analyses/accessAnalysis.ml b/src/analyses/accessAnalysis.ml index bd1ca528a7..f0025c2f1c 100644 --- a/src/analyses/accessAnalysis.ml +++ b/src/analyses/accessAnalysis.ml @@ -54,7 +54,7 @@ struct (** We just lift start state, global and dependency functions: *) let startstate v = () - let threadenter ?(multiple=false) ctx lval f args = [()] + let threadenter ctx ~multiple lval f args = [()] let exitstate v = () let context fd d = () diff --git a/src/analyses/activeLongjmp.ml b/src/analyses/activeLongjmp.ml index 43da8c6512..9baa601ddc 100644 --- a/src/analyses/activeLongjmp.ml +++ b/src/analyses/activeLongjmp.ml @@ -26,7 +26,7 @@ struct (* Initial values don't really matter: overwritten at longjmp call. *) let startstate v = D.bot () - let threadenter ?(multiple=false) ctx lval f args = [D.bot ()] + let threadenter ctx ~multiple lval f args = [D.bot ()] let exitstate v = D.top () let query ctx (type a) (q: a Queries.t): a Queries.result = diff --git a/src/analyses/activeSetjmp.ml b/src/analyses/activeSetjmp.ml index a69bf4db95..be13489993 100644 --- a/src/analyses/activeSetjmp.ml +++ b/src/analyses/activeSetjmp.ml @@ -25,7 +25,7 @@ struct | _ -> ctx.local let startstate v = D.bot () - let threadenter ?(multiple=false) ctx lval f args = [D.bot ()] + let threadenter ctx ~multiple lval f args = [D.bot ()] let exitstate v = D.top () let query ctx (type a) (q: a Queries.t): a Queries.result = diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index d56064a42f..c232ccae9b 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -647,7 +647,7 @@ struct (* Thread transfer functions. *) - let threadenter ?(multiple=false) ctx lval f args = + let threadenter ctx ~multiple lval f args = let st = ctx.local in match Cilfacade.find_varinfo_fundec f with | fd -> diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 3b6be2eff8..e824fac013 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2503,7 +2503,7 @@ struct in combine_one ctx.local after - let threadenter ?(multiple=false) ctx (lval: lval option) (f: varinfo) (args: exp list): D.t list = + let threadenter ctx ~multiple (lval: lval option) (f: varinfo) (args: exp list): D.t list = match Cilfacade.find_varinfo_fundec f with | fd -> [make_entry ~thread:true ctx fd args] diff --git a/src/analyses/condVars.ml b/src/analyses/condVars.ml index 3a2cc5798d..820ff69efa 100644 --- a/src/analyses/condVars.ml +++ b/src/analyses/condVars.ml @@ -155,7 +155,7 @@ struct ctx.local let startstate v = D.bot () - let threadenter ?(multiple=false) ctx lval f args = [D.bot ()] + let threadenter ctx ~multiple lval f args = [D.bot ()] let threadspawn ctx lval f args fctx = ctx.local let exitstate v = D.bot () end diff --git a/src/analyses/expsplit.ml b/src/analyses/expsplit.ml index 9c610a96bf..141a04283b 100644 --- a/src/analyses/expsplit.ml +++ b/src/analyses/expsplit.ml @@ -84,7 +84,7 @@ struct in emit_splits ctx d - let threadenter ?(multiple=false) ctx lval f args = [ctx.local] + let threadenter ctx ~multiple lval f args = [ctx.local] let threadspawn ctx lval f args fctx = emit_splits_ctx ctx diff --git a/src/analyses/extractPthread.ml b/src/analyses/extractPthread.ml index 591704cc70..f72f72c1fe 100644 --- a/src/analyses/extractPthread.ml +++ b/src/analyses/extractPthread.ml @@ -1238,7 +1238,7 @@ module Spec : Analyses.MCPSpec = struct (Ctx.top ()) - let threadenter ?(multiple=false) ctx lval f args = + let threadenter ctx ~multiple lval f args = let d : D.t = ctx.local in let tasks = ctx.global tasks_var in (* TODO: optimize finding *) diff --git a/src/analyses/fileUse.ml b/src/analyses/fileUse.ml index b12953c71c..b8e7fd78f5 100644 --- a/src/analyses/fileUse.ml +++ b/src/analyses/fileUse.ml @@ -287,7 +287,7 @@ struct | _ -> m let startstate v = D.bot () - let threadenter ?(multiple=false) ctx lval f args = [D.bot ()] + let threadenter ctx ~multiple lval f args = [D.bot ()] let threadspawn ctx lval f args fctx = ctx.local let exitstate v = D.bot () end diff --git a/src/analyses/locksetAnalysis.ml b/src/analyses/locksetAnalysis.ml index 56fe960a47..6a816b9e6c 100644 --- a/src/analyses/locksetAnalysis.ml +++ b/src/analyses/locksetAnalysis.ml @@ -18,7 +18,7 @@ struct module C = D let startstate v = D.empty () - let threadenter ?(multiple=false) ctx lval f args = [D.empty ()] + let threadenter ctx ~multiple lval f args = [D.empty ()] let exitstate v = D.empty () end diff --git a/src/analyses/mCP.ml b/src/analyses/mCP.ml index e305e9c7f6..5259bdb6c7 100644 --- a/src/analyses/mCP.ml +++ b/src/analyses/mCP.ml @@ -565,7 +565,7 @@ struct let d = do_emits ctx !emits d q in if q then raise Deadcode else d - let threadenter ?(multiple=false) (ctx:(D.t, G.t, C.t, V.t) ctx) lval f a = + let threadenter (ctx:(D.t, G.t, C.t, V.t) ctx) ~multiple lval f a = let sides = ref [] in let emits = ref [] in let ctx'' = outer_ctx "threadenter" ~sides ~emits ctx in diff --git a/src/analyses/mallocFresh.ml b/src/analyses/mallocFresh.ml index 861e4958bd..e171ad4ea1 100644 --- a/src/analyses/mallocFresh.ml +++ b/src/analyses/mallocFresh.ml @@ -52,7 +52,7 @@ struct | None -> ctx.local | Some lval -> assign_lval (Analyses.ask_of_ctx ctx) lval ctx.local - let threadenter ?(multiple=false) ctx lval f args = + let threadenter ctx ~multiple lval f args = [D.empty ()] let threadspawn ctx lval f args fctx = diff --git a/src/analyses/malloc_null.ml b/src/analyses/malloc_null.ml index 2d90112636..41c251dfce 100644 --- a/src/analyses/malloc_null.ml +++ b/src/analyses/malloc_null.ml @@ -215,7 +215,7 @@ struct let name () = "malloc_null" let startstate v = D.empty () - let threadenter ?(multiple=false) ctx lval f args = [D.empty ()] + let threadenter ctx ~multiple lval f args = [D.empty ()] let threadspawn ctx lval f args fctx = ctx.local let exitstate v = D.empty () diff --git a/src/analyses/modifiedSinceLongjmp.ml b/src/analyses/modifiedSinceLongjmp.ml index d9c8f5102c..7da0030b9a 100644 --- a/src/analyses/modifiedSinceLongjmp.ml +++ b/src/analyses/modifiedSinceLongjmp.ml @@ -63,7 +63,7 @@ struct ctx.local let startstate v = D.bot () - let threadenter ?(multiple=false) ctx lval f args = [D.bot ()] + let threadenter ctx ~multiple lval f args = [D.bot ()] let exitstate v = D.top () let query ctx (type a) (q: a Queries.t): a Queries.result = diff --git a/src/analyses/mutexTypeAnalysis.ml b/src/analyses/mutexTypeAnalysis.ml index 7051173bd0..66e60aede1 100644 --- a/src/analyses/mutexTypeAnalysis.ml +++ b/src/analyses/mutexTypeAnalysis.ml @@ -65,7 +65,7 @@ struct | _ -> ctx.local let startstate v = D.bot () - let threadenter ?(multiple=false) ctx lval f args = [D.top ()] + let threadenter ctx ~multiple lval f args = [D.top ()] let threadspawn ctx lval f args fctx = ctx.local let exitstate v = D.top () diff --git a/src/analyses/poisonVariables.ml b/src/analyses/poisonVariables.ml index 8c79626cc9..87dddd1e54 100644 --- a/src/analyses/poisonVariables.ml +++ b/src/analyses/poisonVariables.ml @@ -61,7 +61,7 @@ struct VS.join au ctx.local let startstate v = D.bot () - let threadenter ?(multiple=false) ctx lval f args = [D.bot ()] + let threadenter ctx ~multiple lval f args = [D.bot ()] let exitstate v = D.top () let event ctx e octx = diff --git a/src/analyses/pthreadSignals.ml b/src/analyses/pthreadSignals.ml index 83455965ec..70f1624922 100644 --- a/src/analyses/pthreadSignals.ml +++ b/src/analyses/pthreadSignals.ml @@ -73,7 +73,7 @@ struct | _ -> ctx.local let startstate v = Signals.empty () - let threadenter ?(multiple=false) ctx lval f args = [ctx.local] + let threadenter ctx ~multiple lval f args = [ctx.local] let exitstate v = Signals.empty () end diff --git a/src/analyses/region.ml b/src/analyses/region.ml index 9d68221fcd..86cad5684b 100644 --- a/src/analyses/region.ml +++ b/src/analyses/region.ml @@ -175,7 +175,7 @@ struct let startstate v = `Lifted (RegMap.bot ()) - let threadenter ?(multiple=false) ctx lval f args = + let threadenter ctx ~multiple lval f args = [`Lifted (RegMap.bot ())] let threadspawn ctx lval f args fctx = ctx.local diff --git a/src/analyses/spec.ml b/src/analyses/spec.ml index e5434eb264..c44edd6c87 100644 --- a/src/analyses/spec.ml +++ b/src/analyses/spec.ml @@ -487,7 +487,7 @@ struct let startstate v = D.bot () - let threadenter ?(multiple=false) ctx lval f args = [D.bot ()] + let threadenter ctx ~multiple lval f args = [D.bot ()] let threadspawn ctx lval f args fctx = ctx.local let exitstate v = D.bot () end diff --git a/src/analyses/stackTrace.ml b/src/analyses/stackTrace.ml index 3d70c50856..3c3bd56640 100644 --- a/src/analyses/stackTrace.ml +++ b/src/analyses/stackTrace.ml @@ -21,7 +21,7 @@ struct ctx.local (* keep local as opposed to IdentitySpec *) let startstate v = D.bot () - let threadenter ?(multiple=false) ctx lval f args = [D.bot ()] + let threadenter ctx ~multiple lval f args = [D.bot ()] let exitstate v = D.top () end @@ -45,7 +45,7 @@ struct let startstate v = D.bot () let exitstate v = D.top () - let threadenter ?(multiple=false) ctx lval f args = + let threadenter ctx ~multiple lval f args = [D.push !Tracing.current_loc ctx.local] end diff --git a/src/analyses/symbLocks.ml b/src/analyses/symbLocks.ml index b99ef93039..32be32f73d 100644 --- a/src/analyses/symbLocks.ml +++ b/src/analyses/symbLocks.ml @@ -29,7 +29,7 @@ struct let name () = "symb_locks" let startstate v = D.top () - let threadenter ?(multiple=false) ctx lval f args = [D.top ()] + let threadenter ctx ~multiple lval f args = [D.top ()] let threadspawn ctx lval f args fctx = ctx.local let exitstate v = D.top () diff --git a/src/analyses/taintPartialContexts.ml b/src/analyses/taintPartialContexts.ml index 25e981dcbf..b45ea54877 100644 --- a/src/analyses/taintPartialContexts.ml +++ b/src/analyses/taintPartialContexts.ml @@ -101,7 +101,7 @@ struct d let startstate v = D.bot () - let threadenter ?(multiple=false) ctx lval f args = + let threadenter ctx ~multiple lval f args = [D.bot ()] let threadspawn ctx lval f args fctx = match lval with diff --git a/src/analyses/termination.ml b/src/analyses/termination.ml index 5e5e0d36f1..0563730fb2 100644 --- a/src/analyses/termination.ml +++ b/src/analyses/termination.ml @@ -217,7 +217,7 @@ struct (* | _ -> ctx.local *) let startstate v = D.bot () - let threadenter ?(multiple=false) ctx lval f args = [D.bot ()] + let threadenter ctx ~multiple lval f args = [D.bot ()] let exitstate v = D.bot () end diff --git a/src/analyses/threadAnalysis.ml b/src/analyses/threadAnalysis.ml index 740cca3a53..ff4b4d5c63 100644 --- a/src/analyses/threadAnalysis.ml +++ b/src/analyses/threadAnalysis.ml @@ -85,7 +85,7 @@ struct let startstate v = D.bot () - let threadenter ?(multiple=false) ctx lval f args = + let threadenter ctx ~multiple lval f args = if multiple then (let tid = ThreadId.get_current_unlift (Analyses.ask_of_ctx ctx) in ctx.sideg tid (true, TS.bot (), false)); diff --git a/src/analyses/threadEscape.ml b/src/analyses/threadEscape.ml index 0674ebf3d1..0948a3976d 100644 --- a/src/analyses/threadEscape.ml +++ b/src/analyses/threadEscape.ml @@ -150,7 +150,7 @@ struct let startstate v = D.bot () let exitstate v = D.bot () - let threadenter ?(multiple=false) ctx lval f args = + let threadenter ctx ~multiple lval f args = [D.bot ()] let threadspawn ctx lval f args fctx = diff --git a/src/analyses/threadFlag.ml b/src/analyses/threadFlag.ml index f3b132918a..81e05af679 100644 --- a/src/analyses/threadFlag.ml +++ b/src/analyses/threadFlag.ml @@ -58,7 +58,7 @@ struct let access ctx _ = is_currently_multi (Analyses.ask_of_ctx ctx) - let threadenter ?(multiple=false) ctx lval f args = + let threadenter ctx ~multiple lval f args = if not (has_ever_been_multi (Analyses.ask_of_ctx ctx)) then ctx.emit Events.EnterMultiThreaded; [create_tid f] diff --git a/src/analyses/threadId.ml b/src/analyses/threadId.ml index f1de1dfdcb..a9f3fa35f7 100644 --- a/src/analyses/threadId.ml +++ b/src/analyses/threadId.ml @@ -133,7 +133,7 @@ struct | `Lifted node, count -> node, Some count | (`Bot | `Top), _ -> ctx.prev_node, None - let threadenter ?(multiple=false) ctx lval f args:D.t list = + let threadenter ctx ~multiple lval f args:D.t list = let n, i = indexed_node_for_ctx ctx in let+ tid = create_tid ~multiple ctx.local (n, i) f in (`Lifted (f, n, i), tid, (TD.bot (), TD.bot ())) diff --git a/src/analyses/threadReturn.ml b/src/analyses/threadReturn.ml index 176a4d3465..0aed06851a 100644 --- a/src/analyses/threadReturn.ml +++ b/src/analyses/threadReturn.ml @@ -28,7 +28,7 @@ struct ctx.local (* keep local as opposed to IdentitySpec *) let startstate v = true - let threadenter ?(multiple=false) ctx lval f args = [true] + let threadenter ctx ~multiple lval f args = [true] let exitstate v = D.top () let query (ctx: (D.t, _, _, _) ctx) (type a) (x: a Queries.t): a Queries.result = diff --git a/src/analyses/tmpSpecial.ml b/src/analyses/tmpSpecial.ml index f3d092e59e..046345e627 100644 --- a/src/analyses/tmpSpecial.ml +++ b/src/analyses/tmpSpecial.ml @@ -88,7 +88,7 @@ struct | _ -> Queries.Result.top q let startstate v = D.bot () - let threadenter ?(multiple=false) ctx lval f args = [D.bot ()] + let threadenter ctx ~multiple lval f args = [D.bot ()] let threadspawn ctx lval f args fctx = ctx.local let exitstate v = D.bot () end diff --git a/src/analyses/tutorials/taint.ml b/src/analyses/tutorials/taint.ml index 166ce2c3f6..7fc3fd7343 100644 --- a/src/analyses/tutorials/taint.ml +++ b/src/analyses/tutorials/taint.ml @@ -129,7 +129,7 @@ struct (* You may leave these alone *) let startstate v = D.bot () - let threadenter ?(multiple=false) ctx lval f args = [D.top ()] + let threadenter ctx ~multiple lval f args = [D.top ()] let threadspawn ctx lval f args fctx = ctx.local let exitstate v = D.top () end diff --git a/src/analyses/tutorials/unitAnalysis.ml b/src/analyses/tutorials/unitAnalysis.ml index b5fb4d6367..3ecddc2bc0 100644 --- a/src/analyses/tutorials/unitAnalysis.ml +++ b/src/analyses/tutorials/unitAnalysis.ml @@ -39,7 +39,7 @@ struct ctx.local let startstate v = D.bot () - let threadenter ?(multiple=false) ctx lval f args = [D.top ()] + let threadenter ctx ~multiple lval f args = [D.top ()] let threadspawn ctx lval f args fctx = ctx.local let exitstate v = D.top () end diff --git a/src/analyses/uninit.ml b/src/analyses/uninit.ml index abdcd67aaa..2b388d1190 100644 --- a/src/analyses/uninit.ml +++ b/src/analyses/uninit.ml @@ -25,7 +25,7 @@ struct let name () = "uninit" let startstate v : D.t = D.empty () - let threadenter ?(multiple=false) ctx lval f args = [D.empty ()] + let threadenter ctx ~multiple lval f args = [D.empty ()] let threadspawn ctx lval f args fctx = ctx.local let exitstate v : D.t = D.empty () diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index 6033c689e1..0c7a46c35f 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -196,7 +196,7 @@ struct end | _ -> state - let threadenter ?(multiple=false) ctx lval f args = [ctx.local] + let threadenter ctx ~multiple lval f args = [ctx.local] let threadspawn ctx lval f args fctx = ctx.local let startstate v = D.bot () diff --git a/src/analyses/varEq.ml b/src/analyses/varEq.ml index 7bd3453b8a..3aaef95265 100644 --- a/src/analyses/varEq.ml +++ b/src/analyses/varEq.ml @@ -43,7 +43,7 @@ struct let name () = "var_eq" let startstate v = D.top () - let threadenter ?(multiple=false) ctx lval f args = [D.top ()] + let threadenter ctx ~multiple lval f args = [D.top ()] let threadspawn ctx lval f args fctx = ctx.local let exitstate v = D.top () diff --git a/src/analyses/vla.ml b/src/analyses/vla.ml index 8bd0168de0..665612aa99 100644 --- a/src/analyses/vla.ml +++ b/src/analyses/vla.ml @@ -33,7 +33,7 @@ struct ctx.local || Cilfacade.isVLAType v.vtype let startstate v = D.bot () - let threadenter ?(multiple=false) ctx lval f args = [D.top ()] + let threadenter ctx ~multiple lval f args = [D.top ()] let exitstate v = D.top () end diff --git a/src/analyses/wrapperFunctionAnalysis.ml b/src/analyses/wrapperFunctionAnalysis.ml index 89242e044e..a1bec69a8c 100644 --- a/src/analyses/wrapperFunctionAnalysis.ml +++ b/src/analyses/wrapperFunctionAnalysis.ml @@ -87,7 +87,7 @@ struct let startstate v = D.bot () - let threadenter ?(multiple=false) ctx lval f args = + let threadenter ctx ~multiple lval f args = (* The new thread receives a fresh counter *) [D.bot ()] diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 54a3a18f1a..3bb88a6ead 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -444,7 +444,7 @@ sig val paths_as_set : (D.t, G.t, C.t, V.t) ctx -> D.t list (** Returns initial state for created thread. *) - val threadenter : ?multiple:bool -> (D.t, G.t, C.t, V.t) ctx -> lval option -> varinfo -> exp list -> D.t list + val threadenter : (D.t, G.t, C.t, V.t) ctx -> multiple:bool -> lval option -> varinfo -> exp list -> D.t list (** Updates the local state of the creator thread using initial state of created thread. *) val threadspawn : (D.t, G.t, C.t, V.t) ctx -> lval option -> varinfo -> exp list -> (D.t, G.t, C.t, V.t) ctx -> D.t @@ -696,7 +696,7 @@ struct let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) = ctx.local - let threadenter ?(multiple=false) ctx lval f args = [ctx.local] + let threadenter ctx ~multiple lval f args = [ctx.local] let threadspawn ctx lval f args fctx = ctx.local end diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 62b8d46efa..ed492f4237 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -83,8 +83,8 @@ struct let combine_assign ctx r fe f args fc es f_ask = D.lift @@ S.combine_assign (conv ctx) r fe f args fc (D.unlift es) f_ask - let threadenter ?(multiple=false) ctx lval f args = - List.map D.lift @@ (S.threadenter ~multiple) (conv ctx) lval f args + let threadenter ctx ~multiple lval f args = + List.map D.lift @@ S.threadenter (conv ctx) ~multiple lval f args let threadspawn ctx lval f args fctx = D.lift @@ S.threadspawn (conv ctx) lval f args (conv fctx) @@ -167,8 +167,8 @@ struct let combine_assign ctx r fe f args fc es f_ask = S.combine_assign (conv ctx) r fe f args (Option.map C.unlift fc) es f_ask - let threadenter ?(multiple=false) ctx lval f args = - S.threadenter ~multiple (conv ctx) lval f args + let threadenter ctx ~multiple lval f args = + S.threadenter (conv ctx) ~multiple lval f args let threadspawn ctx lval f args fctx = S.threadspawn (conv ctx) lval f args (conv fctx) @@ -249,7 +249,7 @@ struct let combine_env' ctx r fe f args fc es f_ask = lift_fun ctx (lift ctx) S.combine_env (fun p -> p r fe f args fc (fst es) f_ask) let combine_assign' ctx r fe f args fc es f_ask = lift_fun ctx (lift ctx) S.combine_assign (fun p -> p r fe f args fc (fst es) f_ask) - let threadenter ?(multiple=false) ctx lval f args = lift_fun ctx (List.map lift_start_level) (S.threadenter ~multiple) ((|>) args % (|>) f % (|>) lval) + let threadenter ctx ~multiple lval f args = lift_fun ctx (List.map lift_start_level) (S.threadenter ~multiple) ((|>) args % (|>) f % (|>) lval) let threadspawn ctx lval f args fctx = lift_fun ctx (lift ctx) S.threadspawn ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval) let leq0 = function @@ -394,7 +394,7 @@ struct let event ctx e octx = lift_fun ctx S.event ((|>) (conv octx) % (|>) e) - let threadenter ?(multiple=false) ctx lval f args = S.threadenter ~multiple (conv ctx) lval f args |> List.map (fun d -> (d, snd ctx.local)) + let threadenter ctx ~multiple lval f args = S.threadenter (conv ctx) ~multiple lval f args |> List.map (fun d -> (d, snd ctx.local)) let threadspawn ctx lval f args fctx = lift_fun ctx S.threadspawn ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval) let enter ctx r f args = @@ -485,7 +485,7 @@ struct let combine_env ctx r fe f args fc es f_ask = lift_fun ctx D.lift S.combine_env (fun p -> p r fe f args fc (D.unlift es) f_ask) `Bot let combine_assign ctx r fe f args fc es f_ask = lift_fun ctx D.lift S.combine_assign (fun p -> p r fe f args fc (D.unlift es) f_ask) `Bot - let threadenter ?(multiple=false) ctx lval f args = lift_fun ctx (List.map D.lift) (S.threadenter ~multiple) ((|>) args % (|>) f % (|>) lval) [] + let threadenter ctx ~multiple lval f args = lift_fun ctx (List.map D.lift) (S.threadenter ~multiple) ((|>) args % (|>) f % (|>) lval) [] let threadspawn ctx lval f args fctx = lift_fun ctx D.lift S.threadspawn ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval) `Bot let event (ctx:(D.t,G.t,C.t,V.t) ctx) (e:Events.t) (octx:(D.t,G.t,C.t,V.t) ctx):D.t = lift_fun ctx D.lift S.event ((|>) (conv octx) % (|>) e) `Bot @@ -1262,7 +1262,7 @@ struct let fd1 = D.choose octx.local in map ctx Spec.event (fun h -> h e (conv octx fd1)) - let threadenter ?(multiple=false) ctx lval f args = + let threadenter ctx ~multiple lval f args = let g xs ys = (List.map (fun y -> D.singleton y) ys) @ xs in fold' ctx (Spec.threadenter ~multiple) (fun h -> h lval f args) g [] @@ -1449,7 +1449,7 @@ struct let combine_env ctx = S.combine_env (conv ctx) let combine_assign ctx = S.combine_assign (conv ctx) let special ctx = S.special (conv ctx) - let threadenter ?(multiple=false) ctx = S.threadenter ~multiple (conv ctx) + let threadenter ctx = S.threadenter (conv ctx) let threadspawn ctx lv f args fctx = S.threadspawn (conv ctx) lv f args (conv fctx) let sync ctx = S.sync (conv ctx) let skip ctx = S.skip (conv ctx) @@ -1685,7 +1685,7 @@ struct List.iter handle_path (S.paths_as_set conv_ctx); S.D.bot () | _ -> S.special conv_ctx lv f args - let threadenter ?(multiple=false) ctx = S.threadenter ~multiple (conv ctx) + let threadenter ctx = S.threadenter (conv ctx) let threadspawn ctx lv f args fctx = S.threadspawn (conv ctx) lv f args (conv fctx) let sync ctx = S.sync (conv ctx) let skip ctx = S.skip (conv ctx) diff --git a/src/framework/control.ml b/src/framework/control.ml index 72890be4b4..3ec428014b 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -423,7 +423,7 @@ struct } in (* TODO: don't hd *) - List.hd (Spec.threadenter ctx None v []) + List.hd (Spec.threadenter ctx ~multiple:false None v []) (* TODO: do threadspawn to mainfuns? *) in let prestartstate = Spec.startstate MyCFG.dummy_func.svar in (* like in do_extern_inits *) diff --git a/src/util/wideningTokens.ml b/src/util/wideningTokens.ml index c88f3f00c1..73c160e3bb 100644 --- a/src/util/wideningTokens.ml +++ b/src/util/wideningTokens.ml @@ -179,7 +179,7 @@ struct let combine_env ctx r fe f args fc es f_ask = lift_fun ctx lift' S.combine_env (fun p -> p r fe f args fc (D.unlift es) f_ask) (* TODO: use tokens from es *) let combine_assign ctx r fe f args fc es f_ask = lift_fun ctx lift' S.combine_assign (fun p -> p r fe f args fc (D.unlift es) f_ask) (* TODO: use tokens from es *) - let threadenter ?(multiple=false) ctx lval f args = lift_fun ctx (fun l ts -> List.map (Fun.flip lift' ts) l) (S.threadenter ~multiple) ((|>) args % (|>) f % (|>) lval ) + let threadenter ctx ~multiple lval f args = lift_fun ctx (fun l ts -> List.map (Fun.flip lift' ts) l) (S.threadenter ~multiple) ((|>) args % (|>) f % (|>) lval ) let threadspawn ctx lval f args fctx = lift_fun ctx lift' S.threadspawn ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval) let event ctx e octx = lift_fun ctx lift' S.event ((|>) (conv octx) % (|>) e) end diff --git a/src/witness/observerAnalysis.ml b/src/witness/observerAnalysis.ml index 3c702d199f..45a547c471 100644 --- a/src/witness/observerAnalysis.ml +++ b/src/witness/observerAnalysis.ml @@ -76,7 +76,7 @@ struct step_ctx ctx let startstate v = `Lifted Automaton.initial - let threadenter ?(multiple=false) ctx lval f args = [D.top ()] + let threadenter ctx ~multiple lval f args = [D.top ()] let threadspawn ctx lval f args fctx = ctx.local let exitstate v = D.top () end diff --git a/src/witness/witnessConstraints.ml b/src/witness/witnessConstraints.ml index ad32713fa8..1bf6294020 100644 --- a/src/witness/witnessConstraints.ml +++ b/src/witness/witnessConstraints.ml @@ -199,7 +199,7 @@ struct let r = Dom.bindings a in List.map (fun (x,v) -> (Dom.singleton x v, b)) r - let threadenter ?(multiple=false) ctx lval f args = + let threadenter ctx ~multiple lval f args = let g xs x' ys = let ys' = List.map (fun y -> let yr = step ctx.prev_node (ctx.context ()) x' (ThreadEntry (lval, f, args)) (nosync x') in (* threadenter called on before-sync state *) From 23a5d83ab8a1af6431a9c00934139ff056758642 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 25 Sep 2023 16:30:25 +0300 Subject: [PATCH 0671/1312] Add eqd to TD3 tracing output --- src/solvers/td3.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/solvers/td3.ml b/src/solvers/td3.ml index 29ad301292..07edc632c7 100644 --- a/src/solvers/td3.ml +++ b/src/solvers/td3.ml @@ -327,7 +327,7 @@ module Base = else box old eqd in - if tracing then trace "sol" "Var: %a (wp: %b)\nOld value: %a\nNew value: %a\n" S.Var.pretty_trace x wp S.Dom.pretty old S.Dom.pretty wpd; + if tracing then trace "sol" "Var: %a (wp: %b)\nOld value: %a\nEqd: %a\nNew value: %a\n" S.Var.pretty_trace x wp S.Dom.pretty old S.Dom.pretty eqd S.Dom.pretty wpd; if cache then ( if tracing then trace "cache" "cache size %d for %a\n" (HM.length l) S.Var.pretty_trace x; cache_sizes := HM.length l :: !cache_sizes; From 810fab57274821d600b5d841a8a1be977311a3b2 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 25 Sep 2023 17:36:12 +0300 Subject: [PATCH 0672/1312] Add zstd unsound both branches dead test --- .../03-practical/31-zstd-cctxpool-blobs.c | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 tests/regression/03-practical/31-zstd-cctxpool-blobs.c diff --git a/tests/regression/03-practical/31-zstd-cctxpool-blobs.c b/tests/regression/03-practical/31-zstd-cctxpool-blobs.c new file mode 100644 index 0000000000..40e448eb22 --- /dev/null +++ b/tests/regression/03-practical/31-zstd-cctxpool-blobs.c @@ -0,0 +1,29 @@ +#include +#include + +struct ZSTD_CCtx_s { + int bmi2; +}; + +typedef struct ZSTD_CCtx_s ZSTD_CCtx; + +typedef struct { + ZSTD_CCtx* cctx[1]; +} ZSTDMT_CCtxPool; + +void *t_fun(void *arg) { + return NULL; +} + +int main() { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); // enter multithreaded + + ZSTDMT_CCtxPool* const cctxPool = calloc(1, sizeof(ZSTDMT_CCtxPool)); + cctxPool->cctx[0] = malloc(sizeof(ZSTD_CCtx)); + if (!cctxPool->cctx[0]) // TODO NOWARN + __goblint_check(1); // TODO reachable + else + __goblint_check(1); // TODO reachable + return 0; +} From 5347c087b4eec4c7db77832b2c4f97c7d2841598 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 26 Sep 2023 11:22:12 +0300 Subject: [PATCH 0673/1312] Add eval_offset tracing --- src/cdomains/valueDomain.ml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/cdomains/valueDomain.ml b/src/cdomains/valueDomain.ml index 9d894b6b34..cba4b04c18 100644 --- a/src/cdomains/valueDomain.ml +++ b/src/cdomains/valueDomain.ml @@ -824,6 +824,8 @@ struct (* Funny, this does not compile without the final type annotation! *) let rec eval_offset (ask: VDQ.t) f (x: t) (offs:offs) (exp:exp option) (v:lval option) (t:typ): t = let rec do_eval_offset (ask:VDQ.t) f (x:t) (offs:offs) (exp:exp option) (l:lval option) (o:offset option) (v:lval option) (t:typ): t = + if M.tracing then M.traceli "eval_offset" "do_eval_offset %a %a (%a)\n" pretty x Offs.pretty offs (Pretty.docOpt (CilType.Exp.pretty ())) exp; + let r = match x, offs with | Blob((va, _, orig) as c), `Index (_, ox) -> begin @@ -886,6 +888,9 @@ struct | Top -> M.info ~category:Imprecise "Trying to read an index, but the array is unknown"; top () | _ -> M.warn ~category:Imprecise ~tags:[Category Program] "Trying to read an index, but was not given an array (%a)" pretty x; top () end + in + if M.tracing then M.traceu "eval_offset" "do_eval_offset -> %a\n" pretty r; + r in let l, o = match exp with | Some(Lval (x,o)) -> Some ((x, NoOffset)), Some(o) From 0caab0f1cbc006508dcd197d2687cf172659107e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 26 Sep 2023 14:01:50 +0300 Subject: [PATCH 0674/1312] Add final messages (issue #1190) --- src/util/messages.ml | 24 ++++++++++++++++++++++-- src/util/server.ml | 1 + 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/util/messages.ml b/src/util/messages.ml index a06a183eee..6cd9027739 100644 --- a/src/util/messages.ml +++ b/src/util/messages.ml @@ -248,12 +248,21 @@ let add m = Table.add m ) +let final_table: unit Table.MH.t = Table.MH.create 13 + +let add_final m = + Table.MH.replace final_table m () + let finalize () = if get_bool "warn.deterministic" then ( !Table.messages_list |> List.sort Message.compare |> List.iter print - ) + ); + Table.MH.iter (fun m () -> + print m; + Table.add m + ) final_table (* TODO: also deterministic case *) let current_context: ControlSpecC.t option ref = ref None @@ -282,7 +291,7 @@ let msg_noloc severity ?(tags=[]) ?(category=Category.Unknown) fmt = if !AnalysisState.should_warn && Severity.should_warn severity && (Category.should_warn category || Tags.should_warn tags) then ( let finish doc = let text = GobPretty.show doc in - add {tags = Category category :: tags; severity; multipiece = Single {loc = None; text; context = msg_context ()}} + add {tags = Category category :: tags; severity; multipiece = Single {loc = None; text; context = msg_context ()}} (* TODO: why context? *) in Pretty.gprintf finish fmt ) @@ -316,4 +325,15 @@ let debug_noloc ?tags = msg_noloc Debug ?tags let success ?loc = msg Success ?loc let success_noloc ?tags = msg_noloc Success ?tags +let msg_final severity ?(tags=[]) ?(category=Category.Unknown) fmt = + if !AnalysisState.should_warn then ( + let finish doc = + let text = GobPretty.show doc in + add_final {tags = Category category :: tags; severity; multipiece = Single {loc = None; text; context = msg_context ()}} (* TODO: why context? *) + in + Pretty.gprintf finish fmt + ) + else + GobPretty.igprintf () fmt + include Tracing diff --git a/src/util/server.ml b/src/util/server.ml index e133fb96c3..22f5a03350 100644 --- a/src/util/server.ml +++ b/src/util/server.ml @@ -264,6 +264,7 @@ let node_locator: Locator.t ResettableLazy.t = let analyze ?(reset=false) (s: t) = Messages.Table.(MH.clear messages_table); + Messages.(Table.MH.clear final_table); Messages.Table.messages_list := []; let file, reparsed = reparse s in if reset then ( From 857b6c1654ec37220ded09f0f1e79b341574e243 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 26 Sep 2023 14:02:06 +0300 Subject: [PATCH 0675/1312] Add fixpoint not reached final message --- src/solvers/postSolver.ml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/solvers/postSolver.ml b/src/solvers/postSolver.ml index 346ce312b1..f96ca832a1 100644 --- a/src/solvers/postSolver.ml +++ b/src/solvers/postSolver.ml @@ -82,10 +82,13 @@ module Verify: F = let complain_constraint x ~lhs ~rhs = AnalysisState.verified := Some false; + M.msg_final Error ~category:Unsound "Fixpoint not reached"; ignore (Pretty.printf "Fixpoint not reached at %a\n @[Solver computed:\n%a\nRight-Hand-Side:\n%a\nDifference: %a\n@]" S.Var.pretty_trace x S.Dom.pretty lhs S.Dom.pretty rhs S.Dom.pretty_diff (rhs, lhs)) let complain_side x y ~lhs ~rhs = AnalysisState.verified := Some false; + + M.msg_final Error ~category:Unsound "Fixpoint not reached"; ignore (Pretty.printf "Fixpoint not reached at %a\nOrigin: %a\n @[Solver computed:\n%a\nSide-effect:\n%a\nDifference: %a\n@]" S.Var.pretty_trace y S.Var.pretty_trace x S.Dom.pretty lhs S.Dom.pretty rhs S.Dom.pretty_diff (rhs, lhs)) let one_side ~vh ~x ~y ~d = From f2d2c1c498749e86f43c8ae6e80342290617c2a9 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 26 Sep 2023 14:04:56 +0300 Subject: [PATCH 0676/1312] Add function definition missing final message --- src/analyses/libraryFunctions.ml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 137a3103a5..b12da8230b 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -1219,8 +1219,10 @@ let unknown_desc ~f name = (* TODO: remove name argument, unknown function shoul match classify name args with | `Unknown _ as category -> (* TODO: remove hack when all classify are migrated *) - if not (CilType.Varinfo.equal f dummyFunDec.svar) && not (use_special f.vname) then - M.error ~category:Imprecise ~tags:[Category Unsound] "Function definition missing for %s" f.vname; + if not (CilType.Varinfo.equal f dummyFunDec.svar) && not (use_special f.vname) then ( + M.msg_final Error ~category:Imprecise ~tags:[Category Unsound] "Function definition missing"; + M.error ~category:Imprecise ~tags:[Category Unsound] "Function definition missing for %s" f.vname + ); category | category -> category in From 2430e6e53d1983230c0d7f6d3849a8a54b815ec8 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 26 Sep 2023 14:29:38 +0300 Subject: [PATCH 0677/1312] Add both branches dead final message --- src/framework/constraints.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 288b7ad0ea..ff2a08263b 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1390,6 +1390,7 @@ struct let cilinserted = if loc.synthetic then "(possibly inserted by CIL) " else "" in M.warn ~loc:(Node g) ~tags:[CWE (if tv then 571 else 570)] ~category:Deadcode "condition '%a' %sis always %B" d_exp exp cilinserted tv | `Bot when not (CilType.Exp.equal exp one) -> (* all branches dead *) + M.msg_final Error ~category:Analyzer ~tags:[Category Unsound] "Both branches dead"; M.error ~loc:(Node g) ~category:Analyzer ~tags:[Category Unsound] "both branches over condition '%a' are dead" d_exp exp | `Bot (* all branches dead, fine at our inserted Neg(1)-s because no Pos(1) *) | `Top -> (* may be both true and false *) From 50609787821b53611716cdf705e3b594e58e7d18 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 26 Sep 2023 14:29:47 +0300 Subject: [PATCH 0678/1312] Add ASM final message --- src/framework/analyses.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index df3346af93..bb2170509d 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -643,7 +643,8 @@ struct let vdecl ctx _ = ctx.local let asm x = - ignore (M.info ~category:Unsound "ASM statement ignored."); + M.msg_final Info ~category:Unsound "ASM ignored"; + M.info ~category:Unsound "ASM statement ignored."; x.local (* Just ignore. *) let skip x = x.local (* Just ignore. *) From a28a8ffc58287e087b6fab6322247a16e6fc79f6 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 26 Sep 2023 14:29:58 +0300 Subject: [PATCH 0679/1312] Add no suitable function final message --- src/framework/constraints.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index ff2a08263b..4efb0d5346 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -785,6 +785,7 @@ struct in let funs = List.filter_map one_function functions in if [] = funs then begin + M.msg_final Warning ~category:Unsound ~tags:[Category Call] "No suitable function to call"; M.warn ~category:Unsound ~tags:[Category Call] "No suitable function to be called at call site. Continuing with state before call."; d (* because LevelSliceLifter *) end else From c4bf1ccbeb23c2f8248fa755a8f4949efbe328e9 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 26 Sep 2023 15:16:51 +0300 Subject: [PATCH 0680/1312] Add some library functions for zstd --- src/analyses/libraryFunctions.ml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 137a3103a5..9ee9dc8c9d 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -209,6 +209,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("strnlen", unknown [drop "s" [r]; drop "maxlen" []]); ("chmod", unknown [drop "pathname" [r]; drop "mode" []]); ("fchmod", unknown [drop "fd" []; drop "mode" []]); + ("chown", unknown [drop "pathname" [r]; drop "owner" []; drop "group" []]); ("fchown", unknown [drop "fd" []; drop "owner" []; drop "group" []]); ("lchown", unknown [drop "pathname" [r]; drop "owner" []; drop "group" []]); ("clock_gettime", unknown [drop "clockid" []; drop "tp" [w]]); @@ -245,6 +246,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("timer_settime", unknown [drop "timerid" []; drop "flags" []; drop "new_value" [r_deep]; drop "old_value" [w_deep]]); ("timer_gettime", unknown [drop "timerid" []; drop "curr_value" [w_deep]]); ("timer_getoverrun", unknown [drop "timerid" []]); + ("fstat", unknown [drop "fd" []; drop "statbuf" [w]]); ("lstat", unknown [drop "pathname" [r]; drop "statbuf" [w]]); ("getpwnam", unknown [drop "name" [r]]); ("chdir", unknown [drop "path" [r]]); @@ -833,12 +835,23 @@ let zlib_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("inflateInit2", unknown [drop "strm" [r_deep; w_deep]; drop "windowBits" []]); ("inflateInit2_", unknown [drop "strm" [r_deep; w_deep]; drop "windowBits" []; drop "version" [r]; drop "stream_size" []]); ("inflateEnd", unknown [drop "strm" [f_deep]]); + ("deflate", unknown [drop "strm" [r_deep; w_deep]; drop "flush" []]); + ("deflateInit2", unknown [drop "strm" [r_deep; w_deep]; drop "level" []; drop "method" []; drop "windowBits" []; drop "memLevel" []; drop "strategy" []]); + ("deflateInit2_", unknown [drop "strm" [r_deep; w_deep]; drop "level" []; drop "method" []; drop "windowBits" []; drop "memLevel" []; drop "strategy" []; drop "version" [r]; drop "stream_size" []]); + ("deflateEnd", unknown [drop "strm" [f_deep]]); + ("zlibVersion", unknown []); ] let liblzma_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("lzma_code", unknown [drop "strm" [r_deep; w_deep]; drop "action" []]); ("lzma_auto_decoder", unknown [drop "strm" [r_deep; w_deep]; drop "memlimit" []; drop "flags" []]); + ("lzma_alone_decoder", unknown [drop "strm" [r_deep; w_deep]; drop "memlimit" []]); + ("lzma_stream_decoder", unknown [drop "strm" [r_deep; w_deep]; drop "memlimit" []; drop "flags" []]); + ("lzma_alone_encoder", unknown [drop "strm" [r_deep; w_deep]; drop "options" [r_deep]]); + ("lzma_easy_encoder", unknown [drop "strm" [r_deep; w_deep]; drop "preset" []; drop "check" []]); ("lzma_end", unknown [drop "strm" [r_deep; w_deep; f_deep]]); + ("lzma_version_string", unknown []); + ("lzma_lzma_preset", unknown [drop "options" [w_deep]; drop "preset" []]); ] let libraries = Hashtbl.of_list [ From 684a74cd39ab8fd4e1a8514beec44cd879a88fe9 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 26 Sep 2023 15:20:34 +0300 Subject: [PATCH 0681/1312] Update cram tests with final messages --- tests/regression/04-mutex/49-type-invariants.t | 2 ++ tests/regression/04-mutex/77-type-nested-fields.t | 1 + tests/regression/04-mutex/79-type-nested-fields-deep1.t | 1 + tests/regression/04-mutex/80-type-nested-fields-deep2.t | 1 + tests/regression/04-mutex/90-distribute-fields-type-1.t | 1 + tests/regression/04-mutex/91-distribute-fields-type-2.t | 1 + tests/regression/04-mutex/92-distribute-fields-type-deep.t | 1 + tests/regression/04-mutex/93-distribute-fields-type-global.t | 1 + tests/regression/06-symbeq/16-type_rc.t | 2 ++ tests/regression/06-symbeq/21-mult_accs_rc.t | 2 ++ 10 files changed, 13 insertions(+) diff --git a/tests/regression/04-mutex/49-type-invariants.t b/tests/regression/04-mutex/49-type-invariants.t index 3ddd8f237d..4c105d1559 100644 --- a/tests/regression/04-mutex/49-type-invariants.t +++ b/tests/regression/04-mutex/49-type-invariants.t @@ -21,6 +21,7 @@ [Info][Imprecise] Invalidating expressions: AddrOf(Var(s, NoOffset)) (49-type-invariants.c:21:3-21:21) [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (49-type-invariants.c:21:3-21:21) [Error][Imprecise][Unsound] Function definition missing for getS (49-type-invariants.c:21:3-21:21) + [Error][Imprecise][Unsound] Function definition missing $ goblint --enable warn.deterministic --disable ana.race.direct-arithmetic --enable allglobs 49-type-invariants.c [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (49-type-invariants.c:21:3-21:21) @@ -45,3 +46,4 @@ [Info][Imprecise] Invalidating expressions: AddrOf(Var(s, NoOffset)) (49-type-invariants.c:21:3-21:21) [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (49-type-invariants.c:21:3-21:21) [Error][Imprecise][Unsound] Function definition missing for getS (49-type-invariants.c:21:3-21:21) + [Error][Imprecise][Unsound] Function definition missing diff --git a/tests/regression/04-mutex/77-type-nested-fields.t b/tests/regression/04-mutex/77-type-nested-fields.t index 738784f5d5..bb935cb0ed 100644 --- a/tests/regression/04-mutex/77-type-nested-fields.t +++ b/tests/regression/04-mutex/77-type-nested-fields.t @@ -27,3 +27,4 @@ [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (77-type-nested-fields.c:38:3-38:22) [Error][Imprecise][Unsound] Function definition missing for getS (77-type-nested-fields.c:31:3-31:20) [Error][Imprecise][Unsound] Function definition missing for getT (77-type-nested-fields.c:38:3-38:22) + [Error][Imprecise][Unsound] Function definition missing diff --git a/tests/regression/04-mutex/79-type-nested-fields-deep1.t b/tests/regression/04-mutex/79-type-nested-fields-deep1.t index 2e15f83c39..ba1399d225 100644 --- a/tests/regression/04-mutex/79-type-nested-fields-deep1.t +++ b/tests/regression/04-mutex/79-type-nested-fields-deep1.t @@ -27,3 +27,4 @@ [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (79-type-nested-fields-deep1.c:43:3-43:24) [Error][Imprecise][Unsound] Function definition missing for getS (79-type-nested-fields-deep1.c:36:3-36:20) [Error][Imprecise][Unsound] Function definition missing for getU (79-type-nested-fields-deep1.c:43:3-43:24) + [Error][Imprecise][Unsound] Function definition missing diff --git a/tests/regression/04-mutex/80-type-nested-fields-deep2.t b/tests/regression/04-mutex/80-type-nested-fields-deep2.t index 48e726f623..71bdcfb2e2 100644 --- a/tests/regression/04-mutex/80-type-nested-fields-deep2.t +++ b/tests/regression/04-mutex/80-type-nested-fields-deep2.t @@ -27,3 +27,4 @@ [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (80-type-nested-fields-deep2.c:43:3-43:24) [Error][Imprecise][Unsound] Function definition missing for getT (80-type-nested-fields-deep2.c:36:3-36:22) [Error][Imprecise][Unsound] Function definition missing for getU (80-type-nested-fields-deep2.c:43:3-43:24) + [Error][Imprecise][Unsound] Function definition missing diff --git a/tests/regression/04-mutex/90-distribute-fields-type-1.t b/tests/regression/04-mutex/90-distribute-fields-type-1.t index ba3e3da0ed..46435045b9 100644 --- a/tests/regression/04-mutex/90-distribute-fields-type-1.t +++ b/tests/regression/04-mutex/90-distribute-fields-type-1.t @@ -29,3 +29,4 @@ [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (90-distribute-fields-type-1.c:39:3-39:17) [Error][Imprecise][Unsound] Function definition missing for getS (90-distribute-fields-type-1.c:31:3-31:20) [Error][Imprecise][Unsound] Function definition missing for getT (90-distribute-fields-type-1.c:39:3-39:17) + [Error][Imprecise][Unsound] Function definition missing diff --git a/tests/regression/04-mutex/91-distribute-fields-type-2.t b/tests/regression/04-mutex/91-distribute-fields-type-2.t index fd544b0b0b..c7e66c0527 100644 --- a/tests/regression/04-mutex/91-distribute-fields-type-2.t +++ b/tests/regression/04-mutex/91-distribute-fields-type-2.t @@ -29,3 +29,4 @@ [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (91-distribute-fields-type-2.c:40:3-40:17) [Error][Imprecise][Unsound] Function definition missing for getS (91-distribute-fields-type-2.c:32:3-32:17) [Error][Imprecise][Unsound] Function definition missing for getT (91-distribute-fields-type-2.c:40:3-40:17) + [Error][Imprecise][Unsound] Function definition missing diff --git a/tests/regression/04-mutex/92-distribute-fields-type-deep.t b/tests/regression/04-mutex/92-distribute-fields-type-deep.t index aefc1520d1..4fc1c7e101 100644 --- a/tests/regression/04-mutex/92-distribute-fields-type-deep.t +++ b/tests/regression/04-mutex/92-distribute-fields-type-deep.t @@ -29,3 +29,4 @@ [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (92-distribute-fields-type-deep.c:44:3-44:17) [Error][Imprecise][Unsound] Function definition missing for getS (92-distribute-fields-type-deep.c:36:3-36:20) [Error][Imprecise][Unsound] Function definition missing for getU (92-distribute-fields-type-deep.c:44:3-44:17) + [Error][Imprecise][Unsound] Function definition missing diff --git a/tests/regression/04-mutex/93-distribute-fields-type-global.t b/tests/regression/04-mutex/93-distribute-fields-type-global.t index 2199c689b1..bf34d99936 100644 --- a/tests/regression/04-mutex/93-distribute-fields-type-global.t +++ b/tests/regression/04-mutex/93-distribute-fields-type-global.t @@ -23,3 +23,4 @@ [Info][Imprecise] Invalidating expressions: AddrOf(Var(s, NoOffset)) (93-distribute-fields-type-global.c:13:3-13:29) [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (93-distribute-fields-type-global.c:13:3-13:29) [Error][Imprecise][Unsound] Function definition missing for getS (93-distribute-fields-type-global.c:13:3-13:29) + [Error][Imprecise][Unsound] Function definition missing diff --git a/tests/regression/06-symbeq/16-type_rc.t b/tests/regression/06-symbeq/16-type_rc.t index 06a3b314a4..b63471a45e 100644 --- a/tests/regression/06-symbeq/16-type_rc.t +++ b/tests/regression/06-symbeq/16-type_rc.t @@ -10,6 +10,7 @@ Disable info messages because race summary contains (safe) memory location count write with [mhp:{tid=[main]; created={[main, t_fun@16-type_rc.c:35:3-35:37#top]}}, thread:[main]] (conf. 100) (exp: & *d) (16-type_rc.c:36:3-36:9) [Error][Imprecise][Unsound] Function definition missing for get_s (16-type_rc.c:20:12-20:24) [Error][Imprecise][Unsound] Function definition missing for get_s (16-type_rc.c:31:3-31:14) + [Error][Imprecise][Unsound] Function definition missing $ goblint --enable warn.deterministic --disable warn.info --disable ana.race.direct-arithmetic --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" --enable allglobs 16-type_rc.c [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (16-type_rc.c:21:3-21:15) @@ -20,3 +21,4 @@ Disable info messages because race summary contains (safe) memory location count write with [mhp:{tid=[main, t_fun@16-type_rc.c:35:3-35:37#top]}, thread:[main, t_fun@16-type_rc.c:35:3-35:37#top]] (conf. 100) (exp: & s->datum) (16-type_rc.c:21:3-21:15) [Error][Imprecise][Unsound] Function definition missing for get_s (16-type_rc.c:20:12-20:24) [Error][Imprecise][Unsound] Function definition missing for get_s (16-type_rc.c:31:3-31:14) + [Error][Imprecise][Unsound] Function definition missing diff --git a/tests/regression/06-symbeq/21-mult_accs_rc.t b/tests/regression/06-symbeq/21-mult_accs_rc.t index 9b02dcde76..227c66058e 100644 --- a/tests/regression/06-symbeq/21-mult_accs_rc.t +++ b/tests/regression/06-symbeq/21-mult_accs_rc.t @@ -15,6 +15,7 @@ Disable info messages because race summary contains (safe) memory location count [Error][Imprecise][Unsound] Function definition missing for get_s (21-mult_accs_rc.c:13:3-13:14) [Error][Imprecise][Unsound] Function definition missing for get_s (21-mult_accs_rc.c:15:3-15:14) [Error][Imprecise][Unsound] Function definition missing for get_s (21-mult_accs_rc.c:27:3-27:14) + [Error][Imprecise][Unsound] Function definition missing $ goblint --enable warn.deterministic --disable warn.info --disable ana.race.direct-arithmetic --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" --enable allglobs 21-mult_accs_rc.c [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (21-mult_accs_rc.c:14:3-14:32) @@ -30,3 +31,4 @@ Disable info messages because race summary contains (safe) memory location count [Error][Imprecise][Unsound] Function definition missing for get_s (21-mult_accs_rc.c:13:3-13:14) [Error][Imprecise][Unsound] Function definition missing for get_s (21-mult_accs_rc.c:15:3-15:14) [Error][Imprecise][Unsound] Function definition missing for get_s (21-mult_accs_rc.c:27:3-27:14) + [Error][Imprecise][Unsound] Function definition missing From f06a0f5c7592b750c3ddb471d680b19a9634684c Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 26 Sep 2023 16:49:10 +0200 Subject: [PATCH 0682/1312] Add check for bot and top address offsets --- src/analyses/memOutOfBounds.ml | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 97907e9d6f..4b39594bb8 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -177,12 +177,7 @@ struct | true -> M.warn "Pointer %a has an empty points-to-set" d_exp ptr; IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind () - | false -> - (* - Offset should be the same for all elements in the points-to set. - Hence, we can just pick one element and obtain its offset - *) - let (_, o) = VDQ.LS.choose a in + | false -> let rec to_int_dom_offs = function | `NoOffset -> `NoOffset | `Field (f, o) -> `Field (f, to_int_dom_offs o) @@ -194,6 +189,20 @@ struct in `Index (exp_as_int_dom, to_int_dom_offs o) in + let () = + if VDQ.LS.exists (fun (_, o) -> IntDomain.IntDomTuple.is_bot @@ offs_to_idx t (to_int_dom_offs o)) a then ( + (* TODO: Uncomment once staging-memsafety branch changes are applied *) + (* set_mem_safety_flag InvalidDeref; *) + M.warn "Pointer %a has a bot address offset. An invalid memory access may occur" d_exp ptr + ) else if VDQ.LS.exists (fun (_, o) -> IntDomain.IntDomTuple.is_top @@ offs_to_idx t (to_int_dom_offs o)) a then ( + (* TODO: Uncomment once staging-memsafety branch changes are applied *) + (* set_mem_safety_flag InvalidDeref; *) + M.warn "Pointer %a has a top address offset. An invalid memory access may occur" d_exp ptr + ) + in + (* Offset should be the same for all elements in the points-to set *) + (* Hence, we can just pick one element and obtain its offset *) + let (_, o) = VDQ.LS.choose a in offs_to_idx t (to_int_dom_offs o) end | None -> From eb97eca09e20c94924c2235d0ecd1a7cad12b626 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 27 Sep 2023 11:26:18 +0300 Subject: [PATCH 0683/1312] Remove TODO annotations from passing checks --- tests/regression/01-cpa/33-asserts.c | 8 ++++---- tests/regression/28-race_reach/22-deref_read_racefree.c | 2 +- tests/regression/66-interval-set-one/51-widen-sides.c | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/regression/01-cpa/33-asserts.c b/tests/regression/01-cpa/33-asserts.c index f8bf6c3132..26efad44fc 100644 --- a/tests/regression/01-cpa/33-asserts.c +++ b/tests/regression/01-cpa/33-asserts.c @@ -26,14 +26,14 @@ int main(){ check(j==6); // assert UNKNOWN unknown(&k); - assume(k==4); // TODO? assert SUCCESS + assume(k==4); check(k==4); // assert SUCCESS unknown(&k); - assume(k+1==n); // TODO? FAIL + assume(k+1==n); - assume(n==5); // TODO? NOWARN - assert(0); // NOWARN + assume(n==5); // contradiction + assert(0); // NOWARN (unreachable) return 0; } \ No newline at end of file diff --git a/tests/regression/28-race_reach/22-deref_read_racefree.c b/tests/regression/28-race_reach/22-deref_read_racefree.c index 3386277083..2e4c5ebbb6 100644 --- a/tests/regression/28-race_reach/22-deref_read_racefree.c +++ b/tests/regression/28-race_reach/22-deref_read_racefree.c @@ -17,7 +17,7 @@ int main() { create_threads(t); q = p; pthread_mutex_lock(&mutex); - assert_racefree(*q); // TODO + assert_racefree(*q); pthread_mutex_unlock(&mutex); return 0; } diff --git a/tests/regression/66-interval-set-one/51-widen-sides.c b/tests/regression/66-interval-set-one/51-widen-sides.c index 72eb1396b1..b086baf026 100644 --- a/tests/regression/66-interval-set-one/51-widen-sides.c +++ b/tests/regression/66-interval-set-one/51-widen-sides.c @@ -3,13 +3,13 @@ int further(int n) { // Even sides-local can not save us here :( - __goblint_check(n <= 1); //TODO + __goblint_check(n <= 2); //TODO } int fun(int n, const char* arg) { // Fails with solvers.td3.side_widen sides, needs sides-local - __goblint_check(n <= 1); + __goblint_check(n <= 2); further(n); } @@ -26,5 +26,5 @@ int main() { doIt("two"); // In the setting with solvers.td3.side_widen sides, widening happens and the bound is lost - fun(1, "org"); + fun(2, "org"); } From a2bea7b3081b9c7ebe92d91dc2cf21283ece3ee8 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 27 Sep 2023 14:45:21 +0200 Subject: [PATCH 0684/1312] Fix issues after merge with master --- src/analyses/base.ml | 7 ++- src/analyses/memOutOfBounds.ml | 86 +++++++++++++++++----------------- 2 files changed, 47 insertions(+), 46 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index c1047f9c6a..8b76a1cc03 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1261,10 +1261,9 @@ struct else ( (* If we need the BlobSize from the base address, then remove any offsets *) let a = - if from_base_addr then - ValueDomainQueries.LS.elements s - |> List.map (fun (v, _) -> Addr.of_var v) - |> AD.of_list + if from_base_addr then AD.map (function + | Addr (v, o) -> Addr (v, `NoOffset) + | addr -> addr) a else a in diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 4b39594bb8..94c16e9c94 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -92,8 +92,11 @@ struct let points_to_heap_only ctx ptr = match ctx.ask (Queries.MayPointTo ptr) with - | a when not (Queries.LS.is_top a) && not (Queries.LS.mem (dummyFunDec.svar, `NoOffset) a) -> - Queries.LS.for_all (fun (v, _) -> ctx.ask (Queries.IsHeapVar v)) a + | a when not (Queries.AD.is_top a)-> + Queries.AD.for_all (function + | Addr (v, o) -> ctx.ask (Queries.IsHeapVar v) + | _ -> false + ) a | _ -> false let get_size_of_ptr_target ctx ptr = @@ -102,21 +105,25 @@ struct ctx.ask (Queries.BlobSize {exp = ptr; base_address = true}) else match ctx.ask (Queries.MayPointTo ptr) with - | a when not (Queries.LS.is_top a) -> - let pts_list = Queries.LS.elements a in - let pts_elems_to_sizes (v, _) = - begin match v.vtype with - | TArray (item_typ, _, _) -> - let item_typ_size_in_bytes = (bitsSizeOf item_typ) / 8 in - let item_typ_size_in_bytes = intdom_of_int item_typ_size_in_bytes in - begin match ctx.ask (Queries.EvalLength ptr) with - | `Lifted arr_len -> `Lifted (IntDomain.IntDomTuple.mul item_typ_size_in_bytes arr_len) - | `Bot -> VDQ.ID.bot () - | `Top -> VDQ.ID.top () + | a when not (Queries.AD.is_top a) -> + let pts_list = Queries.AD.elements a in + let pts_elems_to_sizes (addr: Queries.AD.elt) = + begin match addr with + | Addr (v, _) -> + begin match v.vtype with + | TArray (item_typ, _, _) -> + let item_typ_size_in_bytes = (bitsSizeOf item_typ) / 8 in + let item_typ_size_in_bytes = intdom_of_int item_typ_size_in_bytes in + begin match ctx.ask (Queries.EvalLength ptr) with + | `Lifted arr_len -> `Lifted (IntDomain.IntDomTuple.mul item_typ_size_in_bytes arr_len) + | `Bot -> VDQ.ID.bot () + | `Top -> VDQ.ID.top () + end + | _ -> + let type_size_in_bytes = (bitsSizeOf v.vtype) / 8 in + `Lifted (intdom_of_int type_size_in_bytes) end - | _ -> - let type_size_in_bytes = (bitsSizeOf v.vtype) / 8 in - `Lifted (intdom_of_int type_size_in_bytes) + | _ -> VDQ.ID.top () end in (* Map each points-to-set element to its size *) @@ -169,41 +176,36 @@ struct let rec get_addr_offs ctx ptr = match ctx.ask (Queries.MayPointTo ptr) with - | a when not (VDQ.LS.is_top a) -> + | a when not (VDQ.AD.is_top a) -> let ptr_deref_type = get_ptr_deref_type @@ typeOf ptr in begin match ptr_deref_type with | Some t -> - begin match VDQ.LS.is_empty a with + begin match VDQ.AD.is_empty a with | true -> M.warn "Pointer %a has an empty points-to-set" d_exp ptr; IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind () | false -> - let rec to_int_dom_offs = function - | `NoOffset -> `NoOffset - | `Field (f, o) -> `Field (f, to_int_dom_offs o) - | `Index (i, o) -> - let exp_as_int_dom = match ctx.ask (Queries.EvalInt i) with - | `Lifted i -> i - | `Bot -> IntDomain.IntDomTuple.bot_of @@ Cilfacade.ptrdiff_ikind () - | `Top -> IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind () - in - `Index (exp_as_int_dom, to_int_dom_offs o) - in - let () = - if VDQ.LS.exists (fun (_, o) -> IntDomain.IntDomTuple.is_bot @@ offs_to_idx t (to_int_dom_offs o)) a then ( - (* TODO: Uncomment once staging-memsafety branch changes are applied *) - (* set_mem_safety_flag InvalidDeref; *) - M.warn "Pointer %a has a bot address offset. An invalid memory access may occur" d_exp ptr - ) else if VDQ.LS.exists (fun (_, o) -> IntDomain.IntDomTuple.is_top @@ offs_to_idx t (to_int_dom_offs o)) a then ( - (* TODO: Uncomment once staging-memsafety branch changes are applied *) - (* set_mem_safety_flag InvalidDeref; *) - M.warn "Pointer %a has a top address offset. An invalid memory access may occur" d_exp ptr - ) - in + if VDQ.AD.exists (function + | Addr (_, o) -> IntDomain.IntDomTuple.is_bot @@ offs_to_idx t o + | _ -> false + ) a then ( + (* TODO: Uncomment once staging-memsafety branch changes are applied *) + (* set_mem_safety_flag InvalidDeref; *) + M.warn "Pointer %a has a bot address offset. An invalid memory access may occur" d_exp ptr + ) else if VDQ.AD.exists (function + | Addr (_, o) -> IntDomain.IntDomTuple.is_bot @@ offs_to_idx t o + | _ -> false + ) a then ( + (* TODO: Uncomment once staging-memsafety branch changes are applied *) + (* set_mem_safety_flag InvalidDeref; *) + M.warn "Pointer %a has a top address offset. An invalid memory access may occur" d_exp ptr + ); (* Offset should be the same for all elements in the points-to set *) (* Hence, we can just pick one element and obtain its offset *) - let (_, o) = VDQ.LS.choose a in - offs_to_idx t (to_int_dom_offs o) + begin match VDQ.AD.choose a with + | Addr (_, o) -> offs_to_idx t o + | _ -> IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind () + end end | None -> M.error "Expression %a doesn't have pointer type" d_exp ptr; From f0c73c53fa4b88ddeb0484a01b05aa142a07dcf9 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 27 Sep 2023 15:00:56 +0200 Subject: [PATCH 0685/1312] Add support for 3rd param of memcpy --- src/analyses/libraryDesc.ml | 2 +- src/analyses/libraryFunctions.ml | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/analyses/libraryDesc.ml b/src/analyses/libraryDesc.ml index 94de4fbf82..2ff1169db8 100644 --- a/src/analyses/libraryDesc.ml +++ b/src/analyses/libraryDesc.ml @@ -62,7 +62,7 @@ type special = | Math of { fun_args: math; } | Memset of { dest: Cil.exp; ch: Cil.exp; count: Cil.exp; } | Bzero of { dest: Cil.exp; count: Cil.exp; } - | Memcpy of { dest: Cil.exp; src: Cil.exp } + | Memcpy of { dest: Cil.exp; src: Cil.exp; n: Cil.exp; } | Strcpy of { dest: Cil.exp; src: Cil.exp; n: Cil.exp option; } | Strcat of { dest: Cil.exp; src: Cil.exp; n: Cil.exp option; } | Strlen of Cil.exp diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 137a3103a5..575c036a23 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -12,12 +12,12 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("memset", special [__ "dest" [w]; __ "ch" []; __ "count" []] @@ fun dest ch count -> Memset { dest; ch; count; }); ("__builtin_memset", special [__ "dest" [w]; __ "ch" []; __ "count" []] @@ fun dest ch count -> Memset { dest; ch; count; }); ("__builtin___memset_chk", special [__ "dest" [w]; __ "ch" []; __ "count" []; drop "os" []] @@ fun dest ch count -> Memset { dest; ch; count; }); - ("memcpy", special [__ "dest" [w]; __ "src" [r]; drop "n" []] @@ fun dest src -> Memcpy { dest; src }); - ("__builtin_memcpy", special [__ "dest" [w]; __ "src" [r]; drop "n" []] @@ fun dest src -> Memcpy { dest; src }); - ("__builtin___memcpy_chk", special [__ "dest" [w]; __ "src" [r]; drop "n" []; drop "os" []] @@ fun dest src -> Memcpy { dest; src }); - ("memmove", special [__ "dest" [w]; __ "src" [r]; drop "count" []] @@ fun dest src -> Memcpy { dest; src }); - ("__builtin_memmove", special [__ "dest" [w]; __ "src" [r]; drop "count" []] @@ fun dest src -> Memcpy { dest; src }); - ("__builtin___memmove_chk", special [__ "dest" [w]; __ "src" [r]; drop "count" []; drop "os" []] @@ fun dest src -> Memcpy { dest; src }); + ("memcpy", special [__ "dest" [w]; __ "src" [r]; __ "n" []] @@ fun dest src n -> Memcpy { dest; src; n; }); + ("__builtin_memcpy", special [__ "dest" [w]; __ "src" [r]; __ "n" []] @@ fun dest src n -> Memcpy { dest; src; n; }); + ("__builtin___memcpy_chk", special [__ "dest" [w]; __ "src" [r]; __ "n" []; drop "os" []] @@ fun dest src n -> Memcpy { dest; src; n; }); + ("memmove", special [__ "dest" [w]; __ "src" [r]; __ "count" []] @@ fun dest src count -> Memcpy { dest; src; n = count; }); + ("__builtin_memmove", special [__ "dest" [w]; __ "src" [r]; __ "count" []] @@ fun dest src count -> Memcpy { dest; src; n = count; }); + ("__builtin___memmove_chk", special [__ "dest" [w]; __ "src" [r]; __ "count" []; drop "os" []] @@ fun dest src count -> Memcpy { dest; src; n = count; }); ("strcpy", special [__ "dest" [w]; __ "src" [r]] @@ fun dest src -> Strcpy { dest; src; n = None; }); ("__builtin_strcpy", special [__ "dest" [w]; __ "src" [r]] @@ fun dest src -> Strcpy { dest; src; n = None; }); ("__builtin___strcpy_chk", special [__ "dest" [w]; __ "src" [r]; drop "os" []] @@ fun dest src -> Strcpy { dest; src; n = None; }); @@ -452,8 +452,8 @@ let glibc_desc_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("strcasestr", unknown [drop "haystack" [r]; drop "needle" [r]]); ("inet_aton", unknown [drop "cp" [r]; drop "inp" [w]]); ("fopencookie", unknown [drop "cookie" []; drop "mode" [r]; drop "io_funcs" [s_deep]]); (* doesn't access cookie but passes it to io_funcs *) - ("mempcpy", special [__ "dest" [w]; __ "src" [r]; drop "n" []] @@ fun dest src -> Memcpy { dest; src }); - ("__builtin___mempcpy_chk", special [__ "dest" [w]; __ "src" [r]; drop "n" []; drop "os" []] @@ fun dest src -> Memcpy { dest; src }); + ("mempcpy", special [__ "dest" [w]; __ "src" [r]; __ "n" []] @@ fun dest src n -> Memcpy { dest; src; n; }); + ("__builtin___mempcpy_chk", special [__ "dest" [w]; __ "src" [r]; __ "n" []; drop "os" []] @@ fun dest src n -> Memcpy { dest; src; n; }); ] let linux_userspace_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ From 7f9ec9a4ef10ee1221b62cd7b99d867b668a0204 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 27 Sep 2023 16:03:49 +0300 Subject: [PATCH 0686/1312] Fix pthread-lock-return test on OSX --- tests/regression/04-mutex/58-pthread-lock-return.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/regression/04-mutex/58-pthread-lock-return.c b/tests/regression/04-mutex/58-pthread-lock-return.c index 0ae96b60ae..3e2a05c94e 100644 --- a/tests/regression/04-mutex/58-pthread-lock-return.c +++ b/tests/regression/04-mutex/58-pthread-lock-return.c @@ -70,7 +70,7 @@ void *t_fun(void *arg) { #ifndef __APPLE__ if (!pthread_spin_lock(&spin)) { - __goblint_check(1); // reachable + __goblint_check(1); // TODO reachable (TODO for OSX) g_spin++; // NORACE pthread_spin_unlock(&spin); } @@ -79,12 +79,12 @@ void *t_fun(void *arg) { } if (!pthread_spin_trylock(&spin)) { - __goblint_check(1); // reachable + __goblint_check(1); // TODO reachable (TODO for OSX) g_spin++; // NORACE pthread_spin_unlock(&spin); } else { - __goblint_check(1); // reachable + __goblint_check(1); // TODO reachable (TODO for OSX) } #endif From eb674b70ec61db2bfd8738959e219a9cf9aa2457 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 28 Sep 2023 10:42:07 +0200 Subject: [PATCH 0687/1312] Add MemOOB to `goblint_lib.ml` (PR #1094) --- src/goblint_lib.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/goblint_lib.ml b/src/goblint_lib.ml index 625e81f18b..6e700485dd 100644 --- a/src/goblint_lib.ml +++ b/src/goblint_lib.ml @@ -85,6 +85,7 @@ module MallocFresh = MallocFresh module Malloc_null = Malloc_null module MemLeak = MemLeak module UseAfterFree = UseAfterFree +module MemOutOfBounds = MemOutOfBounds (** {2 Concurrency} From dd2a70bf0d3bbeee9b53403b7fbd157ec8eafa8e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 27 Sep 2023 16:04:10 +0300 Subject: [PATCH 0688/1312] Add reachability timing --- src/analyses/base.ml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 45c4ee2e9b..c4fdd633d3 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -570,6 +570,8 @@ struct if M.tracing then M.traceu "reachability" "All reachable vars: %a\n" AD.pretty !visited; List.map AD.singleton (AD.elements !visited) + let reachable_vars ask args gs st = Timing.wrap "reachability" (reachable_vars ask args gs) st + let drop_non_ptrs (st:CPA.t) : CPA.t = if CPA.is_top st then st else let rec replace_val = function From 75f15808590ae9e595bc3857ac353cd0cf595a57 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 28 Sep 2023 11:54:22 +0200 Subject: [PATCH 0689/1312] Improve double free check --- src/analyses/useAfterFree.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index ba2c49f012..1b1e2f008d 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -216,7 +216,7 @@ struct List.mem arg read_shallow_args || List.mem arg read_deep_args || List.mem arg write_shallow_args || List.mem arg write_deep_args in Option.iter (fun x -> warn_lval_might_contain_freed ("special: " ^ f.vname) ctx x) lval; - List.iter (fun arg -> warn_exp_might_contain_freed ~is_implicitly_derefed:(is_arg_implicitly_derefed arg) ~is_double_free:(f.vname = "free") ("special: " ^ f.vname) ctx arg) arglist; + List.iter (fun arg -> warn_exp_might_contain_freed ~is_implicitly_derefed:(is_arg_implicitly_derefed arg) ~is_double_free:(match desc.special arglist with Free _ -> true | _ -> false) ("special: " ^ f.vname) ctx arg) arglist; match desc.special arglist with | Free ptr -> begin match ctx.ask (Queries.MayPointTo ptr) with From c19e4c097e474d55f3f03884e08171556feb8cab Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 28 Sep 2023 12:01:27 +0200 Subject: [PATCH 0690/1312] Fix blobsize query compare --- src/domains/queries.ml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/domains/queries.ml b/src/domains/queries.ml index eedbb3ac34..1a7ec2276d 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -337,7 +337,12 @@ struct | Any (EvalLength e1), Any (EvalLength e2) -> CilType.Exp.compare e1 e2 | Any (EvalMutexAttr e1), Any (EvalMutexAttr e2) -> CilType.Exp.compare e1 e2 | Any (EvalValue e1), Any (EvalValue e2) -> CilType.Exp.compare e1 e2 - | Any (BlobSize {exp = e1; _}), Any (BlobSize {exp = e2; _}) -> CilType.Exp.compare e1 e2 + | Any (BlobSize {exp = e1; base_address = b1}), Any (BlobSize {exp = e2; base_address = b2}) -> + let r = CilType.Exp.compare e1 e2 in + if r <> 0 then + r + else + Stdlib.compare b1 b2 | Any (CondVars e1), Any (CondVars e2) -> CilType.Exp.compare e1 e2 | Any (PartAccess p1), Any (PartAccess p2) -> compare_access p1 p2 | Any (IterPrevVars ip1), Any (IterPrevVars ip2) -> compare_iterprevvar ip1 ip2 From 7ebddbc89f20e16927eafb6abafa3e79abde2fe8 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 28 Sep 2023 13:48:47 +0200 Subject: [PATCH 0691/1312] Also pass to threadspawn for history TID + uniqueness --- src/analyses/abortUnless.ml | 2 +- src/analyses/accessAnalysis.ml | 2 +- src/analyses/apron/relationAnalysis.apron.ml | 2 +- src/analyses/base.ml | 2 +- src/analyses/condVars.ml | 2 +- src/analyses/expsplit.ml | 2 +- src/analyses/extractPthread.ml | 2 +- src/analyses/fileUse.ml | 2 +- src/analyses/mCP.ml | 4 +- src/analyses/mallocFresh.ml | 2 +- src/analyses/malloc_null.ml | 2 +- src/analyses/mutexTypeAnalysis.ml | 2 +- src/analyses/region.ml | 2 +- src/analyses/spec.ml | 2 +- src/analyses/symbLocks.ml | 2 +- src/analyses/taintPartialContexts.ml | 2 +- src/analyses/threadAnalysis.ml | 2 +- src/analyses/threadEscape.ml | 2 +- src/analyses/threadFlag.ml | 2 +- src/analyses/threadId.ml | 8 +-- src/analyses/threadJoins.ml | 2 +- src/analyses/tmpSpecial.ml | 2 +- src/analyses/tutorials/taint.ml | 2 +- src/analyses/tutorials/unitAnalysis.ml | 2 +- src/analyses/uninit.ml | 2 +- src/analyses/useAfterFree.ml | 2 +- src/analyses/varEq.ml | 2 +- src/cdomains/threadIdDomain.ml | 55 ++++++++++---------- src/framework/analyses.ml | 4 +- src/framework/constraints.ml | 30 +++++------ src/util/wideningTokens.ml | 2 +- src/witness/observerAnalysis.ml | 2 +- src/witness/witnessConstraints.ml | 4 +- 33 files changed, 80 insertions(+), 79 deletions(-) diff --git a/src/analyses/abortUnless.ml b/src/analyses/abortUnless.ml index 5c24e61f7c..ee4db69820 100644 --- a/src/analyses/abortUnless.ml +++ b/src/analyses/abortUnless.ml @@ -66,7 +66,7 @@ struct let startstate v = false let threadenter ctx ~multiple lval f args = [false] - let threadspawn ctx lval f args fctx = false + let threadspawn ctx ~multiple lval f args fctx = false let exitstate v = false end diff --git a/src/analyses/accessAnalysis.ml b/src/analyses/accessAnalysis.ml index f0025c2f1c..b181a1c70e 100644 --- a/src/analyses/accessAnalysis.ml +++ b/src/analyses/accessAnalysis.ml @@ -121,7 +121,7 @@ struct ctx.local - let threadspawn ctx lval f args fctx = + let threadspawn ctx ~multiple lval f args fctx = (* must explicitly access thread ID lval because special to pthread_create doesn't if singlethreaded before *) begin match lval with | None -> () diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index c232ccae9b..13f549fc44 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -665,7 +665,7 @@ struct (* TODO: do something like base? *) failwith "relation.threadenter: unknown function" - let threadspawn ctx lval f args fctx = + let threadspawn ctx ~multiple lval f args fctx = ctx.local let event ctx e octx = diff --git a/src/analyses/base.ml b/src/analyses/base.ml index e824fac013..01646a54cf 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2513,7 +2513,7 @@ struct let st = special_unknown_invalidate ctx (Analyses.ask_of_ctx ctx) ctx.global st f args in [st] - let threadspawn ctx (lval: lval option) (f: varinfo) (args: exp list) fctx: D.t = + let threadspawn ctx ~multiple (lval: lval option) (f: varinfo) (args: exp list) fctx: D.t = begin match lval with | Some lval -> begin match ThreadId.get_current (Analyses.ask_of_ctx fctx) with diff --git a/src/analyses/condVars.ml b/src/analyses/condVars.ml index 820ff69efa..3b23dc03fc 100644 --- a/src/analyses/condVars.ml +++ b/src/analyses/condVars.ml @@ -156,7 +156,7 @@ struct let startstate v = D.bot () let threadenter ctx ~multiple lval f args = [D.bot ()] - let threadspawn ctx lval f args fctx = ctx.local + let threadspawn ctx ~multiple lval f args fctx = ctx.local let exitstate v = D.bot () end diff --git a/src/analyses/expsplit.ml b/src/analyses/expsplit.ml index 141a04283b..fef3d9ff9f 100644 --- a/src/analyses/expsplit.ml +++ b/src/analyses/expsplit.ml @@ -86,7 +86,7 @@ struct let threadenter ctx ~multiple lval f args = [ctx.local] - let threadspawn ctx lval f args fctx = + let threadspawn ctx ~multiple lval f args fctx = emit_splits_ctx ctx let event ctx (event: Events.t) octx = diff --git a/src/analyses/extractPthread.ml b/src/analyses/extractPthread.ml index f72f72c1fe..f084a21edb 100644 --- a/src/analyses/extractPthread.ml +++ b/src/analyses/extractPthread.ml @@ -1254,7 +1254,7 @@ module Spec : Analyses.MCPSpec = struct [ { f_d with pred = d.pred } ] - let threadspawn ctx lval f args fctx = ctx.local + let threadspawn ctx ~multiple lval f args fctx = ctx.local let exitstate v = D.top () diff --git a/src/analyses/fileUse.ml b/src/analyses/fileUse.ml index b8e7fd78f5..58257b7843 100644 --- a/src/analyses/fileUse.ml +++ b/src/analyses/fileUse.ml @@ -288,7 +288,7 @@ struct let startstate v = D.bot () let threadenter ctx ~multiple lval f args = [D.bot ()] - let threadspawn ctx lval f args fctx = ctx.local + let threadspawn ctx ~multiple lval f args fctx = ctx.local let exitstate v = D.bot () end diff --git a/src/analyses/mCP.ml b/src/analyses/mCP.ml index 5259bdb6c7..7a7e787ad7 100644 --- a/src/analyses/mCP.ml +++ b/src/analyses/mCP.ml @@ -578,7 +578,7 @@ struct (* TODO: this do_emits is now different from everything else *) map (fun d -> do_emits ctx !emits d false) @@ map topo_sort_an @@ n_cartesian_product css - let threadspawn (ctx:(D.t, G.t, C.t, V.t) ctx) lval f a fctx = + let threadspawn (ctx:(D.t, G.t, C.t, V.t) ctx) ~multiple lval f a fctx = let sides = ref [] in let emits = ref [] in let ctx'' = outer_ctx "threadspawn" ~sides ~emits ctx in @@ -586,7 +586,7 @@ struct let f post_all (n,(module S:MCPSpec),(d,fd)) = let ctx' : (S.D.t, S.G.t, S.C.t, S.V.t) ctx = inner_ctx "threadspawn" ~post_all ctx'' n d in let fctx' : (S.D.t, S.G.t, S.C.t, S.V.t) ctx = inner_ctx "threadspawn" ~post_all fctx'' n fd in - n, repr @@ S.threadspawn ctx' lval f a fctx' + n, repr @@ S.threadspawn ~multiple ctx' lval f a fctx' in let d, q = map_deadcode f @@ spec_list2 ctx.local fctx.local in do_sideg ctx !sides; diff --git a/src/analyses/mallocFresh.ml b/src/analyses/mallocFresh.ml index e171ad4ea1..b45573a801 100644 --- a/src/analyses/mallocFresh.ml +++ b/src/analyses/mallocFresh.ml @@ -55,7 +55,7 @@ struct let threadenter ctx ~multiple lval f args = [D.empty ()] - let threadspawn ctx lval f args fctx = + let threadspawn ctx ~multiple lval f args fctx = D.empty () module A = diff --git a/src/analyses/malloc_null.ml b/src/analyses/malloc_null.ml index 41c251dfce..f993db0c6e 100644 --- a/src/analyses/malloc_null.ml +++ b/src/analyses/malloc_null.ml @@ -216,7 +216,7 @@ struct let startstate v = D.empty () let threadenter ctx ~multiple lval f args = [D.empty ()] - let threadspawn ctx lval f args fctx = ctx.local + let threadspawn ctx ~multiple lval f args fctx = ctx.local let exitstate v = D.empty () let init marshal = diff --git a/src/analyses/mutexTypeAnalysis.ml b/src/analyses/mutexTypeAnalysis.ml index 66e60aede1..e640a261cd 100644 --- a/src/analyses/mutexTypeAnalysis.ml +++ b/src/analyses/mutexTypeAnalysis.ml @@ -66,7 +66,7 @@ struct let startstate v = D.bot () let threadenter ctx ~multiple lval f args = [D.top ()] - let threadspawn ctx lval f args fctx = ctx.local + let threadspawn ctx ~multiple lval f args fctx = ctx.local let exitstate v = D.top () let query ctx (type a) (q: a Queries.t): a Queries.result = diff --git a/src/analyses/region.ml b/src/analyses/region.ml index 86cad5684b..652526543c 100644 --- a/src/analyses/region.ml +++ b/src/analyses/region.ml @@ -177,7 +177,7 @@ struct let threadenter ctx ~multiple lval f args = [`Lifted (RegMap.bot ())] - let threadspawn ctx lval f args fctx = ctx.local + let threadspawn ctx ~multiple lval f args fctx = ctx.local let exitstate v = `Lifted (RegMap.bot ()) diff --git a/src/analyses/spec.ml b/src/analyses/spec.ml index c44edd6c87..2f754f6160 100644 --- a/src/analyses/spec.ml +++ b/src/analyses/spec.ml @@ -488,7 +488,7 @@ struct let startstate v = D.bot () let threadenter ctx ~multiple lval f args = [D.bot ()] - let threadspawn ctx lval f args fctx = ctx.local + let threadspawn ctx ~multiple lval f args fctx = ctx.local let exitstate v = D.bot () end diff --git a/src/analyses/symbLocks.ml b/src/analyses/symbLocks.ml index 32be32f73d..f6fdd96c2e 100644 --- a/src/analyses/symbLocks.ml +++ b/src/analyses/symbLocks.ml @@ -30,7 +30,7 @@ struct let startstate v = D.top () let threadenter ctx ~multiple lval f args = [D.top ()] - let threadspawn ctx lval f args fctx = ctx.local + let threadspawn ctx ~multiple lval f args fctx = ctx.local let exitstate v = D.top () let branch ctx exp tv = ctx.local diff --git a/src/analyses/taintPartialContexts.ml b/src/analyses/taintPartialContexts.ml index b45ea54877..88cf532ab2 100644 --- a/src/analyses/taintPartialContexts.ml +++ b/src/analyses/taintPartialContexts.ml @@ -103,7 +103,7 @@ struct let startstate v = D.bot () let threadenter ctx ~multiple lval f args = [D.bot ()] - let threadspawn ctx lval f args fctx = + let threadspawn ctx ~multiple lval f args fctx = match lval with | Some lv -> taint_lval ctx lv | None -> ctx.local diff --git a/src/analyses/threadAnalysis.ml b/src/analyses/threadAnalysis.ml index ff4b4d5c63..f51e9395db 100644 --- a/src/analyses/threadAnalysis.ml +++ b/src/analyses/threadAnalysis.ml @@ -91,7 +91,7 @@ struct ctx.sideg tid (true, TS.bot (), false)); [D.bot ()] - let threadspawn ctx lval f args fctx = + let threadspawn ctx ~multiple lval f args fctx = let creator = ThreadId.get_current (Analyses.ask_of_ctx ctx) in let tid = ThreadId.get_current_unlift (Analyses.ask_of_ctx fctx) in let repeated = D.mem tid ctx.local in diff --git a/src/analyses/threadEscape.ml b/src/analyses/threadEscape.ml index 0948a3976d..21a8b69c93 100644 --- a/src/analyses/threadEscape.ml +++ b/src/analyses/threadEscape.ml @@ -153,7 +153,7 @@ struct let threadenter ctx ~multiple lval f args = [D.bot ()] - let threadspawn ctx lval f args fctx = + let threadspawn ctx ~multiple lval f args fctx = D.join ctx.local @@ match args with | [ptc_arg] -> diff --git a/src/analyses/threadFlag.ml b/src/analyses/threadFlag.ml index 81e05af679..6bd466caef 100644 --- a/src/analyses/threadFlag.ml +++ b/src/analyses/threadFlag.ml @@ -63,7 +63,7 @@ struct ctx.emit Events.EnterMultiThreaded; [create_tid f] - let threadspawn ctx lval f args fctx = + let threadspawn ctx ~multiple lval f args fctx = if not (has_ever_been_multi (Analyses.ask_of_ctx ctx)) then ctx.emit Events.EnterMultiThreaded; D.join ctx.local (Flag.get_main ()) diff --git a/src/analyses/threadId.ml b/src/analyses/threadId.ml index a9f3fa35f7..900870a676 100644 --- a/src/analyses/threadId.ml +++ b/src/analyses/threadId.ml @@ -58,8 +58,8 @@ struct let create_tid ?(multiple=false) (_, current, (td, _)) ((node, index): Node.t * int option) v = match current with - | `Lifted current when not multiple -> - let+ tid = Thread.threadenter (current, td) node index v in + | `Lifted current -> + let+ tid = Thread.threadenter ~multiple (current, td) node index v in if GobConfig.get_bool "dbg.print_tids" then Hashtbl.replace !tids tid (); `Lifted tid @@ -138,10 +138,10 @@ struct let+ tid = create_tid ~multiple ctx.local (n, i) f in (`Lifted (f, n, i), tid, (TD.bot (), TD.bot ())) - let threadspawn ctx lval f args fctx = + let threadspawn ctx ~multiple lval f args fctx = let (current_n, current, (td,tdl)) = ctx.local in let v, n, i = match fctx.local with `Lifted vni, _, _ -> vni | _ -> failwith "ThreadId.threadspawn" in - (current_n, current, (Thread.threadspawn td n i v, Thread.threadspawn tdl n i v)) + (current_n, current, (Thread.threadspawn ~multiple td n i v, Thread.threadspawn ~multiple tdl n i v)) type marshal = (Thread.t,unit) Hashtbl.t (* TODO: don't use polymorphic Hashtbl *) let init (m:marshal option): unit = diff --git a/src/analyses/threadJoins.ml b/src/analyses/threadJoins.ml index f2cd36619f..862711073c 100644 --- a/src/analyses/threadJoins.ml +++ b/src/analyses/threadJoins.ml @@ -81,7 +81,7 @@ struct ) | _, _ -> ctx.local - let threadspawn ctx lval f args fctx = + let threadspawn ctx ~multiple lval f args fctx = if D.is_bot ctx.local then ( (* bot is All threads *) M.info ~category:Imprecise "Thread created while ALL threads must-joined, continuing with no threads joined."; D.top () (* top is no threads *) diff --git a/src/analyses/tmpSpecial.ml b/src/analyses/tmpSpecial.ml index 046345e627..9ed6da7c60 100644 --- a/src/analyses/tmpSpecial.ml +++ b/src/analyses/tmpSpecial.ml @@ -89,7 +89,7 @@ struct let startstate v = D.bot () let threadenter ctx ~multiple lval f args = [D.bot ()] - let threadspawn ctx lval f args fctx = ctx.local + let threadspawn ctx ~multiple lval f args fctx = ctx.local let exitstate v = D.bot () end diff --git a/src/analyses/tutorials/taint.ml b/src/analyses/tutorials/taint.ml index 7fc3fd7343..a978d0faf4 100644 --- a/src/analyses/tutorials/taint.ml +++ b/src/analyses/tutorials/taint.ml @@ -130,7 +130,7 @@ struct (* You may leave these alone *) let startstate v = D.bot () let threadenter ctx ~multiple lval f args = [D.top ()] - let threadspawn ctx lval f args fctx = ctx.local + let threadspawn ctx ~multiple lval f args fctx = ctx.local let exitstate v = D.top () end diff --git a/src/analyses/tutorials/unitAnalysis.ml b/src/analyses/tutorials/unitAnalysis.ml index 3ecddc2bc0..dc377cdd97 100644 --- a/src/analyses/tutorials/unitAnalysis.ml +++ b/src/analyses/tutorials/unitAnalysis.ml @@ -40,7 +40,7 @@ struct let startstate v = D.bot () let threadenter ctx ~multiple lval f args = [D.top ()] - let threadspawn ctx lval f args fctx = ctx.local + let threadspawn ctx ~multiple lval f args fctx = ctx.local let exitstate v = D.top () end diff --git a/src/analyses/uninit.ml b/src/analyses/uninit.ml index 2b388d1190..8693599a4d 100644 --- a/src/analyses/uninit.ml +++ b/src/analyses/uninit.ml @@ -26,7 +26,7 @@ struct let startstate v : D.t = D.empty () let threadenter ctx ~multiple lval f args = [D.empty ()] - let threadspawn ctx lval f args fctx = ctx.local + let threadspawn ctx ~multiple lval f args fctx = ctx.local let exitstate v : D.t = D.empty () let access_address (ask: Queries.ask) write lv = diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index 0c7a46c35f..683ddbdcc2 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -197,7 +197,7 @@ struct | _ -> state let threadenter ctx ~multiple lval f args = [ctx.local] - let threadspawn ctx lval f args fctx = ctx.local + let threadspawn ctx ~multiple lval f args fctx = ctx.local let startstate v = D.bot () let exitstate v = D.top () diff --git a/src/analyses/varEq.ml b/src/analyses/varEq.ml index 3aaef95265..3f5a65516f 100644 --- a/src/analyses/varEq.ml +++ b/src/analyses/varEq.ml @@ -44,7 +44,7 @@ struct let startstate v = D.top () let threadenter ctx ~multiple lval f args = [D.top ()] - let threadspawn ctx lval f args fctx = ctx.local + let threadspawn ctx ~multiple lval f args fctx = ctx.local let exitstate v = D.top () let typ_equal = CilType.Typ.equal (* TODO: Used to have equality checking, which ignores attributes. Is that needed? *) diff --git a/src/cdomains/threadIdDomain.ml b/src/cdomains/threadIdDomain.ml index 7193552048..a9646cffd2 100644 --- a/src/cdomains/threadIdDomain.ml +++ b/src/cdomains/threadIdDomain.ml @@ -23,7 +23,7 @@ module type Stateless = sig include S - val threadenter: Node.t -> int option -> varinfo -> t + val threadenter: multiple:bool -> Node.t -> int option -> varinfo -> t end module type Stateful = @@ -32,8 +32,8 @@ sig module D: Lattice.S - val threadenter: t * D.t -> Node.t -> int option -> varinfo -> t list - val threadspawn: D.t -> Node.t -> int option -> varinfo -> D.t + val threadenter: multiple:bool -> t * D.t -> Node.t -> int option -> varinfo -> t list + val threadspawn: multiple:bool -> D.t -> Node.t -> int option -> varinfo -> D.t (** If it is possible to get a list of threads created thus far, get it *) val created: t -> D.t -> (t list) option @@ -71,9 +71,10 @@ struct let threadinit v ~multiple: t = (v, None) - let threadenter l i v: t = + let threadenter ~multiple l i v: t = if GobConfig.get_bool "ana.thread.include-node" then - (v, Some (l, i)) + let counter = Option.map (fun x -> if multiple then WrapperFunctionAnalysis0.ThreadCreateUniqueCount.top () else x) i in + (v, Some (l, counter)) else (v, None) @@ -93,8 +94,8 @@ struct module D = Lattice.Unit - let threadenter _ n i v = [threadenter n i v] - let threadspawn () _ _ _ = () + let threadenter ~multiple _ n i v = [threadenter ~multiple n i v] + let threadspawn ~multiple () _ _ _ = () let created _ _ = None end @@ -162,10 +163,10 @@ struct else ([base_tid], S.empty ()) - let threadenter ((p, _ ) as current, (cs,_)) (n: Node.t) i v = - let ni = Base.threadenter n i v in + let threadenter ~multiple ((p, _ ) as current, (cs,_)) (n: Node.t) i v = + let ni = Base.threadenter ~multiple n i v in let ((p', s') as composed) = compose current ni in - if is_unique composed && S.mem ni cs then + if is_unique composed && (S.mem ni cs || multiple) then [(p, S.singleton ni); composed] (* also respawn unique version of the thread to keep it reachable while thread ID sets refer to it *) else [composed] @@ -182,12 +183,12 @@ struct in Some (List.concat_map map_one els) - let threadspawn (cs,cms) l i v = - let e = Base.threadenter l i v in + let threadspawn ~multiple (cs,cms) l i v = + let e = Base.threadenter ~multiple l i v in if S.mem e cs then (cs, S.add e cms) else - (S.add e cs, cms) + (S.add e cs, if multiple then S.add e cms else cms) let is_main = function | ([fl], s) when S.is_empty s && Base.is_main fl -> true @@ -257,24 +258,24 @@ struct | (None, Some x'), `Top -> liftp x' (P.D.top ()) | _ -> None - let threadenter x n i v = + let threadenter ~multiple x n i v = match x with - | ((Some x', None), `Lifted1 d) -> H.threadenter (x',d) n i v |> List.map (fun t -> (Some t, None)) - | ((Some x', None), `Bot) -> H.threadenter (x',H.D.bot ()) n i v |> List.map (fun t -> (Some t, None)) - | ((Some x', None), `Top) -> H.threadenter (x',H.D.top ()) n i v |> List.map (fun t -> (Some t, None)) - | ((None, Some x'), `Lifted2 d) -> P.threadenter (x',d) n i v |> List.map (fun t -> (None, Some t)) - | ((None, Some x'), `Bot) -> P.threadenter (x',P.D.bot ()) n i v |> List.map (fun t -> (None, Some t)) - | ((None, Some x'), `Top) -> P.threadenter (x',P.D.top ()) n i v |> List.map (fun t -> (None, Some t)) + | ((Some x', None), `Lifted1 d) -> H.threadenter ~multiple (x',d) n i v |> List.map (fun t -> (Some t, None)) + | ((Some x', None), `Bot) -> H.threadenter ~multiple (x',H.D.bot ()) n i v |> List.map (fun t -> (Some t, None)) + | ((Some x', None), `Top) -> H.threadenter ~multiple (x',H.D.top ()) n i v |> List.map (fun t -> (Some t, None)) + | ((None, Some x'), `Lifted2 d) -> P.threadenter ~multiple (x',d) n i v |> List.map (fun t -> (None, Some t)) + | ((None, Some x'), `Bot) -> P.threadenter ~multiple (x',P.D.bot ()) n i v |> List.map (fun t -> (None, Some t)) + | ((None, Some x'), `Top) -> P.threadenter ~multiple (x',P.D.top ()) n i v |> List.map (fun t -> (None, Some t)) | _ -> failwith "FlagConfiguredTID received a value where not exactly one component is set" - let threadspawn x n i v = + let threadspawn ~multiple x n i v = match x with - | `Lifted1 x' -> `Lifted1 (H.threadspawn x' n i v) - | `Lifted2 x' -> `Lifted2 (P.threadspawn x' n i v) - | `Bot when history_enabled () -> `Lifted1 (H.threadspawn (H.D.bot ()) n i v) - | `Bot -> `Lifted2 (P.threadspawn (P.D.bot ()) n i v) - | `Top when history_enabled () -> `Lifted1 (H.threadspawn (H.D.top ()) n i v) - | `Top -> `Lifted2 (P.threadspawn (P.D.top ()) n i v) + | `Lifted1 x' -> `Lifted1 (H.threadspawn ~multiple x' n i v) + | `Lifted2 x' -> `Lifted2 (P.threadspawn ~multiple x' n i v) + | `Bot when history_enabled () -> `Lifted1 (H.threadspawn ~multiple (H.D.bot ()) n i v) + | `Bot -> `Lifted2 (P.threadspawn ~multiple (P.D.bot ()) n i v) + | `Top when history_enabled () -> `Lifted1 (H.threadspawn ~multiple (H.D.top ()) n i v) + | `Top -> `Lifted2 (P.threadspawn ~multiple (P.D.top ()) n i v) let name () = "FlagConfiguredTID: " ^ if history_enabled () then H.name () else P.name () end diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 3bb88a6ead..e1a4560003 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -447,7 +447,7 @@ sig val threadenter : (D.t, G.t, C.t, V.t) ctx -> multiple:bool -> lval option -> varinfo -> exp list -> D.t list (** Updates the local state of the creator thread using initial state of created thread. *) - val threadspawn : (D.t, G.t, C.t, V.t) ctx -> lval option -> varinfo -> exp list -> (D.t, G.t, C.t, V.t) ctx -> D.t + val threadspawn : (D.t, G.t, C.t, V.t) ctx -> multiple:bool -> lval option -> varinfo -> exp list -> (D.t, G.t, C.t, V.t) ctx -> D.t val event : (D.t, G.t, C.t, V.t) ctx -> Events.t -> (D.t, G.t, C.t, V.t) ctx -> D.t end @@ -697,7 +697,7 @@ struct ctx.local let threadenter ctx ~multiple lval f args = [ctx.local] - let threadspawn ctx lval f args fctx = ctx.local + let threadspawn ctx ~multiple lval f args fctx = ctx.local end diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index ed492f4237..f474df6834 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -86,8 +86,8 @@ struct let threadenter ctx ~multiple lval f args = List.map D.lift @@ S.threadenter (conv ctx) ~multiple lval f args - let threadspawn ctx lval f args fctx = - D.lift @@ S.threadspawn (conv ctx) lval f args (conv fctx) + let threadspawn ctx ~multiple lval f args fctx = + D.lift @@ S.threadspawn (conv ctx) ~multiple lval f args (conv fctx) let paths_as_set ctx = List.map (fun x -> D.lift x) @@ S.paths_as_set (conv ctx) @@ -170,8 +170,8 @@ struct let threadenter ctx ~multiple lval f args = S.threadenter (conv ctx) ~multiple lval f args - let threadspawn ctx lval f args fctx = - S.threadspawn (conv ctx) lval f args (conv fctx) + let threadspawn ctx ~multiple lval f args fctx = + S.threadspawn (conv ctx) ~multiple lval f args (conv fctx) let paths_as_set ctx = S.paths_as_set (conv ctx) let event ctx e octx = S.event (conv ctx) e (conv octx) @@ -250,7 +250,7 @@ struct let combine_assign' ctx r fe f args fc es f_ask = lift_fun ctx (lift ctx) S.combine_assign (fun p -> p r fe f args fc (fst es) f_ask) let threadenter ctx ~multiple lval f args = lift_fun ctx (List.map lift_start_level) (S.threadenter ~multiple) ((|>) args % (|>) f % (|>) lval) - let threadspawn ctx lval f args fctx = lift_fun ctx (lift ctx) S.threadspawn ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval) + let threadspawn ctx ~multiple lval f args fctx = lift_fun ctx (lift ctx) (S.threadspawn ~multiple) ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval) let leq0 = function | `Top -> false @@ -395,7 +395,7 @@ struct let event ctx e octx = lift_fun ctx S.event ((|>) (conv octx) % (|>) e) let threadenter ctx ~multiple lval f args = S.threadenter (conv ctx) ~multiple lval f args |> List.map (fun d -> (d, snd ctx.local)) - let threadspawn ctx lval f args fctx = lift_fun ctx S.threadspawn ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval) + let threadspawn ctx ~multiple lval f args fctx = lift_fun ctx (S.threadspawn ~multiple) ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval) let enter ctx r f args = let m = snd ctx.local in @@ -486,7 +486,7 @@ struct let combine_assign ctx r fe f args fc es f_ask = lift_fun ctx D.lift S.combine_assign (fun p -> p r fe f args fc (D.unlift es) f_ask) `Bot let threadenter ctx ~multiple lval f args = lift_fun ctx (List.map D.lift) (S.threadenter ~multiple) ((|>) args % (|>) f % (|>) lval) [] - let threadspawn ctx lval f args fctx = lift_fun ctx D.lift S.threadspawn ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval) `Bot + let threadspawn ctx ~multiple lval f args fctx = lift_fun ctx D.lift (S.threadspawn ~multiple) ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval) `Bot let event (ctx:(D.t,G.t,C.t,V.t) ctx) (e:Events.t) (octx:(D.t,G.t,C.t,V.t) ctx):D.t = lift_fun ctx D.lift S.event ((|>) (conv octx) % (|>) e) `Bot end @@ -563,7 +563,7 @@ struct if !AnalysisState.postsolving then sideg (GVar.contexts f) (G.create_contexts (G.CSet.singleton c)) - let common_ctx var edge prev_node pval (getl:lv -> ld) sidel getg sideg : (D.t, S.G.t, S.C.t, S.V.t) ctx * D.t list ref * (lval option * varinfo * exp list * D.t) list ref = + let common_ctx var edge prev_node pval (getl:lv -> ld) sidel getg sideg : (D.t, S.G.t, S.C.t, S.V.t) ctx * D.t list ref * (lval option * varinfo * exp list * D.t * bool) list ref = let r = ref [] in let spawns = ref [] in (* now watch this ... *) @@ -586,7 +586,7 @@ struct (* TODO: don't repeat for all paths that spawn same *) let ds = S.threadenter ~multiple ctx lval f args in List.iter (fun d -> - spawns := (lval, f, args, d) :: !spawns; + spawns := (lval, f, args, d, multiple) :: !spawns; match Cilfacade.find_varinfo_fundec f with | fd -> let c = S.context fd d in @@ -618,14 +618,14 @@ struct } in (* TODO: don't forget path dependencies *) - let one_spawn (lval, f, args, fd) = + let one_spawn (lval, f, args, fd, multiple) = let rec fctx = { ctx with ask = (fun (type a) (q: a Queries.t) -> S.query fctx q) ; local = fd } in - S.threadspawn ctx' lval f args fctx + S.threadspawn ctx' ~multiple lval f args fctx in bigsqcup (List.map one_spawn spawns) @@ -1266,9 +1266,9 @@ struct let g xs ys = (List.map (fun y -> D.singleton y) ys) @ xs in fold' ctx (Spec.threadenter ~multiple) (fun h -> h lval f args) g [] - let threadspawn ctx lval f args fctx = + let threadspawn ctx ~multiple lval f args fctx = let fd1 = D.choose fctx.local in - map ctx Spec.threadspawn (fun h -> h lval f args (conv fctx fd1)) + map ctx (Spec.threadspawn ~multiple) (fun h -> h lval f args (conv fctx fd1)) let sync ctx reason = map ctx Spec.sync (fun h -> h reason) @@ -1450,7 +1450,7 @@ struct let combine_assign ctx = S.combine_assign (conv ctx) let special ctx = S.special (conv ctx) let threadenter ctx = S.threadenter (conv ctx) - let threadspawn ctx lv f args fctx = S.threadspawn (conv ctx) lv f args (conv fctx) + let threadspawn ctx ~multiple lv f args fctx = S.threadspawn (conv ctx) ~multiple lv f args (conv fctx) let sync ctx = S.sync (conv ctx) let skip ctx = S.skip (conv ctx) let asm ctx = S.asm (conv ctx) @@ -1686,7 +1686,7 @@ struct S.D.bot () | _ -> S.special conv_ctx lv f args let threadenter ctx = S.threadenter (conv ctx) - let threadspawn ctx lv f args fctx = S.threadspawn (conv ctx) lv f args (conv fctx) + let threadspawn ctx ~multiple lv f args fctx = S.threadspawn (conv ctx) ~multiple lv f args (conv fctx) let sync ctx = S.sync (conv ctx) let skip ctx = S.skip (conv ctx) let asm ctx = S.asm (conv ctx) diff --git a/src/util/wideningTokens.ml b/src/util/wideningTokens.ml index 73c160e3bb..1816de90c7 100644 --- a/src/util/wideningTokens.ml +++ b/src/util/wideningTokens.ml @@ -180,6 +180,6 @@ struct let combine_assign ctx r fe f args fc es f_ask = lift_fun ctx lift' S.combine_assign (fun p -> p r fe f args fc (D.unlift es) f_ask) (* TODO: use tokens from es *) let threadenter ctx ~multiple lval f args = lift_fun ctx (fun l ts -> List.map (Fun.flip lift' ts) l) (S.threadenter ~multiple) ((|>) args % (|>) f % (|>) lval ) - let threadspawn ctx lval f args fctx = lift_fun ctx lift' S.threadspawn ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval) + let threadspawn ctx ~multiple lval f args fctx = lift_fun ctx lift' (S.threadspawn ~multiple) ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval) let event ctx e octx = lift_fun ctx lift' S.event ((|>) (conv octx) % (|>) e) end diff --git a/src/witness/observerAnalysis.ml b/src/witness/observerAnalysis.ml index 45a547c471..e8daf56155 100644 --- a/src/witness/observerAnalysis.ml +++ b/src/witness/observerAnalysis.ml @@ -77,7 +77,7 @@ struct let startstate v = `Lifted Automaton.initial let threadenter ctx ~multiple lval f args = [D.top ()] - let threadspawn ctx lval f args fctx = ctx.local + let threadspawn ctx ~multiple lval f args fctx = ctx.local let exitstate v = D.top () end diff --git a/src/witness/witnessConstraints.ml b/src/witness/witnessConstraints.ml index 1bf6294020..8dedf77a79 100644 --- a/src/witness/witnessConstraints.ml +++ b/src/witness/witnessConstraints.ml @@ -209,9 +209,9 @@ struct ys' @ xs in fold' ctx (Spec.threadenter ~multiple) (fun h -> h lval f args) g [] - let threadspawn ctx lval f args fctx = + let threadspawn ctx ~multiple lval f args fctx = let fd1 = Dom.choose_key (fst fctx.local) in - map ctx Spec.threadspawn (fun h -> h lval f args (conv fctx fd1)) + map ctx (Spec.threadspawn ~multiple) (fun h -> h lval f args (conv fctx fd1)) let sync ctx reason = fold'' ctx Spec.sync (fun h -> h reason) (fun (a, async) x r a' -> From 44ef0843efac4839f45f267f015ace6a27a88414 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 28 Sep 2023 13:54:36 +0200 Subject: [PATCH 0692/1312] Add example for race despite uniqueness counter --- .../40-threadid/11-multiple-unique-counter.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 tests/regression/40-threadid/11-multiple-unique-counter.c diff --git a/tests/regression/40-threadid/11-multiple-unique-counter.c b/tests/regression/40-threadid/11-multiple-unique-counter.c new file mode 100644 index 0000000000..37c08ae61a --- /dev/null +++ b/tests/regression/40-threadid/11-multiple-unique-counter.c @@ -0,0 +1,16 @@ +// PARAM: --set ana.thread.unique_thread_id_count 4 +#include +#include + +int myglobal; + +void *t_fun(void *arg) { + myglobal=40; //RACE + return NULL; +} + +int main(void) { + // This should spawn a non-unique thread + unknown(t_fun); + return 0; +} From b718e46241c0c4f9b926620060a8743c3c49b6d7 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 28 Sep 2023 14:04:49 +0200 Subject: [PATCH 0693/1312] Add check that only one mainfun is specified to privatizations --- src/analyses/basePriv.ml | 2 +- src/analyses/commonPriv.ml | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index 0154924a1c..3843dda300 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -893,7 +893,7 @@ end module MinePrivBase = struct include NoFinalize - include ConfCheck.RequireMutexPathSensInit + include ConfCheck.RequireMutexPathSensOneMainInit include MutexGlobals (* explicit not needed here because G is Prod anyway? *) let thread_join ?(force=false) ask get e st = st diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index 1b92cb320d..38a8dfe1b7 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -19,12 +19,14 @@ struct if not mutex_active then failwith "Privatization (to be useful) requires the 'mutex' analysis to be enabled (it is currently disabled)" end - module RequireMutexPathSensInit = + module RequireMutexPathSensOneMainInit = struct let init () = RequireMutexActivatedInit.init (); let mutex_path_sens = List.mem "mutex" (GobConfig.get_string_list "ana.path_sens") in if not mutex_path_sens then failwith "The activated privatization requires the 'mutex' analysis to be enabled & path sensitive (it is currently enabled, but not path sensitive)"; + let mainfuns = List.length @@ GobConfig.get_list "mainfun" in + if not (mainfuns = 1) then failwith "The activated privatization requires exactly one main function to be specified"; () end From e780a83125a973e7b4df4d17e372a5a692e4bd37 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 28 Sep 2023 15:41:25 +0300 Subject: [PATCH 0694/1312] Print final messages deterministically --- src/util/messages.ml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/util/messages.ml b/src/util/messages.ml index 6cd9027739..9cc08b6faf 100644 --- a/src/util/messages.ml +++ b/src/util/messages.ml @@ -259,10 +259,13 @@ let finalize () = |> List.sort Message.compare |> List.iter print ); - Table.MH.iter (fun m () -> + Table.MH.to_seq_keys final_table + |> List.of_seq + |> List.sort Message.compare + |> List.iter (fun m -> print m; Table.add m - ) final_table (* TODO: also deterministic case *) + ) let current_context: ControlSpecC.t option ref = ref None From 3c7555bedfdb4fd041e1a5a6d5cf83c8b2c5247a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 28 Sep 2023 16:41:42 +0300 Subject: [PATCH 0695/1312] Remove context from noloc and final messages --- src/util/messages.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/messages.ml b/src/util/messages.ml index 9cc08b6faf..42a3118978 100644 --- a/src/util/messages.ml +++ b/src/util/messages.ml @@ -294,7 +294,7 @@ let msg_noloc severity ?(tags=[]) ?(category=Category.Unknown) fmt = if !AnalysisState.should_warn && Severity.should_warn severity && (Category.should_warn category || Tags.should_warn tags) then ( let finish doc = let text = GobPretty.show doc in - add {tags = Category category :: tags; severity; multipiece = Single {loc = None; text; context = msg_context ()}} (* TODO: why context? *) + add {tags = Category category :: tags; severity; multipiece = Single {loc = None; text; context = None}} in Pretty.gprintf finish fmt ) @@ -332,7 +332,7 @@ let msg_final severity ?(tags=[]) ?(category=Category.Unknown) fmt = if !AnalysisState.should_warn then ( let finish doc = let text = GobPretty.show doc in - add_final {tags = Category category :: tags; severity; multipiece = Single {loc = None; text; context = msg_context ()}} (* TODO: why context? *) + add_final {tags = Category category :: tags; severity; multipiece = Single {loc = None; text; context = None}} in Pretty.gprintf finish fmt ) From 55a8f1e34a81c899bf5cff57a101139c2c240830 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 28 Sep 2023 15:41:58 +0200 Subject: [PATCH 0696/1312] Remove __builtin_alloca from invalidate_actions --- src/analyses/libraryFunctions.ml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 800d3e4902..e4f42cfaf2 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -1038,7 +1038,6 @@ let invalidate_actions = [ "sigaddset", writesAll;(*unsafe*) "raise", writesAll;(*unsafe*) "_strlen", readsAll;(*safe*) - "__builtin_alloca", readsAll;(*safe*) "dlopen", readsAll;(*safe*) "dlsym", readsAll;(*safe*) "dlclose", readsAll;(*safe*) From 21584eec15a258f47f5f9cb47b5daeef13ba0865 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 09:43:40 +0200 Subject: [PATCH 0697/1312] Extend UAF analysis to support alloca() --- src/analyses/useAfterFree.ml | 41 +++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index 8c70322553..c313c0a90b 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -4,7 +4,12 @@ open GoblintCil open Analyses open MessageCategory -module ToppedVarInfoSet = SetDomain.ToppedSet(CilType.Varinfo)(struct let topname = "All Heap Variables" end) +module AllocaVars = SetDomain.ToppedSet(CilType.Varinfo)(struct let topname = "All alloca() Variables" end) +module HeapVars = SetDomain.ToppedSet(CilType.Varinfo)(struct let topname = "All Heap Variables" end) + +(* Heap vars created by alloca() and deallocated at function exit * Heap vars deallocated by free() *) +module StackAndHeapVars = Lattice.Prod(AllocaVars)(HeapVars) + module ThreadIdSet = SetDomain.Make(ThreadIdDomain.ThreadLifted) module Spec : Analyses.MCPSpec = @@ -13,7 +18,7 @@ struct let name () = "useAfterFree" - module D = ToppedVarInfoSet + module D = StackAndHeapVars module C = Lattice.Unit module G = ThreadIdSet module V = VarinfoV @@ -57,7 +62,7 @@ struct let any_equal_current threads = ThreadIdSet.exists (equal_current current) threads in if not current_is_unique && any_equal_current freeing_threads then M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Current thread is not unique and a Use-After-Free might occur for heap variable %a" CilType.Varinfo.pretty heap_var - else if D.mem heap_var ctx.local then + else if HeapVars.mem heap_var (snd ctx.local) then M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Use-After-Free might occur in current unique thread %a for heap variable %a" ThreadIdDomain.FlagConfiguredTID.pretty current CilType.Varinfo.pretty heap_var end | `Top -> @@ -85,7 +90,7 @@ struct match ctx.ask (Queries.MayPointTo lval_to_query) with | ad when not (Queries.AD.is_top ad) -> let warn_for_heap_var v = - if D.mem v state then + if HeapVars.mem v (snd state) then M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "lval (%s) in \"%s\" points to a maybe freed memory region" v.vname transfer_fn_name in let pointed_to_heap_vars = @@ -129,7 +134,7 @@ struct let side_effect_mem_free ctx freed_heap_vars threadid = let threadid = G.singleton threadid in - D.iter (fun var -> ctx.sideg var threadid) freed_heap_vars + HeapVars.iter (fun var -> ctx.sideg var threadid) freed_heap_vars (* TRANSFER FUNCTIONS *) @@ -153,7 +158,7 @@ struct let enter ctx (lval:lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = let caller_state = ctx.local in List.iter (fun arg -> warn_exp_might_contain_freed "enter" ctx arg) args; - if D.is_empty caller_state then + if AllocaVars.is_empty (fst caller_state) && HeapVars.is_empty (snd caller_state) then [caller_state, caller_state] else ( let reachable_from_args = List.fold_left (fun ad arg -> Queries.AD.join ad (ctx.ask (ReachableFrom arg))) (Queries.AD.empty ()) args in @@ -161,13 +166,18 @@ struct [caller_state, caller_state] else let reachable_vars = Queries.AD.to_var_may reachable_from_args in - let callee_state = D.filter (fun var -> List.mem var reachable_vars) caller_state in (* TODO: use AD.mem directly *) + let callee_state = (AllocaVars.empty (), HeapVars.filter (fun var -> List.mem var reachable_vars) (snd caller_state)) in (* TODO: use AD.mem directly *) [caller_state, callee_state] ) let combine_env ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (callee_local:D.t) (f_ask:Queries.ask) : D.t = - let caller_state = ctx.local in - D.join caller_state callee_local + let (caller_stack_state, caller_heap_state) = ctx.local in + let callee_stack_state = fst callee_local in + let callee_heap_state = snd callee_local in + (* Put all alloca()-vars together with all freed() vars in the caller's second component *) + (* Don't change caller's first component => caller hasn't exited yet *) + let callee_combined_state = HeapVars.join callee_stack_state callee_heap_state in + (caller_stack_state, HeapVars.join caller_heap_state callee_combined_state) let combine_assign ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (callee_local:D.t) (f_ask: Queries.ask): D.t = Option.iter (fun x -> warn_lval_might_contain_freed "enter" ctx x) lval; @@ -185,13 +195,20 @@ struct let pointed_to_heap_vars = Queries.AD.fold (fun addr state -> match addr with - | Queries.AD.Addr.Addr (var,_) when ctx.ask (Queries.IsDynamicallyAlloced var) -> D.add var state + | Queries.AD.Addr.Addr (var,_) when ctx.ask (Queries.IsDynamicallyAlloced var) && ctx.ask (Queries.IsHeapVar var) -> HeapVars.add var state | _ -> state - ) ad (D.empty ()) + ) ad (HeapVars.empty ()) in (* Side-effect the tid that's freeing all the heap vars collected here *) side_effect_mem_free ctx pointed_to_heap_vars (get_current_threadid ctx); - D.join state (pointed_to_heap_vars) (* Add all heap vars, which ptr points to, to the state *) + (* Add all heap vars, which ptr points to, to the state *) + (fst state, HeapVars.join (snd state) pointed_to_heap_vars) + | _ -> state + end + | Alloca _ -> + (* Create fresh heap var for the alloca() call *) + begin match ctx.ask (Queries.HeapVar {on_stack = true}) with + | `Lifted v -> (AllocaVars.add v (fst state), snd state) | _ -> state end | _ -> state From 05a3b98ea27b42994d275e14e43fb372c8337a2c Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 09:44:02 +0200 Subject: [PATCH 0698/1312] Add alloca() UAF test case --- .../regression/74-use_after_free/13-alloca-uaf.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 tests/regression/74-use_after_free/13-alloca-uaf.c diff --git a/tests/regression/74-use_after_free/13-alloca-uaf.c b/tests/regression/74-use_after_free/13-alloca-uaf.c new file mode 100644 index 0000000000..bb052b010c --- /dev/null +++ b/tests/regression/74-use_after_free/13-alloca-uaf.c @@ -0,0 +1,16 @@ +//PARAM: --set ana.activated[+] useAfterFree +#include +#include + +int *f() { + int *c = alloca(sizeof(int)); + return c; +} + +int main(int argc, char const *argv[]) { + int *ps = alloca(sizeof(int)); + int *c = f(); + int a = *ps; + int b = *c; //WARN + return 0; +} From 2fbcdb8d19e8e84998d2d709fb47bd949d743549 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 09:44:22 +0200 Subject: [PATCH 0699/1312] Add alloca() invalid dealloc test case --- .../09-juliet-invalid-dealloc-alloca.c | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 tests/regression/75-invalid_dealloc/09-juliet-invalid-dealloc-alloca.c diff --git a/tests/regression/75-invalid_dealloc/09-juliet-invalid-dealloc-alloca.c b/tests/regression/75-invalid_dealloc/09-juliet-invalid-dealloc-alloca.c new file mode 100644 index 0000000000..9a84d1e49a --- /dev/null +++ b/tests/regression/75-invalid_dealloc/09-juliet-invalid-dealloc-alloca.c @@ -0,0 +1,75 @@ +#include +#include + +typedef struct twoIntsStruct { + int intOne ; + int intTwo ; +} twoIntsStruct; + +void CWE590_Free_Memory_Not_on_Heap__free_struct_alloca_54_bad(void) { + twoIntsStruct *data; + data = (twoIntsStruct *)0; + { + twoIntsStruct *dataBuffer = __builtin_alloca(800UL); + { + size_t i; + i = 0UL; + + goto ldv_3204; + ldv_3203: + ; + + (dataBuffer + i)->intOne = 1; + (dataBuffer + i)->intTwo = 1; + + i += 1UL; + ldv_3204: + ; + + if (i <= 99UL) + goto ldv_3203; + else + goto ldv_3205; + ldv_3205: + ; + } + + data = dataBuffer; + } + + CWE590_Free_Memory_Not_on_Heap__free_struct_alloca_54b_badSink(data); + return; +} + +void CWE590_Free_Memory_Not_on_Heap__free_struct_alloca_54b_badSink(twoIntsStruct *data) { + CWE590_Free_Memory_Not_on_Heap__free_struct_alloca_54c_badSink(data); + return; +} + +void CWE590_Free_Memory_Not_on_Heap__free_struct_alloca_54c_badSink(twoIntsStruct *data) { + CWE590_Free_Memory_Not_on_Heap__free_struct_alloca_54d_badSink(data); + return; +} + +void CWE590_Free_Memory_Not_on_Heap__free_struct_alloca_54d_badSink(twoIntsStruct *data) { + CWE590_Free_Memory_Not_on_Heap__free_struct_alloca_54e_badSink(data); + return; +} + +void CWE590_Free_Memory_Not_on_Heap__free_struct_alloca_54e_badSink(twoIntsStruct *data) { + free((void *)data); //WARN + return; +} + +int main(int argc, char **argv) { + int __retres; + { + CWE590_Free_Memory_Not_on_Heap__free_struct_alloca_54_bad(); + __retres = 0; + goto return_label; + } + + __retres = 0; + return_label: + return __retres; +} From ee6dc3672fac8492a1e59bedb9fddc5c95b542a3 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 29 Sep 2023 10:54:19 +0300 Subject: [PATCH 0700/1312] Fix race in race_reach test for sv-benchmarks --- tests/regression/28-race_reach/61-invariant_racing.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/regression/28-race_reach/61-invariant_racing.c b/tests/regression/28-race_reach/61-invariant_racing.c index 3facd56d32..22277557f9 100644 --- a/tests/regression/28-race_reach/61-invariant_racing.c +++ b/tests/regression/28-race_reach/61-invariant_racing.c @@ -6,9 +6,12 @@ pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; void *t_fun(void *arg) { pthread_mutex_lock(&mutex); - if (x == 0) { + pthread_mutex_lock(&__global_lock); + if (x == 0) { // NORACE + pthread_mutex_unlock(&__global_lock); pthread_mutex_unlock(&mutex); } else { + pthread_mutex_unlock(&__global_lock); pthread_mutex_unlock(&mutex); access(x); } From 5cca543065efea0562294392a4dec1d6833b4b58 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 10:17:57 +0200 Subject: [PATCH 0701/1312] Add forgotton base_address check in BlobSize after master merge --- src/analyses/base.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index b825bafab0..21513cc175 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1249,7 +1249,7 @@ struct (* If there's a non-heap var or an offset in the lval set, we answer with bottom *) (* If we're asking for the BlobSize from the base address, then don't check for offsets => we want to avoid getting bot *) if AD.exists (function - | Addr (v,o) -> (not @@ ctx.ask (Queries.IsDynamicallyAlloced v)) || (ctx.ask (Queries.IsDynamicallyAlloced v) && not @@ ctx.ask (Queries.IsHeapVar v)) || o <> `NoOffset + | Addr (v,o) -> (not @@ ctx.ask (Queries.IsDynamicallyAlloced v)) || (ctx.ask (Queries.IsDynamicallyAlloced v) && not @@ ctx.ask (Queries.IsHeapVar v)) || (if not from_base_addr then o <> `NoOffset else false) | _ -> false) a then Queries.Result.bot q else ( From 4090e7498b43093c2fd1a8a3250194b03c72043c Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 10:20:36 +0200 Subject: [PATCH 0702/1312] Fix __builtin_alloca classification --- src/analyses/libraryFunctions.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 2388bff586..c8c9a25ec1 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -440,7 +440,7 @@ let gcc_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__sync_fetch_and_add", unknown (drop "ptr" [r; w] :: drop "value" [] :: VarArgs (drop' []))); ("__sync_fetch_and_sub", unknown (drop "ptr" [r; w] :: drop "value" [] :: VarArgs (drop' []))); ("__builtin_va_copy", unknown [drop "dest" [w]; drop "src" [r]]); - ("__builtin_alloca", special [__ "size" []] @@ fun size -> Malloc size); + ("__builtin_alloca", special [__ "size" []] @@ fun size -> Alloca size); ] let glibc_desc_list: (string * LibraryDesc.t) list = LibraryDsl.[ From 35513d09c2ed2a0fb6f00a1e0dd1854f9dd79329 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 11:27:35 +0200 Subject: [PATCH 0703/1312] Move all *alloca() functions to gcc_descs_list --- src/analyses/libraryFunctions.ml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index c8c9a25ec1..87f7ef0572 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -64,8 +64,6 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("strtok", unknown ~attrs:[ThreadUnsafe] [drop "str" [r; w]; drop "delim" [r]]); ("__builtin_strcmp", special [__ "s1" [r]; __ "s2" [r]] @@ fun s1 s2 -> Strcmp { s1; s2; n = None; }); ("strncmp", special [__ "s1" [r]; __ "s2" [r]; __ "n" []] @@ fun s1 s2 n -> Strcmp { s1; s2; n = Some n; }); - ("alloca", special [__ "size" []] @@ fun size -> Alloca size); - ("__builtin_alloca", special [__ "size" []] @@ fun size -> Alloca size); ("malloc", special [__ "size" []] @@ fun size -> Malloc size); ("calloc", special [__ "n" []; __ "size" []] @@ fun n size -> Calloc {count = n; size}); ("realloc", special [__ "ptr" [r; f]; __ "size" []] @@ fun ptr size -> Realloc { ptr; size }); @@ -440,6 +438,7 @@ let gcc_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__sync_fetch_and_add", unknown (drop "ptr" [r; w] :: drop "value" [] :: VarArgs (drop' []))); ("__sync_fetch_and_sub", unknown (drop "ptr" [r; w] :: drop "value" [] :: VarArgs (drop' []))); ("__builtin_va_copy", unknown [drop "dest" [w]; drop "src" [r]]); + ("alloca", special [__ "size" []] @@ fun size -> Alloca size); ("__builtin_alloca", special [__ "size" []] @@ fun size -> Alloca size); ] From 6e7c00edaf5891050401b8f3f87a2d07333dca6a Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 11:54:52 +0200 Subject: [PATCH 0704/1312] Add out-of-bounds check for memset and memcpy --- src/analyses/base.ml | 81 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 2 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index c4fdd633d3..49f61fdd1e 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2026,6 +2026,80 @@ struct M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 761] "Free of memory not at start of buffer in function %s for pointer %a" special_fn.vname d_exp ptr | _ -> M.warn ~category:MessageCategory.Analyzer "Pointer %a in function %s doesn't evaluate to a valid address." d_exp ptr special_fn.vname + (* Get the size of the smallest memory that dest points-to *) + let get_min_size_of_dest ctx dest = + let intdom_of_int x = + ID.of_int (Cilfacade.ptrdiff_ikind ()) (Z.of_int x) + in + let points_to_heap_only = + match ctx.ask (Queries.MayPointTo dest) with + | a when not (AD.is_top a) -> + AD.for_all (function + | Addr (v, _) -> ctx.ask (Queries.IsHeapVar v) + | _ -> false + ) a + | _ -> false + in + if points_to_heap_only then + (* Ask for BlobSize from the base address (the second field set to true) in order to avoid BlobSize giving us bot *) + ctx.ask (Queries.BlobSize {exp = dest; base_address = true}) + else + match ctx.ask (Queries.MayPointTo dest) with + | a when not (Queries.AD.is_top a) -> + let pts_list = Queries.AD.elements a in + let pts_elems_to_sizes (addr: Queries.AD.elt) = + begin match addr with + | Addr (v, _) -> + begin match v.vtype with + | TArray (item_typ, _, _) -> + let item_typ_size_in_bytes = (bitsSizeOf item_typ) / 8 in + let item_typ_size_in_bytes = intdom_of_int item_typ_size_in_bytes in + begin match ctx.ask (Queries.EvalLength dest) with + | `Lifted arr_len -> `Lifted (ID.mul item_typ_size_in_bytes arr_len) + | `Bot -> `Bot + | `Top -> `Top + end + | _ -> + let type_size_in_bytes = (bitsSizeOf v.vtype) / 8 in + `Lifted (intdom_of_int type_size_in_bytes) + end + | _ -> `Top + end + in + (* Map each points-to-set element to its size *) + let pts_sizes = List.map pts_elems_to_sizes pts_list in + (* Take the smallest of all sizes that ptr's contents may have *) + begin match pts_sizes with + | [] -> `Bot + | [x] -> x + | x::xs -> List.fold_left (fun acc elem -> + if ValueDomainQueries.ID.compare acc elem >= 0 then elem else acc + ) x xs + end + | _ -> + M.warn "Pointer %a has a points-to-set of top. An invalid memory access might occur" d_exp dest; + `Top + + (* Used for memset() and memcpy() out-of-bounds checks *) + let check_count ctx fun_name dest n = + let (behavior:MessageCategory.behavior) = Undefined MemoryOutOfBoundsAccess in + let cwe_number = 823 in + let dest_size = get_min_size_of_dest ctx dest in + let eval_n = ctx.ask (Queries.EvalInt n) in + match ValueDomainQueries.ID.is_top dest_size, ValueDomainQueries.ID.is_top eval_n with + | true, _ -> + AnalysisState.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest %a in function %s is unknown. Memory out-of-bounds access might occur" d_exp dest fun_name + | _, true -> + AnalysisState.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Count parameter, passed to function %s is unknown. Memory out-of-bounds access might occur" fun_name + | false, false -> + if dest_size < eval_n then begin + AnalysisState.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest in function %s is %a (in bytes). Count is %a (in bytes). Memory out-of-bounds access must occur" fun_name ValueDomainQueries.ID.pretty dest_size ValueDomainQueries.ID.pretty eval_n + end + + let special ctx (lv:lval option) (f: varinfo) (args: exp list) = let invalidate_ret_lv st = match lv with | Some lv -> @@ -2093,7 +2167,8 @@ struct in let st = match desc.special args, f.vname with | Memset { dest; ch; count; }, _ -> - (* TODO: check count *) + (* Check count *) + check_count ctx f.vname dest count; let eval_ch = eval_rv (Analyses.ask_of_ctx ctx) gs st ch in let dest_a, dest_typ = addr_type_of_exp dest in let value = @@ -2110,7 +2185,9 @@ struct let dest_a, dest_typ = addr_type_of_exp dest in let value = VD.zero_init_value dest_typ in set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value - | Memcpy { dest = dst; src }, _ -> + | Memcpy { dest = dst; src; n; }, _ -> + (* Check n *) + check_count ctx f.vname dst n; memory_copying dst src (* strcpy(dest, src); *) | Strcpy { dest = dst; src; n = None }, _ -> From 5ed769f22598ed1aeb5cb36038ffc94cb1c49b55 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 11:55:16 +0200 Subject: [PATCH 0705/1312] Add regr. test case for memset and memcpy out-of-bounds --- .../77-mem-oob/06-memset-memcpy-oob.c | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 tests/regression/77-mem-oob/06-memset-memcpy-oob.c diff --git a/tests/regression/77-mem-oob/06-memset-memcpy-oob.c b/tests/regression/77-mem-oob/06-memset-memcpy-oob.c new file mode 100644 index 0000000000..84a46c75ba --- /dev/null +++ b/tests/regression/77-mem-oob/06-memset-memcpy-oob.c @@ -0,0 +1,20 @@ +// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval +#include +#include + +int main(int argc, char const *argv[]) { + int *a = malloc(10 * sizeof(int)); //Size is 40 bytes, assuming a 4-byte int + int *b = malloc(15 * sizeof(int)); //Size is 60 bytes, assuming a 4-byte int + + memset(a, 0, 40); //NOWARN + memset(a, 0, 10 * sizeof(int)); //NOWARN + memset(a, 0, 41); //WARN + memset(a, 0, 40000000); //WARN + + memcpy(a, b, 40); //NOWARN + memcpy(a, b, 10 * sizeof(int)); //NOWARN + memcpy(a, b, 41); //WARN + memcpy(a, b, 40000000); //WARN + memcpy(a, b, 15 * sizeof(int)); //WARN + return 0; +} From 432a4e3259bea4ac597de6bf78db30f86ba101ae Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 13:31:31 +0300 Subject: [PATCH 0706/1312] Simply HeapVar ask in heap_var Co-authored-by: Simmo Saan --- src/analyses/base.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 21513cc175..f2c4c4ed65 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -151,7 +151,7 @@ struct let longjmp_return = ref dummyFunDec.svar let heap_var on_stack ctx = - let info = match (ctx.ask (Q.HeapVar {on_stack = on_stack})) with + let info = match (ctx.ask (Q.HeapVar {on_stack})) with | `Lifted vinfo -> vinfo | _ -> failwith("Ran without a malloc analysis.") in info From 3bdc92d7c6e58520f6ed552cb687fa158adcf429 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 12:41:48 +0200 Subject: [PATCH 0707/1312] Extract long query ask into own function --- src/analyses/base.ml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index f2c4c4ed65..1c21da3327 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1122,6 +1122,9 @@ struct (* interpreter end *) + let is_not_heap_alloc_var ctx v = + (not (ctx.ask (Queries.IsDynamicallyAlloced v))) || (ctx.ask (Queries.IsDynamicallyAlloced v) && not (ctx.ask (Queries.IsHeapVar v))) + let query_invariant ctx context = let cpa = ctx.local.BaseDomain.cpa in let ask = Analyses.ask_of_ctx ctx in @@ -1249,7 +1252,7 @@ struct (* If there's a non-heap var or an offset in the lval set, we answer with bottom *) (* If we're asking for the BlobSize from the base address, then don't check for offsets => we want to avoid getting bot *) if AD.exists (function - | Addr (v,o) -> (not @@ ctx.ask (Queries.IsDynamicallyAlloced v)) || (ctx.ask (Queries.IsDynamicallyAlloced v) && not @@ ctx.ask (Queries.IsHeapVar v)) || (if not from_base_addr then o <> `NoOffset else false) + | Addr (v,o) -> is_not_heap_alloc_var ctx v || (if not from_base_addr then o <> `NoOffset else false) | _ -> false) a then Queries.Result.bot q else ( @@ -2009,7 +2012,7 @@ struct let check_invalid_mem_dealloc ctx special_fn ptr = let has_non_heap_var = AD.exists (function - | Addr (v,_) -> not (ctx.ask (Q.IsDynamicallyAlloced v)) || (ctx.ask (Q.IsDynamicallyAlloced v) && not @@ ctx.ask (Q.IsHeapVar v)) + | Addr (v,_) -> is_not_heap_alloc_var ctx v | _ -> false) in let has_non_zero_offset = AD.exists (function From 7b1d29850036fb1287b10c8aeb962a162b7fac11 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 12:50:34 +0200 Subject: [PATCH 0708/1312] Rename queries: * HeapVar -> AllocVar * IsDynamicallyAlloced -> IsAllocVar --- src/analyses/base.ml | 10 +++++----- src/analyses/mallocFresh.ml | 2 +- src/analyses/memLeak.ml | 4 ++-- src/analyses/useAfterFree.ml | 6 +++--- src/analyses/wrapperFunctionAnalysis.ml | 4 ++-- src/domains/queries.ml | 26 +++++++++++++------------ 6 files changed, 27 insertions(+), 25 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 1c21da3327..dacd412072 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -151,7 +151,7 @@ struct let longjmp_return = ref dummyFunDec.svar let heap_var on_stack ctx = - let info = match (ctx.ask (Q.HeapVar {on_stack})) with + let info = match (ctx.ask (Q.AllocVar {on_stack})) with | `Lifted vinfo -> vinfo | _ -> failwith("Ran without a malloc analysis.") in info @@ -1123,7 +1123,7 @@ struct (* interpreter end *) let is_not_heap_alloc_var ctx v = - (not (ctx.ask (Queries.IsDynamicallyAlloced v))) || (ctx.ask (Queries.IsDynamicallyAlloced v) && not (ctx.ask (Queries.IsHeapVar v))) + (not (ctx.ask (Queries.IsAllocVar v))) || (ctx.ask (Queries.IsAllocVar v) && not (ctx.ask (Queries.IsHeapVar v))) let query_invariant ctx context = let cpa = ctx.local.BaseDomain.cpa in @@ -1400,7 +1400,7 @@ struct let t = match t_override with | Some t -> t | None -> - if a.f (Q.IsDynamicallyAlloced x) then + if a.f (Q.IsAllocVar x) then (* the vtype of heap vars will be TVoid, so we need to trust the pointer we got to this to be of the right type *) (* i.e. use the static type of the pointer here *) lval_type @@ -1446,7 +1446,7 @@ struct (* Optimization to avoid evaluating integer values when setting them. The case when invariant = true requires the old_value to be sound for the meet. Allocated blocks are representend by Blobs with additional information, so they need to be looked-up. *) - let old_value = if not invariant && Cil.isIntegralType x.vtype && not (a.f (IsDynamicallyAlloced x)) && offs = `NoOffset then begin + let old_value = if not invariant && Cil.isIntegralType x.vtype && not (a.f (IsAllocVar x)) && offs = `NoOffset then begin VD.bot_value ~varAttr:x.vattr lval_type end else Priv.read_global a priv_getg st x @@ -2596,7 +2596,7 @@ struct | MayBeThreadReturn | PartAccess _ | IsHeapVar _ - | IsDynamicallyAlloced _ + | IsAllocVar _ | IsMultiple _ | CreatedThreads | MustJoinedThreads -> diff --git a/src/analyses/mallocFresh.ml b/src/analyses/mallocFresh.ml index 2eed772527..3a501fc72f 100644 --- a/src/analyses/mallocFresh.ml +++ b/src/analyses/mallocFresh.ml @@ -43,7 +43,7 @@ struct | Malloc _ | Calloc _ | Realloc _ -> - begin match ctx.ask (HeapVar {on_stack = false}) with + begin match ctx.ask (AllocVar {on_stack = false}) with | `Lifted var -> D.add var ctx.local | _ -> ctx.local end diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index abf0deb954..8576096dfe 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -44,7 +44,7 @@ struct | Realloc _ -> (* Warn about multi-threaded programs as soon as we encounter a dynamic memory allocation function *) warn_for_multi_threaded ctx; - begin match ctx.ask (Queries.HeapVar {on_stack = false}) with + begin match ctx.ask (Queries.AllocVar {on_stack = false}) with | `Lifted var -> D.add var state | _ -> state end @@ -53,7 +53,7 @@ struct | ad when not (Queries.AD.is_top ad) && Queries.AD.cardinal ad = 1 -> (* Note: Need to always set "ana.malloc.unique_address_count" to a value > 0 *) begin match Queries.AD.choose ad with - | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsDynamicallyAlloced v) && ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsMultiple v) -> D.remove v state (* Unique pointed to heap vars *) + | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsAllocVar v) && ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsMultiple v) -> D.remove v state (* Unique pointed to heap vars *) | _ -> state end | _ -> state diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index c313c0a90b..7e89dddb69 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -96,7 +96,7 @@ struct let pointed_to_heap_vars = Queries.AD.fold (fun addr vars -> match addr with - | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsDynamicallyAlloced v) -> v :: vars + | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsAllocVar v) -> v :: vars | _ -> vars ) ad [] in @@ -195,7 +195,7 @@ struct let pointed_to_heap_vars = Queries.AD.fold (fun addr state -> match addr with - | Queries.AD.Addr.Addr (var,_) when ctx.ask (Queries.IsDynamicallyAlloced var) && ctx.ask (Queries.IsHeapVar var) -> HeapVars.add var state + | Queries.AD.Addr.Addr (var,_) when ctx.ask (Queries.IsAllocVar var) && ctx.ask (Queries.IsHeapVar var) -> HeapVars.add var state | _ -> state ) ad (HeapVars.empty ()) in @@ -207,7 +207,7 @@ struct end | Alloca _ -> (* Create fresh heap var for the alloca() call *) - begin match ctx.ask (Queries.HeapVar {on_stack = true}) with + begin match ctx.ask (Queries.AllocVar {on_stack = true}) with | `Lifted v -> (AllocaVars.add v (fst state), snd state) | _ -> state end diff --git a/src/analyses/wrapperFunctionAnalysis.ml b/src/analyses/wrapperFunctionAnalysis.ml index 43d472d0e3..5c0176df48 100644 --- a/src/analyses/wrapperFunctionAnalysis.ml +++ b/src/analyses/wrapperFunctionAnalysis.ml @@ -133,7 +133,7 @@ module MallocWrapper : MCPSpec = struct let query (ctx: (D.t, G.t, C.t, V.t) ctx) (type a) (q: a Q.t): a Q.result = let wrapper_node, counter = ctx.local in match q with - | Q.HeapVar {on_stack = on_stack} -> + | Q.AllocVar {on_stack = on_stack} -> let node = match wrapper_node with | `Lifted wrapper_node -> wrapper_node | _ -> node_for_ctx ctx @@ -145,7 +145,7 @@ module MallocWrapper : MCPSpec = struct `Lifted var | Q.IsHeapVar v -> NodeVarinfoMap.mem_varinfo v && not @@ hasAttribute "stack_alloca" v.vattr - | Q.IsDynamicallyAlloced v -> + | Q.IsAllocVar v -> NodeVarinfoMap.mem_varinfo v | Q.IsMultiple v -> begin match NodeVarinfoMap.from_varinfo v with diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 07e239bee5..c706339bf2 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -101,9 +101,11 @@ type _ t = | IterVars: itervar -> Unit.t t | PathQuery: int * 'a t -> 'a t (** Query only one path under witness lifter. *) | DYojson: FlatYojson.t t (** Get local state Yojson of one path under [PathQuery]. *) - | HeapVar: {on_stack: bool} -> VI.t t (* If on_stack is [true], then alloca() or a similar function was called *) + | AllocVar: {on_stack: bool} -> VI.t t + (* Create a variable representing a dynamic allocation-site *) + (* If on_stack is [true], then the dynamic allocation is on the stack (i.e., alloca() or a similar function was called). Otherwise, allocation is on the heap *) + | IsAllocVar: varinfo -> MayBool.t t (* [true] if variable represents dynamically allocated memory *) | IsHeapVar: varinfo -> MayBool.t t (* TODO: is may or must? *) - | IsDynamicallyAlloced: varinfo -> MayBool.t t (* [true] if heap var represents dynamically alloced memory *) | IsMultiple: varinfo -> MustBool.t t (* For locals: Is another copy of this local variable reachable via pointers? *) (* For dynamically allocated memory: Does this abstract variable corrrespond to a unique heap location? *) @@ -155,7 +157,7 @@ struct | MayBePublicWithout _ -> (module MayBool) | MayBeThreadReturn -> (module MayBool) | IsHeapVar _ -> (module MayBool) - | IsDynamicallyAlloced _ -> (module MayBool) + | IsAllocVar _ -> (module MayBool) | MustBeProtectedBy _ -> (module MustBool) | MustBeAtomic -> (module MustBool) | MustBeSingleThreaded _ -> (module MustBool) @@ -167,7 +169,7 @@ struct | BlobSize _ -> (module ID) | CurrentThreadId -> (module ThreadIdDomain.ThreadLifted) | ThreadCreateIndexedNode -> (module ThreadNodeLattice) - | HeapVar _ -> (module VI) + | AllocVar _ -> (module VI) | EvalStr _ -> (module SD) | IterPrevVars _ -> (module Unit) | IterVars _ -> (module Unit) @@ -220,7 +222,7 @@ struct | MayBePublicWithout _ -> MayBool.top () | MayBeThreadReturn -> MayBool.top () | IsHeapVar _ -> MayBool.top () - | IsDynamicallyAlloced _ -> MayBool.top () + | IsAllocVar _ -> MayBool.top () | MutexType _ -> MutexAttrDomain.top () | MustBeProtectedBy _ -> MustBool.top () | MustBeAtomic -> MustBool.top () @@ -233,7 +235,7 @@ struct | BlobSize _ -> ID.top () | CurrentThreadId -> ThreadIdDomain.ThreadLifted.top () | ThreadCreateIndexedNode -> ThreadNodeLattice.top () - | HeapVar _ -> VI.top () + | AllocVar _ -> VI.top () | EvalStr _ -> SD.top () | IterPrevVars _ -> Unit.top () | IterVars _ -> Unit.top () @@ -293,7 +295,7 @@ struct | Any (PartAccess _) -> 23 | Any (IterPrevVars _) -> 24 | Any (IterVars _) -> 25 - | Any (HeapVar _) -> 29 + | Any (AllocVar _) -> 29 | Any (IsHeapVar _) -> 30 | Any (IsMultiple _) -> 31 | Any (EvalThread _) -> 32 @@ -318,7 +320,7 @@ struct | Any ThreadCreateIndexedNode -> 51 | Any ThreadsJoinedCleanly -> 52 | Any (TmpSpecial _) -> 53 - | Any (IsDynamicallyAlloced _) -> 54 + | Any (IsAllocVar _) -> 54 let rec compare a b = let r = Stdlib.compare (order a) (order b) in @@ -358,7 +360,7 @@ struct else compare (Any q1) (Any q2) | Any (IsHeapVar v1), Any (IsHeapVar v2) -> CilType.Varinfo.compare v1 v2 - | Any (IsDynamicallyAlloced v1), Any (IsDynamicallyAlloced v2) -> CilType.Varinfo.compare v1 v2 + | Any (IsAllocVar v1), Any (IsAllocVar v2) -> CilType.Varinfo.compare v1 v2 | Any (IsMultiple v1), Any (IsMultiple v2) -> CilType.Varinfo.compare v1 v2 | Any (EvalThread e1), Any (EvalThread e2) -> CilType.Exp.compare e1 e2 | Any (EvalJumpBuf e1), Any (EvalJumpBuf e2) -> CilType.Exp.compare e1 e2 @@ -399,7 +401,7 @@ struct | Any (IterVars i) -> 0 | Any (PathQuery (i, q)) -> 31 * i + hash (Any q) | Any (IsHeapVar v) -> CilType.Varinfo.hash v - | Any (IsDynamicallyAlloced v) -> CilType.Varinfo.hash v + | Any (IsAllocVar v) -> CilType.Varinfo.hash v | Any (IsMultiple v) -> CilType.Varinfo.hash v | Any (EvalThread e) -> CilType.Exp.hash e | Any (EvalJumpBuf e) -> CilType.Exp.hash e @@ -447,9 +449,9 @@ struct | Any (IterPrevVars i) -> Pretty.dprintf "IterPrevVars _" | Any (IterVars i) -> Pretty.dprintf "IterVars _" | Any (PathQuery (i, q)) -> Pretty.dprintf "PathQuery (%d, %a)" i pretty (Any q) - | Any (HeapVar {on_stack = on_stack}) -> Pretty.dprintf "HeapVar %b" on_stack + | Any (AllocVar {on_stack = on_stack}) -> Pretty.dprintf "AllocVar %b" on_stack | Any (IsHeapVar v) -> Pretty.dprintf "IsHeapVar %a" CilType.Varinfo.pretty v - | Any (IsDynamicallyAlloced v) -> Pretty.dprintf "IsDynamicallyAlloced %a" CilType.Varinfo.pretty v + | Any (IsAllocVar v) -> Pretty.dprintf "IsAllocVar %a" CilType.Varinfo.pretty v | Any (IsMultiple v) -> Pretty.dprintf "IsMultiple %a" CilType.Varinfo.pretty v | Any (EvalThread e) -> Pretty.dprintf "EvalThread %a" CilType.Exp.pretty e | Any (EvalJumpBuf e) -> Pretty.dprintf "EvalJumpBuf %a" CilType.Exp.pretty e From 5f162216927f10cf42a9b1b5ad50ddea0bd4868d Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 13:13:46 +0200 Subject: [PATCH 0709/1312] Fix wrong query usage in UAF --- src/analyses/useAfterFree.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index f3dbecacc2..02231336c0 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -121,7 +121,7 @@ struct let pointed_to_heap_vars = Queries.AD.fold (fun addr vars -> match addr with - | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsHeapVar v) -> v :: vars + | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsAllocVar v) -> v :: vars | _ -> vars ) ad [] in From b1f2f2d9a5f3a665bdd63445f1d4796338b59401 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 13:14:04 +0200 Subject: [PATCH 0710/1312] Fix test numbering --- .../74-use_after_free/{13-alloca-uaf.c => 14-alloca-uaf.c} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename tests/regression/74-use_after_free/{13-alloca-uaf.c => 14-alloca-uaf.c} (91%) diff --git a/tests/regression/74-use_after_free/13-alloca-uaf.c b/tests/regression/74-use_after_free/14-alloca-uaf.c similarity index 91% rename from tests/regression/74-use_after_free/13-alloca-uaf.c rename to tests/regression/74-use_after_free/14-alloca-uaf.c index bb052b010c..3dc494cb09 100644 --- a/tests/regression/74-use_after_free/13-alloca-uaf.c +++ b/tests/regression/74-use_after_free/14-alloca-uaf.c @@ -10,7 +10,7 @@ int *f() { int main(int argc, char const *argv[]) { int *ps = alloca(sizeof(int)); int *c = f(); - int a = *ps; + int a = *ps; //NOWARN int b = *c; //WARN return 0; } From 12bee2749dcba9ae933c657cb991ffb3ea292013 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 13:22:37 +0200 Subject: [PATCH 0711/1312] Add temporary fix for failing MacOS CI job --- tests/regression/77-mem-oob/06-memset-memcpy-oob.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/regression/77-mem-oob/06-memset-memcpy-oob.c b/tests/regression/77-mem-oob/06-memset-memcpy-oob.c index 84a46c75ba..1050c199e0 100644 --- a/tests/regression/77-mem-oob/06-memset-memcpy-oob.c +++ b/tests/regression/77-mem-oob/06-memset-memcpy-oob.c @@ -1,4 +1,5 @@ -// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval +// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval --disable warn.info +// TODO: The "--disable warn.info" part is a temporary fix and needs to be removed once the MacOS CI job is fixed #include #include From 80a51be64966b15376f5d1f83e61046d6e522565 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 29 Sep 2023 14:40:07 +0300 Subject: [PATCH 0712/1312] Add extensive atomic and volatile ignorable races tests --- .../regression/04-mutex/62-simple_atomic_nr.c | 83 ++++++++++++++++--- tests/regression/04-mutex/99-volatile.c | 51 ++++++++++-- 2 files changed, 114 insertions(+), 20 deletions(-) diff --git a/tests/regression/04-mutex/62-simple_atomic_nr.c b/tests/regression/04-mutex/62-simple_atomic_nr.c index d63f303251..fdef44bdd6 100644 --- a/tests/regression/04-mutex/62-simple_atomic_nr.c +++ b/tests/regression/04-mutex/62-simple_atomic_nr.c @@ -1,24 +1,83 @@ #include -#include #include -atomic_int myglobal; -pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; -pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER; +atomic_int g1; +_Atomic int g2; +_Atomic(int) g3; + +atomic_int a1[1]; +_Atomic int a2[1]; +_Atomic(int) a3[1]; + +struct s { + int f0; + atomic_int f1; + _Atomic int f2; + _Atomic(int) f3; +}; + +struct s s1; +_Atomic struct s s2; +_Atomic(struct s) s3; + +typedef atomic_int t_int1; +typedef _Atomic int t_int2; +typedef _Atomic(int) t_int3; + +t_int1 t1; +t_int2 t2; +t_int3 t3; + +typedef int t_int0; + +_Atomic t_int0 t0; +_Atomic(t_int0) t00; + +atomic_int *p0 = &g1; +int x; +// int * _Atomic p1 = &x; // TODO: https://github.com/goblint/cil/issues/64 +// _Atomic(int*) p2 = &x; // TODO: https://github.com/goblint/cil/issues/64 +// atomic_int * _Atomic p3 = &g1; // TODO: https://github.com/goblint/cil/issues/64 + +atomic_flag flag = ATOMIC_FLAG_INIT; void *t_fun(void *arg) { - pthread_mutex_lock(&mutex1); - myglobal=myglobal+1; // NORACE - pthread_mutex_unlock(&mutex1); + g1++; // NORACE + g2++; // NORACE + g3++; // NORACE + a1[0]++; // NORACE + a2[0]++; // NORACE + a3[0]++; // NORACE + s1.f1++; // NORACE + s1.f2++; // NORACE + s1.f3++; // NORACE + s2.f0++; // NORACE + s3.f0++; // NORACE + t1++; // NORACE + t2++; // NORACE + t3++; // NORACE + t0++; // NORACE + t00++; // NORACE + (*p0)++; // NORACE + // p1++; // TODO NORACE: https://github.com/goblint/cil/issues/64 + // p2++; // TODO NORACE: https://github.com/goblint/cil/issues/64 + // p3++; // TODO NORACE: https://github.com/goblint/cil/issues/64 + // (*p3)++; // TODO NORACE: https://github.com/goblint/cil/issues/64 + + struct s ss = {0}; + s2 = ss; // NORACE + s3 = ss; // NORACE + + atomic_flag_clear(&flag); // NORACE + atomic_flag_test_and_set(&flag); // NORACE return NULL; } int main(void) { - pthread_t id; + pthread_t id, id2; pthread_create(&id, NULL, t_fun, NULL); - pthread_mutex_lock(&mutex2); - myglobal=myglobal+1; // NORACE - pthread_mutex_unlock(&mutex2); - pthread_join (id, NULL); + pthread_create(&id2, NULL, t_fun, NULL); + pthread_join(id, NULL); + pthread_join(id2, NULL); return 0; } diff --git a/tests/regression/04-mutex/99-volatile.c b/tests/regression/04-mutex/99-volatile.c index aaf81f13a1..7c2a255902 100644 --- a/tests/regression/04-mutex/99-volatile.c +++ b/tests/regression/04-mutex/99-volatile.c @@ -1,18 +1,53 @@ // PARAM: --disable ana.race.volatile #include -#include -volatile int myglobal; +volatile int g1; + +volatile int a1[1]; + +struct s { + int f0; + volatile int f1; +}; + +struct s s1; +volatile struct s s2; + +typedef volatile int t_int1; + +t_int1 t1; + +typedef int t_int0; + +volatile t_int0 t0; + +volatile int *p0 = &g1; +int x; +int * volatile p1 = &x; +volatile int * volatile p2 = &g1; void *t_fun(void *arg) { - myglobal= 8; //NORACE + g1++; // NORACE + a1[0]++; // NORACE + s1.f1++; // NORACE + s2.f0++; // NORACE + t1++; // NORACE + t0++; // NORACE + (*p0)++; // NORACE + p1++; // NORACE + p2++; // NORACE + (*p2)++; // NORACE + + struct s ss = {0}; + s2 = ss; // NORACE return NULL; } int main(void) { - pthread_t id; - pthread_create(&id, NULL, t_fun, (void*) &myglobal); - myglobal = 42; //NORACE - pthread_join (id, NULL); + pthread_t id, id2; + pthread_create(&id, NULL, t_fun, NULL); + pthread_create(&id2, NULL, t_fun, NULL); + pthread_join(id, NULL); + pthread_join(id2, NULL); return 0; -} \ No newline at end of file +} From 8b513e8431e6f6301fca9162e83a2b7ed792dbcd Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 29 Sep 2023 15:39:45 +0300 Subject: [PATCH 0713/1312] Rewrite ignorable race memo check --- src/domains/access.ml | 64 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 51 insertions(+), 13 deletions(-) diff --git a/src/domains/access.ml b/src/domains/access.ml index fa6446df16..7b6fa77b06 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -10,7 +10,7 @@ module M = Messages (* Some helper functions to avoid flagging race warnings on atomic types, and * other irrelevant stuff, such as mutexes and functions. *) -let is_ignorable_type (t: typ): bool = +let rec is_ignorable_type (t: typ): bool = match t with | TNamed ({ tname = "atomic_t" | "pthread_mutex_t" | "pthread_rwlock_t" | "pthread_spinlock_t" | "spinlock_t" | "pthread_cond_t"; _ }, _) -> true | TComp ({ cname = "__pthread_mutex_s" | "__pthread_rwlock_arch_t" | "__jmp_buf_tag" | "_pthread_cleanup_buffer" | "__pthread_cleanup_frame" | "__cancel_jmp_buf_tag"; _}, _) -> true @@ -18,20 +18,59 @@ let is_ignorable_type (t: typ): bool = begin match Cilfacade.split_anoncomp_name cname with | (true, Some ("__once_flag" | "__pthread_unwind_buf_t" | "__cancel_jmp_buf"), _) -> true (* anonstruct *) | (false, Some ("pthread_mutexattr_t" | "pthread_condattr_t" | "pthread_barrierattr_t"), _) -> true (* anonunion *) - | _ -> false + | _ -> false (* TODO: fall back to attrs case *) end | TComp ({ cname = "lock_class_key"; _ }, _) -> true | TInt (IInt, attr) when hasAttribute "mutex" attr -> true - | t when hasAttribute "atomic" (typeAttrs t) -> true (* C11 _Atomic *) - | _ -> false + | TFun _ -> true + | _ -> + let attrs = typeAttrsOuter t in + let is_ignorable_attr = function + | Attr ("volatile", _) when not (get_bool "ana.race.volatile") -> true (* volatile & races on volatiles should not be reported *) + | Attr ("atomic", _) -> true (* C11 _Atomic *) + | _ -> false + in + if List.exists is_ignorable_attr attrs then + true + else ( + match t with + | TNamed ({ttype; _}, attrs) -> is_ignorable_type (typeAddAttributes attrs ttype) + | _ -> false + ) + +let rec is_ignorable_type_offset (t: typ) (o: _ Offset.t): bool = + if is_ignorable_type t then + true + else ( + let blendAttributes baseAttrs = (* copied from Cilfacade.typeOffset *) + let (_, _, contageous) = partitionAttributes ~default:AttrName baseAttrs in + typeAddAttributes contageous + in + match o with + | `NoOffset -> false (* already checked t *) + | `Index (_, o') -> + begin match unrollType t with + | TArray (et, _, attrs) -> + let t' = blendAttributes attrs et in + is_ignorable_type_offset t' o' + | _ -> false (* index on non-array*) + end + | `Field (f, o') -> + begin match unrollType t with + | TComp (_, attrs) -> + let t' = blendAttributes attrs f.ftype in + is_ignorable_type_offset t' o' + | _ -> false (* field on non-compound *) + end + ) + +let is_ignorable_mval = function + | ({vaddrof = false; vattr; _}, _) when hasAttribute "thread" vattr -> true (* Thread-Local Storage *) + | (v, o) -> is_ignorable_type_offset v.vtype o -let is_ignorable = function - | None -> false - | Some (v,os) when hasAttribute "thread" v.vattr && not (v.vaddrof) -> true (* Thread-Local Storage *) - | Some (v,os) when BaseUtil.is_volatile v && not (get_bool "ana.race.volatile") -> true (* volatile & races on volatiles should not be reported *) - | Some (v,os) -> - try isFunctionType v.vtype || is_ignorable_type v.vtype - with Not_found -> false +let is_ignorable_memo = function + | (`Type _, _) -> false (* TODO: do something *) + | (`Var v, o) -> is_ignorable_mval (v, o) module TSH = Hashtbl.Make (CilType.Typsig) @@ -195,8 +234,7 @@ let get_val_type e: acc_typ = (** Add access to {!Memo} after distributing. *) let add_one ~side memo: unit = - let mv = Memo.to_mval memo in - let ignorable = is_ignorable mv in + let ignorable = is_ignorable_memo memo in if M.tracing then M.trace "access" "add_one %a (ignorable = %B)\n" Memo.pretty memo ignorable; if not ignorable then side memo From 66071f2555aa6f6be4cb2be8b8795cab2a0e5888 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 29 Sep 2023 15:47:28 +0300 Subject: [PATCH 0714/1312] Exclude atomic_flag races --- src/analyses/libraryFunctions.ml | 6 ++++++ src/domains/access.ml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index c3ca48da93..6ff591e7c4 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -127,6 +127,10 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("setjmp", special [__ "env" [w]] @@ fun env -> Setjmp { env }); ("longjmp", special [__ "env" [r]; __ "value" []] @@ fun env value -> Longjmp { env; value }); ("atexit", unknown [drop "function" [s]]); + ("atomic_flag_clear", unknown [drop "obj" [w]]); + ("atomic_flag_clear_explicit", unknown [drop "obj" [w]; drop "order" []]); + ("atomic_flag_test_and_set", unknown [drop "obj" [r; w]]); + ("atomic_flag_test_and_set_explicit", unknown [drop "obj" [r; w]; drop "order" []]); ] (** C POSIX library functions. @@ -435,6 +439,8 @@ let gcc_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__builtin_popcountll", unknown [drop "x" []]); ("__atomic_store_n", unknown [drop "ptr" [w]; drop "val" []; drop "memorder" []]); ("__atomic_load_n", unknown [drop "ptr" [r]; drop "memorder" []]); + ("__atomic_clear", unknown [drop "ptr" [w]; drop "memorder" []]); + ("__atomic_test_and_set", unknown [drop "ptr" [r; w]; drop "memorder" []]); ("__sync_fetch_and_add", unknown (drop "ptr" [r; w] :: drop "value" [] :: VarArgs (drop' []))); ("__sync_fetch_and_sub", unknown (drop "ptr" [r; w] :: drop "value" [] :: VarArgs (drop' []))); ("__builtin_va_copy", unknown [drop "dest" [w]; drop "src" [r]]); diff --git a/src/domains/access.ml b/src/domains/access.ml index 7b6fa77b06..7066e6e349 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -12,7 +12,7 @@ module M = Messages let rec is_ignorable_type (t: typ): bool = match t with - | TNamed ({ tname = "atomic_t" | "pthread_mutex_t" | "pthread_rwlock_t" | "pthread_spinlock_t" | "spinlock_t" | "pthread_cond_t"; _ }, _) -> true + | TNamed ({ tname = "atomic_t" | "pthread_mutex_t" | "pthread_rwlock_t" | "pthread_spinlock_t" | "spinlock_t" | "pthread_cond_t" | "atomic_flag"; _ }, _) -> true | TComp ({ cname = "__pthread_mutex_s" | "__pthread_rwlock_arch_t" | "__jmp_buf_tag" | "_pthread_cleanup_buffer" | "__pthread_cleanup_frame" | "__cancel_jmp_buf_tag"; _}, _) -> true | TComp ({ cname; _}, _) when String.starts_with_stdlib ~prefix:"__anon" cname -> begin match Cilfacade.split_anoncomp_name cname with From df9fda742d767ab92108b127a21b5d137967c312 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 29 Sep 2023 15:58:36 +0300 Subject: [PATCH 0715/1312] Document refactored race ignore check --- src/domains/access.ml | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/domains/access.ml b/src/domains/access.ml index 7066e6e349..3601624ae6 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -11,6 +11,7 @@ module M = Messages * other irrelevant stuff, such as mutexes and functions. *) let rec is_ignorable_type (t: typ): bool = + (* efficient pattern matching first *) match t with | TNamed ({ tname = "atomic_t" | "pthread_mutex_t" | "pthread_rwlock_t" | "pthread_spinlock_t" | "spinlock_t" | "pthread_cond_t" | "atomic_flag"; _ }, _) -> true | TComp ({ cname = "__pthread_mutex_s" | "__pthread_rwlock_arch_t" | "__jmp_buf_tag" | "_pthread_cleanup_buffer" | "__pthread_cleanup_frame" | "__cancel_jmp_buf_tag"; _}, _) -> true @@ -18,13 +19,13 @@ let rec is_ignorable_type (t: typ): bool = begin match Cilfacade.split_anoncomp_name cname with | (true, Some ("__once_flag" | "__pthread_unwind_buf_t" | "__cancel_jmp_buf"), _) -> true (* anonstruct *) | (false, Some ("pthread_mutexattr_t" | "pthread_condattr_t" | "pthread_barrierattr_t"), _) -> true (* anonunion *) - | _ -> false (* TODO: fall back to attrs case *) + | _ -> false (* TODO: fall back to attrs case? *) end - | TComp ({ cname = "lock_class_key"; _ }, _) -> true - | TInt (IInt, attr) when hasAttribute "mutex" attr -> true + | TComp ({ cname = "lock_class_key"; _ }, _) -> true (* kernel? *) + | TInt (IInt, attr) when hasAttribute "mutex" attr -> true (* kernel? *) | TFun _ -> true - | _ -> - let attrs = typeAttrsOuter t in + | _ -> (* check attrs *) + let attrs = typeAttrsOuter t in (* only outer because we unroll TNamed ourselves *) let is_ignorable_attr = function | Attr ("volatile", _) when not (get_bool "ana.race.volatile") -> true (* volatile & races on volatiles should not be reported *) | Attr ("atomic", _) -> true (* C11 _Atomic *) @@ -33,14 +34,17 @@ let rec is_ignorable_type (t: typ): bool = if List.exists is_ignorable_attr attrs then true else ( + (* unroll TNamed once *) + (* can't use unrollType because we want to check TNamed-s at all intermediate typedefs as well *) match t with | TNamed ({ttype; _}, attrs) -> is_ignorable_type (typeAddAttributes attrs ttype) | _ -> false ) let rec is_ignorable_type_offset (t: typ) (o: _ Offset.t): bool = + (* similar to Cilfacade.typeOffset but we want to check types at all intermediate offsets as well *) if is_ignorable_type t then - true + true (* type at offset so far ignorable, no need to recurse *) else ( let blendAttributes baseAttrs = (* copied from Cilfacade.typeOffset *) let (_, _, contageous) = partitionAttributes ~default:AttrName baseAttrs in @@ -53,7 +57,7 @@ let rec is_ignorable_type_offset (t: typ) (o: _ Offset.t): bool = | TArray (et, _, attrs) -> let t' = blendAttributes attrs et in is_ignorable_type_offset t' o' - | _ -> false (* index on non-array*) + | _ -> false (* index on non-array *) end | `Field (f, o') -> begin match unrollType t with @@ -66,7 +70,7 @@ let rec is_ignorable_type_offset (t: typ) (o: _ Offset.t): bool = let is_ignorable_mval = function | ({vaddrof = false; vattr; _}, _) when hasAttribute "thread" vattr -> true (* Thread-Local Storage *) - | (v, o) -> is_ignorable_type_offset v.vtype o + | (v, o) -> is_ignorable_type_offset v.vtype o (* can't use Cilfacade.typeOffset because we want to check types at all intermediate offsets as well *) let is_ignorable_memo = function | (`Type _, _) -> false (* TODO: do something *) From 18a1733dc41bbea8178bc7b481c4de48abe43970 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Fri, 29 Sep 2023 16:20:13 +0300 Subject: [PATCH 0716/1312] Add atomic library functions from nidhugg benchmark set --- src/analyses/libraryFunctions.ml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 6ff591e7c4..1964b7bc1f 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -131,6 +131,8 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("atomic_flag_clear_explicit", unknown [drop "obj" [w]; drop "order" []]); ("atomic_flag_test_and_set", unknown [drop "obj" [r; w]]); ("atomic_flag_test_and_set_explicit", unknown [drop "obj" [r; w]; drop "order" []]); + ("atomic_load", unknown [drop "obj" [r]]); + ("atomic_store", unknown [drop "obj" [w]; drop "desired" []]); ] (** C POSIX library functions. @@ -438,9 +440,20 @@ let gcc_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__builtin_popcountl", unknown [drop "x" []]); ("__builtin_popcountll", unknown [drop "x" []]); ("__atomic_store_n", unknown [drop "ptr" [w]; drop "val" []; drop "memorder" []]); + ("__atomic_store", unknown [drop "ptr" [w]; drop "val" [r]; drop "memorder" []]); ("__atomic_load_n", unknown [drop "ptr" [r]; drop "memorder" []]); + ("__atomic_load", unknown [drop "ptr" [r]; drop "ret" [w]; drop "memorder" []]); ("__atomic_clear", unknown [drop "ptr" [w]; drop "memorder" []]); + ("__atomic_compare_exchange_n", unknown [drop "ptr" [r; w]; drop "expected" [r; w]; drop "desired" []; drop "weak" []; drop "success_memorder" []; drop "failure_memorder" []]); + ("__atomic_compare_exchange", unknown [drop "ptr" [r; w]; drop "expected" [r; w]; drop "desired" [r]; drop "weak" []; drop "success_memorder" []; drop "failure_memorder" []]); + ("__atomic_fetch_add", unknown [drop "ptr" [r; w]; drop "val" []; drop "memorder" []]); + ("__atomic_fetch_sub", unknown [drop "ptr" [r; w]; drop "val" []; drop "memorder" []]); + ("__atomic_fetch_and", unknown [drop "ptr" [r; w]; drop "val" []; drop "memorder" []]); + ("__atomic_fetch_xor", unknown [drop "ptr" [r; w]; drop "val" []; drop "memorder" []]); + ("__atomic_fetch_or", unknown [drop "ptr" [r; w]; drop "val" []; drop "memorder" []]); + ("__atomic_fetch_nand", unknown [drop "ptr" [r; w]; drop "val" []; drop "memorder" []]); ("__atomic_test_and_set", unknown [drop "ptr" [r; w]; drop "memorder" []]); + ("__atomic_thread_fence", unknown [drop "memorder" []]); ("__sync_fetch_and_add", unknown (drop "ptr" [r; w] :: drop "value" [] :: VarArgs (drop' []))); ("__sync_fetch_and_sub", unknown (drop "ptr" [r; w] :: drop "value" [] :: VarArgs (drop' []))); ("__builtin_va_copy", unknown [drop "dest" [w]; drop "src" [r]]); From 8c7dbb5d726ffb6084ad9276f1367f2f7032735c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 29 Sep 2023 16:42:28 +0300 Subject: [PATCH 0717/1312] Ignore FILE type in races We are unsound for I/O races anyway. --- src/domains/access.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/domains/access.ml b/src/domains/access.ml index 3601624ae6..69fb5b53fc 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -13,8 +13,8 @@ module M = Messages let rec is_ignorable_type (t: typ): bool = (* efficient pattern matching first *) match t with - | TNamed ({ tname = "atomic_t" | "pthread_mutex_t" | "pthread_rwlock_t" | "pthread_spinlock_t" | "spinlock_t" | "pthread_cond_t" | "atomic_flag"; _ }, _) -> true - | TComp ({ cname = "__pthread_mutex_s" | "__pthread_rwlock_arch_t" | "__jmp_buf_tag" | "_pthread_cleanup_buffer" | "__pthread_cleanup_frame" | "__cancel_jmp_buf_tag"; _}, _) -> true + | TNamed ({ tname = "atomic_t" | "pthread_mutex_t" | "pthread_rwlock_t" | "pthread_spinlock_t" | "spinlock_t" | "pthread_cond_t" | "atomic_flag" | "FILE" | "__FILE"; _ }, _) -> true + | TComp ({ cname = "__pthread_mutex_s" | "__pthread_rwlock_arch_t" | "__jmp_buf_tag" | "_pthread_cleanup_buffer" | "__pthread_cleanup_frame" | "__cancel_jmp_buf_tag" | "_IO_FILE"; _}, _) -> true | TComp ({ cname; _}, _) when String.starts_with_stdlib ~prefix:"__anon" cname -> begin match Cilfacade.split_anoncomp_name cname with | (true, Some ("__once_flag" | "__pthread_unwind_buf_t" | "__cancel_jmp_buf"), _) -> true (* anonstruct *) From abc97e5773dd68e4745ebf3daac4a1538b9d5bb6 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 29 Sep 2023 16:56:45 +0300 Subject: [PATCH 0718/1312] Pin goblint-cil with exposed Cil.typeSigAddAttrs --- goblint.opam | 7 +++---- goblint.opam.locked | 6 ++++++ goblint.opam.template | 7 +++---- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/goblint.opam b/goblint.opam index 661222805b..c84e7a56f0 100644 --- a/goblint.opam +++ b/goblint.opam @@ -74,12 +74,11 @@ dev-repo: "git+https://github.com/goblint/analyzer.git" # on `dune build` goblint.opam will be generated from goblint.opam.template and dune-project # also remember to generate/adjust goblint.opam.locked! available: os-distribution != "alpine" & arch != "arm64" -# pin-depends: [ - # published goblint-cil 2.0.2 is currently up-to-date, so no pin needed - # [ "goblint-cil.2.0.2" "git+https://github.com/goblint/cil.git#98598d94f796a63751e5a9d39c6b3a9fe1f32330" ] +pin-depends: [ + [ "goblint-cil.2.0.2" "git+https://github.com/goblint/cil.git#398dca3d94a06a9026b3737aabf100ee3498229f" ] # TODO: add back after release, only pinned for optimization (https://github.com/ocaml-ppx/ppx_deriving/pull/252) # [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] -# ] +] post-messages: [ "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} ] diff --git a/goblint.opam.locked b/goblint.opam.locked index bb59c41dd1..8556e9eebb 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -128,3 +128,9 @@ conflicts: [ post-messages: [ "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} ] +pin-depends: [ + [ + "goblint-cil.2.0.2" + "git+https://github.com/goblint/cil.git#398dca3d94a06a9026b3737aabf100ee3498229f" + ] +] diff --git a/goblint.opam.template b/goblint.opam.template index 6259c4d498..d9e1ebf477 100644 --- a/goblint.opam.template +++ b/goblint.opam.template @@ -1,12 +1,11 @@ # on `dune build` goblint.opam will be generated from goblint.opam.template and dune-project # also remember to generate/adjust goblint.opam.locked! available: os-distribution != "alpine" & arch != "arm64" -# pin-depends: [ - # published goblint-cil 2.0.2 is currently up-to-date, so no pin needed - # [ "goblint-cil.2.0.2" "git+https://github.com/goblint/cil.git#98598d94f796a63751e5a9d39c6b3a9fe1f32330" ] +pin-depends: [ + [ "goblint-cil.2.0.2" "git+https://github.com/goblint/cil.git#398dca3d94a06a9026b3737aabf100ee3498229f" ] # TODO: add back after release, only pinned for optimization (https://github.com/ocaml-ppx/ppx_deriving/pull/252) # [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] -# ] +] post-messages: [ "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} ] From 93396c8c43ec961d21dadbe6598be5c4ca768a91 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 29 Sep 2023 16:58:56 +0300 Subject: [PATCH 0719/1312] Duplicate race ignore check for typsig --- src/domains/access.ml | 51 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/src/domains/access.ml b/src/domains/access.ml index 69fb5b53fc..ac34966880 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -68,12 +68,61 @@ let rec is_ignorable_type_offset (t: typ) (o: _ Offset.t): bool = end ) +let is_ignorable_typsig (ts: typsig): bool = + (* efficient pattern matching first *) + match ts with + | TSComp (_, ("__pthread_mutex_s" | "__pthread_rwlock_arch_t" | "__jmp_buf_tag" | "_pthread_cleanup_buffer" | "__pthread_cleanup_frame" | "__cancel_jmp_buf_tag" | "_IO_FILE"), _) -> true + | TSComp (_, cname, _) when String.starts_with_stdlib ~prefix:"__anon" cname -> + begin match Cilfacade.split_anoncomp_name cname with + | (true, Some ("__once_flag" | "__pthread_unwind_buf_t" | "__cancel_jmp_buf"), _) -> true (* anonstruct *) + | (false, Some ("pthread_mutexattr_t" | "pthread_condattr_t" | "pthread_barrierattr_t"), _) -> true (* anonunion *) + | _ -> false (* TODO: fall back to attrs case? *) + end + | TSComp (_, "lock_class_key", _) -> true (* kernel? *) + | TSFun _ -> true + | TSBase t -> is_ignorable_type t + | _ -> (* check attrs *) + let attrs = typeSigAttrs ts in (* only outer because we unroll TNamed ourselves *) + let is_ignorable_attr = function + | Attr ("volatile", _) when not (get_bool "ana.race.volatile") -> true (* volatile & races on volatiles should not be reported *) + | Attr ("atomic", _) -> true (* C11 _Atomic *) + | _ -> false + in + List.exists is_ignorable_attr attrs + +let rec is_ignorable_typsig_offset (ts: typsig) (o: _ Offset.t): bool = + (* similar to Cilfacade.typeOffset but we want to check types at all intermediate offsets as well *) + if is_ignorable_typsig ts then + true (* type at offset so far ignorable, no need to recurse *) + else ( + let blendAttributes baseAttrs = (* copied from Cilfacade.typeOffset *) + let (_, _, contageous) = partitionAttributes ~default:AttrName baseAttrs in + typeSigAddAttrs contageous + in + match o with + | `NoOffset -> false (* already checked t *) + | `Index (_, o') -> + begin match ts with + | TSArray (et, _, attrs) -> + let t' = blendAttributes attrs et in + is_ignorable_typsig_offset t' o' + | _ -> false (* index on non-array *) + end + | `Field (f, o') -> + begin match ts with + | TSComp (_, _, attrs) -> + let t' = blendAttributes attrs (typeSig f.ftype) in + is_ignorable_typsig_offset t' o' + | _ -> false (* field on non-compound *) + end + ) + let is_ignorable_mval = function | ({vaddrof = false; vattr; _}, _) when hasAttribute "thread" vattr -> true (* Thread-Local Storage *) | (v, o) -> is_ignorable_type_offset v.vtype o (* can't use Cilfacade.typeOffset because we want to check types at all intermediate offsets as well *) let is_ignorable_memo = function - | (`Type _, _) -> false (* TODO: do something *) + | (`Type ts, o) -> is_ignorable_typsig_offset ts o | (`Var v, o) -> is_ignorable_mval (v, o) module TSH = Hashtbl.Make (CilType.Typsig) From 1d3c7dd62f7d33f39dc69f95bbba0f83b659686e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 29 Sep 2023 17:08:53 +0300 Subject: [PATCH 0720/1312] Extract common comp name and attrs ignoring for races --- src/domains/access.ml | 56 +++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 32 deletions(-) diff --git a/src/domains/access.ml b/src/domains/access.ml index ac34966880..832c6a762e 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -10,28 +10,34 @@ module M = Messages (* Some helper functions to avoid flagging race warnings on atomic types, and * other irrelevant stuff, such as mutexes and functions. *) -let rec is_ignorable_type (t: typ): bool = - (* efficient pattern matching first *) - match t with - | TNamed ({ tname = "atomic_t" | "pthread_mutex_t" | "pthread_rwlock_t" | "pthread_spinlock_t" | "spinlock_t" | "pthread_cond_t" | "atomic_flag" | "FILE" | "__FILE"; _ }, _) -> true - | TComp ({ cname = "__pthread_mutex_s" | "__pthread_rwlock_arch_t" | "__jmp_buf_tag" | "_pthread_cleanup_buffer" | "__pthread_cleanup_frame" | "__cancel_jmp_buf_tag" | "_IO_FILE"; _}, _) -> true - | TComp ({ cname; _}, _) when String.starts_with_stdlib ~prefix:"__anon" cname -> +let is_ignorable_comp_name = function + | "__pthread_mutex_s" | "__pthread_rwlock_arch_t" | "__jmp_buf_tag" | "_pthread_cleanup_buffer" | "__pthread_cleanup_frame" | "__cancel_jmp_buf_tag" | "_IO_FILE" -> true + | cname when String.starts_with_stdlib ~prefix:"__anon" cname -> begin match Cilfacade.split_anoncomp_name cname with | (true, Some ("__once_flag" | "__pthread_unwind_buf_t" | "__cancel_jmp_buf"), _) -> true (* anonstruct *) | (false, Some ("pthread_mutexattr_t" | "pthread_condattr_t" | "pthread_barrierattr_t"), _) -> true (* anonunion *) - | _ -> false (* TODO: fall back to attrs case? *) + | _ -> false end - | TComp ({ cname = "lock_class_key"; _ }, _) -> true (* kernel? *) + | "lock_class_key" -> true (* kernel? *) + | _ -> false + +let is_ignorable_attrs attrs = + let is_ignorable_attr = function + | Attr ("volatile", _) when not (get_bool "ana.race.volatile") -> true (* volatile & races on volatiles should not be reported *) + | Attr ("atomic", _) -> true (* C11 _Atomic *) + | _ -> false + in + List.exists is_ignorable_attr attrs + +let rec is_ignorable_type (t: typ): bool = + (* efficient pattern matching first *) + match t with + | TNamed ({ tname = "atomic_t" | "pthread_mutex_t" | "pthread_rwlock_t" | "pthread_spinlock_t" | "spinlock_t" | "pthread_cond_t" | "atomic_flag" | "FILE" | "__FILE"; _ }, _) -> true + | TComp ({ cname; _}, _) when is_ignorable_comp_name cname -> true | TInt (IInt, attr) when hasAttribute "mutex" attr -> true (* kernel? *) | TFun _ -> true - | _ -> (* check attrs *) - let attrs = typeAttrsOuter t in (* only outer because we unroll TNamed ourselves *) - let is_ignorable_attr = function - | Attr ("volatile", _) when not (get_bool "ana.race.volatile") -> true (* volatile & races on volatiles should not be reported *) - | Attr ("atomic", _) -> true (* C11 _Atomic *) - | _ -> false - in - if List.exists is_ignorable_attr attrs then + | _ -> + if is_ignorable_attrs (typeAttrsOuter t) then (* only outer because we unroll TNamed ourselves *) true else ( (* unroll TNamed once *) @@ -71,24 +77,10 @@ let rec is_ignorable_type_offset (t: typ) (o: _ Offset.t): bool = let is_ignorable_typsig (ts: typsig): bool = (* efficient pattern matching first *) match ts with - | TSComp (_, ("__pthread_mutex_s" | "__pthread_rwlock_arch_t" | "__jmp_buf_tag" | "_pthread_cleanup_buffer" | "__pthread_cleanup_frame" | "__cancel_jmp_buf_tag" | "_IO_FILE"), _) -> true - | TSComp (_, cname, _) when String.starts_with_stdlib ~prefix:"__anon" cname -> - begin match Cilfacade.split_anoncomp_name cname with - | (true, Some ("__once_flag" | "__pthread_unwind_buf_t" | "__cancel_jmp_buf"), _) -> true (* anonstruct *) - | (false, Some ("pthread_mutexattr_t" | "pthread_condattr_t" | "pthread_barrierattr_t"), _) -> true (* anonunion *) - | _ -> false (* TODO: fall back to attrs case? *) - end - | TSComp (_, "lock_class_key", _) -> true (* kernel? *) + | TSComp (_, cname, _) when is_ignorable_comp_name cname -> true | TSFun _ -> true | TSBase t -> is_ignorable_type t - | _ -> (* check attrs *) - let attrs = typeSigAttrs ts in (* only outer because we unroll TNamed ourselves *) - let is_ignorable_attr = function - | Attr ("volatile", _) when not (get_bool "ana.race.volatile") -> true (* volatile & races on volatiles should not be reported *) - | Attr ("atomic", _) -> true (* C11 _Atomic *) - | _ -> false - in - List.exists is_ignorable_attr attrs + | _ -> is_ignorable_attrs (typeSigAttrs ts) let rec is_ignorable_typsig_offset (ts: typsig) (o: _ Offset.t): bool = (* similar to Cilfacade.typeOffset but we want to check types at all intermediate offsets as well *) From 52eac2fbf9287db299514a2b72846cb060c8f234 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 29 Sep 2023 17:16:20 +0300 Subject: [PATCH 0721/1312] Clean up typsig race ignoring --- src/domains/access.ml | 25 +++++++++---------------- src/util/cilfacade.ml | 9 +++++++++ 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/domains/access.ml b/src/domains/access.ml index 832c6a762e..8907ccbc32 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -52,28 +52,25 @@ let rec is_ignorable_type_offset (t: typ) (o: _ Offset.t): bool = if is_ignorable_type t then true (* type at offset so far ignorable, no need to recurse *) else ( - let blendAttributes baseAttrs = (* copied from Cilfacade.typeOffset *) - let (_, _, contageous) = partitionAttributes ~default:AttrName baseAttrs in - typeAddAttributes contageous - in match o with | `NoOffset -> false (* already checked t *) | `Index (_, o') -> begin match unrollType t with | TArray (et, _, attrs) -> - let t' = blendAttributes attrs et in + let t' = Cilfacade.typeBlendAttributes attrs et in is_ignorable_type_offset t' o' | _ -> false (* index on non-array *) end | `Field (f, o') -> begin match unrollType t with | TComp (_, attrs) -> - let t' = blendAttributes attrs f.ftype in + let t' = Cilfacade.typeBlendAttributes attrs f.ftype in is_ignorable_type_offset t' o' | _ -> false (* field on non-compound *) end ) +(** {!is_ignorable_type} for {!typsig}. *) let is_ignorable_typsig (ts: typsig): bool = (* efficient pattern matching first *) match ts with @@ -82,29 +79,25 @@ let is_ignorable_typsig (ts: typsig): bool = | TSBase t -> is_ignorable_type t | _ -> is_ignorable_attrs (typeSigAttrs ts) +(** {!is_ignorable_type_offset} for {!typsig}. *) let rec is_ignorable_typsig_offset (ts: typsig) (o: _ Offset.t): bool = - (* similar to Cilfacade.typeOffset but we want to check types at all intermediate offsets as well *) if is_ignorable_typsig ts then true (* type at offset so far ignorable, no need to recurse *) else ( - let blendAttributes baseAttrs = (* copied from Cilfacade.typeOffset *) - let (_, _, contageous) = partitionAttributes ~default:AttrName baseAttrs in - typeSigAddAttrs contageous - in match o with | `NoOffset -> false (* already checked t *) | `Index (_, o') -> begin match ts with - | TSArray (et, _, attrs) -> - let t' = blendAttributes attrs et in - is_ignorable_typsig_offset t' o' + | TSArray (ets, _, attrs) -> + let ts' = Cilfacade.typeSigBlendAttributes attrs ets in + is_ignorable_typsig_offset ts' o' | _ -> false (* index on non-array *) end | `Field (f, o') -> begin match ts with | TSComp (_, _, attrs) -> - let t' = blendAttributes attrs (typeSig f.ftype) in - is_ignorable_typsig_offset t' o' + let t' = Cilfacade.typeBlendAttributes attrs f.ftype in + is_ignorable_type_offset t' o' (* switch to type because it is more precise with TNamed *) | _ -> false (* field on non-compound *) end ) diff --git a/src/util/cilfacade.ml b/src/util/cilfacade.ml index eb7330aa19..6d55211c8d 100644 --- a/src/util/cilfacade.ml +++ b/src/util/cilfacade.ml @@ -322,6 +322,15 @@ and typeOffset basetyp = | t -> raise (TypeOfError (Field_NonCompound (fi, t))) +let typeBlendAttributes baseAttrs = (* copied from Cilfacade.typeOffset *) + let (_, _, contageous) = partitionAttributes ~default:AttrName baseAttrs in + typeAddAttributes contageous + +let typeSigBlendAttributes baseAttrs = + let (_, _, contageous) = partitionAttributes ~default:AttrName baseAttrs in + typeSigAddAttrs contageous + + (** {!Cil.mkCast} using our {!typeOf}. *) let mkCast ~(e: exp) ~(newt: typ) = let oldt = From 01ad122f7d6f0e345d40868a7ecfe253ce57a4a4 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 29 Sep 2023 17:25:19 +0300 Subject: [PATCH 0722/1312] Change 05-lval_ls/23-race-null-type-deep to not use now-ignored FILE struct --- .../05-lval_ls/23-race-null-type-deep.c | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/tests/regression/05-lval_ls/23-race-null-type-deep.c b/tests/regression/05-lval_ls/23-race-null-type-deep.c index 6f29964d1e..f7de758d8f 100644 --- a/tests/regression/05-lval_ls/23-race-null-type-deep.c +++ b/tests/regression/05-lval_ls/23-race-null-type-deep.c @@ -1,9 +1,15 @@ +// PARAM: --disable sem.unknown_function.invalidate.globals --disable sem.unknown_function.spawn #include -#include + +struct s { + int f; +}; + +extern void magic(struct s *p); void *t_fun(void *arg) { void *top; - fclose(top); // RACE + magic(top); // RACE return NULL; } @@ -21,31 +27,31 @@ int main(void) { void *top; switch (r) { case 0: - feof(NULL); // NORACE + magic(NULL); // NORACE break; case 1: - feof(0); // NORACE + magic(0); // NORACE break; case 2: - feof(zero); // NORACE + magic(zero); // NORACE break; case 3: - feof(1); // RACE + magic(1); // RACE break; case 4: - feof(one); // RACE + magic(one); // RACE break; case 5: - feof(r); // RACE + magic(r); // RACE break; case 6: - feof(null); // NORACE + magic(null); // NORACE break; case 7: - feof(unknown); // RACE + magic(unknown); // RACE break; case 8: - feof(top); // RACE + magic(top); // RACE break; default: break; From 391c6ce91678ba93085d6f8081fa4ee1aa54936d Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 16:41:42 +0200 Subject: [PATCH 0723/1312] Change memset/memcpy count warning from must to may --- src/analyses/base.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 49f61fdd1e..605d0f0993 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2096,7 +2096,7 @@ struct | false, false -> if dest_size < eval_n then begin AnalysisState.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest in function %s is %a (in bytes). Count is %a (in bytes). Memory out-of-bounds access must occur" fun_name ValueDomainQueries.ID.pretty dest_size ValueDomainQueries.ID.pretty eval_n + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest in function %s is %a (in bytes). Count is %a (in bytes). Memory out-of-bounds access may occur" fun_name ValueDomainQueries.ID.pretty dest_size ValueDomainQueries.ID.pretty eval_n end From fae72563a48b8b7a344395e8f6d75d76630bc8c9 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 16:51:05 +0200 Subject: [PATCH 0724/1312] Use ID.lt to compare dest size with count --- src/analyses/base.ml | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 605d0f0993..9e35694ff8 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2086,17 +2086,27 @@ struct let cwe_number = 823 in let dest_size = get_min_size_of_dest ctx dest in let eval_n = ctx.ask (Queries.EvalInt n) in - match ValueDomainQueries.ID.is_top dest_size, ValueDomainQueries.ID.is_top eval_n with - | true, _ -> + match dest_size, eval_n with + | `Top, _ -> AnalysisState.svcomp_may_invalid_deref := true; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest %a in function %s is unknown. Memory out-of-bounds access might occur" d_exp dest fun_name - | _, true -> + | _, `Top -> AnalysisState.svcomp_may_invalid_deref := true; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Count parameter, passed to function %s is unknown. Memory out-of-bounds access might occur" fun_name - | false, false -> - if dest_size < eval_n then begin - AnalysisState.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest in function %s is %a (in bytes). Count is %a (in bytes). Memory out-of-bounds access may occur" fun_name ValueDomainQueries.ID.pretty dest_size ValueDomainQueries.ID.pretty eval_n + | `Bot, _ -> + AnalysisState.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest %a in function %s is bottom. Memory out-of-bounds access might occur" d_exp dest fun_name + | _, `Bot -> + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Count parameter, passed to function %s is bottom" fun_name + | `Lifted ds, `Lifted en -> + begin match ID.to_bool (ID.lt ds en) with + | Some true -> + AnalysisState.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest in function %s is %a (in bytes). Count is %a (in bytes). Memory out-of-bounds access may occur" fun_name ValueDomainQueries.ID.pretty dest_size ValueDomainQueries.ID.pretty eval_n + | Some false -> () + | None -> + AnalysisState.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Could not compare size of dest (%a) with count (%a) in function %s. Memory out-of-bounds access may occur" ID.pretty ds ID.pretty en fun_name end From 655362e70a7d0c2a42c4dba910e5c67de1aee1c9 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 16:53:39 +0200 Subject: [PATCH 0725/1312] Use join to combine all points-to elements sizes --- src/analyses/base.ml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 9e35694ff8..22fce826f5 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2072,9 +2072,7 @@ struct begin match pts_sizes with | [] -> `Bot | [x] -> x - | x::xs -> List.fold_left (fun acc elem -> - if ValueDomainQueries.ID.compare acc elem >= 0 then elem else acc - ) x xs + | x::xs -> List.fold_left ValueDomainQueries.ID.join x xs end | _ -> M.warn "Pointer %a has a points-to-set of top. An invalid memory access might occur" d_exp dest; From 2534945eec78d99658cc2bf5b420af35580b7fe3 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 17:03:45 +0200 Subject: [PATCH 0726/1312] Do not ingore offsets when calling BlobSize for memset/memcpy --- src/analyses/base.ml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 22fce826f5..255f82cb99 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2041,8 +2041,7 @@ struct | _ -> false in if points_to_heap_only then - (* Ask for BlobSize from the base address (the second field set to true) in order to avoid BlobSize giving us bot *) - ctx.ask (Queries.BlobSize {exp = dest; base_address = true}) + ctx.ask (Queries.BlobSize {exp = dest; base_address = false}) else match ctx.ask (Queries.MayPointTo dest) with | a when not (Queries.AD.is_top a) -> From 033913b760c0220d7057c830c3bfe2bc3a964069 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 17:05:39 +0200 Subject: [PATCH 0727/1312] Change name to get_size_of_dest which makes more sense --- src/analyses/base.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 255f82cb99..d510389338 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2026,8 +2026,8 @@ struct M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 761] "Free of memory not at start of buffer in function %s for pointer %a" special_fn.vname d_exp ptr | _ -> M.warn ~category:MessageCategory.Analyzer "Pointer %a in function %s doesn't evaluate to a valid address." d_exp ptr special_fn.vname - (* Get the size of the smallest memory that dest points-to *) - let get_min_size_of_dest ctx dest = + (* Get the size of the memory that dest points-to *) + let get_size_of_dest ctx dest = let intdom_of_int x = ID.of_int (Cilfacade.ptrdiff_ikind ()) (Z.of_int x) in From 4ec98951e641b5862b1caebd88514e3bd4a064f0 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 17:20:41 +0200 Subject: [PATCH 0728/1312] Add exception handling for ID operations --- src/analyses/base.ml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index d510389338..01fb24d862 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2054,7 +2054,11 @@ struct let item_typ_size_in_bytes = (bitsSizeOf item_typ) / 8 in let item_typ_size_in_bytes = intdom_of_int item_typ_size_in_bytes in begin match ctx.ask (Queries.EvalLength dest) with - | `Lifted arr_len -> `Lifted (ID.mul item_typ_size_in_bytes arr_len) + | `Lifted arr_len -> + begin + try `Lifted (ID.mul item_typ_size_in_bytes arr_len) + with IntDomain.ArithmeticOnIntegerBot _ -> `Bot + end | `Bot -> `Bot | `Top -> `Top end @@ -2096,7 +2100,13 @@ struct | _, `Bot -> M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Count parameter, passed to function %s is bottom" fun_name | `Lifted ds, `Lifted en -> - begin match ID.to_bool (ID.lt ds en) with + let dest_size_lt_count = + begin + try ID.lt ds en + with IntDomain.ArithmeticOnIntegerBot _ -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () + end + in + begin match ID.to_bool dest_size_lt_count with | Some true -> AnalysisState.svcomp_may_invalid_deref := true; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest in function %s is %a (in bytes). Count is %a (in bytes). Memory out-of-bounds access may occur" fun_name ValueDomainQueries.ID.pretty dest_size ValueDomainQueries.ID.pretty eval_n From 1e69b64cbf7b3d93c92129e61a1173340d94125d Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 17:21:10 +0200 Subject: [PATCH 0729/1312] Fix wrong name use --- src/analyses/base.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 01fb24d862..26959e6aa3 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2085,7 +2085,7 @@ struct let check_count ctx fun_name dest n = let (behavior:MessageCategory.behavior) = Undefined MemoryOutOfBoundsAccess in let cwe_number = 823 in - let dest_size = get_min_size_of_dest ctx dest in + let dest_size = get_size_of_dest ctx dest in let eval_n = ctx.ask (Queries.EvalInt n) in match dest_size, eval_n with | `Top, _ -> From 49dcac98eac518e8bd2070b542e05f1730c48970 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 30 Sep 2023 00:38:35 +0200 Subject: [PATCH 0730/1312] Fix incompatible ikinds --- src/analyses/base.ml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 26959e6aa3..d56d6653c6 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2055,8 +2055,9 @@ struct let item_typ_size_in_bytes = intdom_of_int item_typ_size_in_bytes in begin match ctx.ask (Queries.EvalLength dest) with | `Lifted arr_len -> + let arr_len_casted = ID.cast_to (Cilfacade.ptrdiff_ikind ()) arr_len in begin - try `Lifted (ID.mul item_typ_size_in_bytes arr_len) + try `Lifted (ID.mul item_typ_size_in_bytes arr_len_casted) with IntDomain.ArithmeticOnIntegerBot _ -> `Bot end | `Bot -> `Bot @@ -2100,9 +2101,11 @@ struct | _, `Bot -> M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Count parameter, passed to function %s is bottom" fun_name | `Lifted ds, `Lifted en -> + let casted_ds = ID.cast_to (Cilfacade.ptrdiff_ikind ()) ds in + let casted_en = ID.cast_to (Cilfacade.ptrdiff_ikind ()) en in let dest_size_lt_count = begin - try ID.lt ds en + try ID.lt casted_ds casted_en with IntDomain.ArithmeticOnIntegerBot _ -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () end in From 87dadd265c5a8af8f8087307ef31aa41de51ff56 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 30 Sep 2023 23:43:27 +0200 Subject: [PATCH 0731/1312] Add more and more sophisticated memset and memcpy tests --- .../77-mem-oob/06-memset-memcpy-oob.c | 21 -------- tests/regression/77-mem-oob/06-memset-oob.c | 54 +++++++++++++++++++ tests/regression/77-mem-oob/07-memcpy-oob.c | 53 ++++++++++++++++++ 3 files changed, 107 insertions(+), 21 deletions(-) delete mode 100644 tests/regression/77-mem-oob/06-memset-memcpy-oob.c create mode 100644 tests/regression/77-mem-oob/06-memset-oob.c create mode 100644 tests/regression/77-mem-oob/07-memcpy-oob.c diff --git a/tests/regression/77-mem-oob/06-memset-memcpy-oob.c b/tests/regression/77-mem-oob/06-memset-memcpy-oob.c deleted file mode 100644 index 1050c199e0..0000000000 --- a/tests/regression/77-mem-oob/06-memset-memcpy-oob.c +++ /dev/null @@ -1,21 +0,0 @@ -// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval --disable warn.info -// TODO: The "--disable warn.info" part is a temporary fix and needs to be removed once the MacOS CI job is fixed -#include -#include - -int main(int argc, char const *argv[]) { - int *a = malloc(10 * sizeof(int)); //Size is 40 bytes, assuming a 4-byte int - int *b = malloc(15 * sizeof(int)); //Size is 60 bytes, assuming a 4-byte int - - memset(a, 0, 40); //NOWARN - memset(a, 0, 10 * sizeof(int)); //NOWARN - memset(a, 0, 41); //WARN - memset(a, 0, 40000000); //WARN - - memcpy(a, b, 40); //NOWARN - memcpy(a, b, 10 * sizeof(int)); //NOWARN - memcpy(a, b, 41); //WARN - memcpy(a, b, 40000000); //WARN - memcpy(a, b, 15 * sizeof(int)); //WARN - return 0; -} diff --git a/tests/regression/77-mem-oob/06-memset-oob.c b/tests/regression/77-mem-oob/06-memset-oob.c new file mode 100644 index 0000000000..931f7eaa8c --- /dev/null +++ b/tests/regression/77-mem-oob/06-memset-oob.c @@ -0,0 +1,54 @@ +// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval --disable warn.info +// TODO: The "--disable warn.info" part is a temporary fix and needs to be removed once the MacOS CI job is fixed +#include +#include +#include + +typedef struct s { + int a; + char b; +} s; + +int main(int argc, char const *argv[]) { + int *a = malloc(10 * sizeof(int)); //Size is 40 bytes, assuming a 4-byte int + + memset(a, 0, 40); //NOWARN + memset(a, 0, 10 * sizeof(int)); //NOWARN + memset(a, 0, 41); //WARN + memset(a, 0, 40000000); //WARN + + int d; + + if (argc == 15) { + int c = 55; + a = &c; + memset(a, 0, argv[5]); //WARN + } else if (argv[2] == 2) { + a = &d; + } + + memset(a, 0, 40); //WARN + + int input; + scanf("%d", &input); + memset(a, 0, input); //WARN + + + + int *b = malloc(15 * sizeof(int)); //Size is 60 bytes, assuming a 4-byte int + memset(b, 0, 60); //NOWARN + b += 1; + memset(b, 0, 60); //WARN + + + + s *s_ptr = malloc(sizeof(s)); + memset(s_ptr, 0, sizeof(s)); //NOWARN + memset(s_ptr->a, 0, sizeof(s)); //WARN + memset(s_ptr->b, 0, sizeof(s)); //WARN + + s_ptr = s_ptr->a; + memset(s_ptr, 0, sizeof(s)); //WARN + + return 0; +} diff --git a/tests/regression/77-mem-oob/07-memcpy-oob.c b/tests/regression/77-mem-oob/07-memcpy-oob.c new file mode 100644 index 0000000000..012f92996e --- /dev/null +++ b/tests/regression/77-mem-oob/07-memcpy-oob.c @@ -0,0 +1,53 @@ +// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval --disable warn.info +// TODO: The "--disable warn.info" part is a temporary fix and needs to be removed once the MacOS CI job is fixed +#include +#include + +typedef struct s { + int a; + char b; +} s; + +int main(int argc, char const *argv[]) { + int *a = malloc(10 * sizeof(int)); //Size is 40 bytes, assuming a 4-byte int + int *b = malloc(15 * sizeof(int)); //Size is 60 bytes, assuming a 4-byte int + + memcpy(a, b, 40); //NOWARN + memcpy(a, b, 10 * sizeof(int)); //NOWARN + memcpy(a, b, 41); //WARN + memcpy(a, b, 40000000); //WARN + memcpy(a, b, 15 * sizeof(int)); //WARN + + int d; + + if (*argv == 42) { + a = &d; + } else if (*(argv + 5)) { + int random = rand(); + a = &random; + memcpy(a, b, 40); //WARN + } + + memcpy(a, b, 40); //WARN + memcpy(a, b, sizeof(a)); //WARN + + memcpy(b, a, 60); //NOWARN + b += 1; + memcpy(b, a, 60); //WARN + + + s *s_ptr = malloc(sizeof(s)); + memcpy(s_ptr, a, sizeof(s)); //NOWARN + memcpy(s_ptr->a, 0, sizeof(s)); //WARN + memcpy(s_ptr->b, 0, sizeof(s)); //WARN + + memcpy(s_ptr, a, 40); //WARN + memcpy(s_ptr, a, 60); //WARN + memcpy(s_ptr, b, 40); //WARN + memcpy(s_ptr, b, 60); //WARN + + s_ptr = s_ptr->b; + memcpy(s_ptr, a, sizeof(s)); //WARN + + return 0; +} From 9e9b5e35d395a6b8e79fb186d9c5fcea68ca8db9 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 1 Oct 2023 00:14:58 +0200 Subject: [PATCH 0732/1312] Add memset/memcpy test case with arrays --- .../77-mem-oob/08-memset-memcpy-array.c | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 tests/regression/77-mem-oob/08-memset-memcpy-array.c diff --git a/tests/regression/77-mem-oob/08-memset-memcpy-array.c b/tests/regression/77-mem-oob/08-memset-memcpy-array.c new file mode 100644 index 0000000000..f231ba2dc4 --- /dev/null +++ b/tests/regression/77-mem-oob/08-memset-memcpy-array.c @@ -0,0 +1,43 @@ +// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval --disable warn.info +// TODO: The "--disable warn.info" part is a temporary fix and needs to be removed once the MacOS CI job is fixed +#include +#include + +int main(int argc, char const *argv[]) { + int arr[42]; // Size should be 168 bytes (with 4 byte ints) + int *b = arr; + + + memset(b, 0, 168); //NOWARN + memset(b, 0, sizeof(arr)); //NOWARN + memset(b, 0, 169); //WARN + memset(b, 0, sizeof(arr) + 1); //WARN + + int *c = malloc(sizeof(arr)); // Size should be 168 bytes (with 4 byte ints) + memcpy(b, c, 168); //NOWARN + memcpy(b, c, sizeof(arr)); //NOWARN + memcpy(b, c, 169); //WARN + memcpy(b, c, sizeof(arr) + 1); //WARN + + int d; + + if (*argv == 42) { + b = &d; + memset(b, 0, 168); //WARN + memcpy(b, c, 168); //WARN + } else if (*(argv + 5)) { + int random = rand(); + b = &random; + memset(b, 0, 168); //WARN + memcpy(b, c, 168); //WARN + } + + memset(b, 0, sizeof(arr)); //WARN + memcpy(b, c, sizeof(arr)); //WARN + memset(b, 0, sizeof(int)); //NOWARN + memcpy(b, c, sizeof(int)); //NOWARN + memset(b, 0, sizeof(int) + 1); //WARN + memcpy(b, c, sizeof(int) + 1); //WARN + + return 0; +} From 5ca357d3ee1412ef0d9907b966b732fda99cb927 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 1 Oct 2023 17:14:55 +0200 Subject: [PATCH 0733/1312] Fix some bugs in memOutOfBounds and move memset/memcpy checks there --- src/analyses/base.ml | 97 ------------------- src/analyses/memOutOfBounds.ml | 170 ++++++++++++++++++++++++--------- 2 files changed, 127 insertions(+), 140 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index d56d6653c6..57c32fa3bc 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2026,99 +2026,6 @@ struct M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 761] "Free of memory not at start of buffer in function %s for pointer %a" special_fn.vname d_exp ptr | _ -> M.warn ~category:MessageCategory.Analyzer "Pointer %a in function %s doesn't evaluate to a valid address." d_exp ptr special_fn.vname - (* Get the size of the memory that dest points-to *) - let get_size_of_dest ctx dest = - let intdom_of_int x = - ID.of_int (Cilfacade.ptrdiff_ikind ()) (Z.of_int x) - in - let points_to_heap_only = - match ctx.ask (Queries.MayPointTo dest) with - | a when not (AD.is_top a) -> - AD.for_all (function - | Addr (v, _) -> ctx.ask (Queries.IsHeapVar v) - | _ -> false - ) a - | _ -> false - in - if points_to_heap_only then - ctx.ask (Queries.BlobSize {exp = dest; base_address = false}) - else - match ctx.ask (Queries.MayPointTo dest) with - | a when not (Queries.AD.is_top a) -> - let pts_list = Queries.AD.elements a in - let pts_elems_to_sizes (addr: Queries.AD.elt) = - begin match addr with - | Addr (v, _) -> - begin match v.vtype with - | TArray (item_typ, _, _) -> - let item_typ_size_in_bytes = (bitsSizeOf item_typ) / 8 in - let item_typ_size_in_bytes = intdom_of_int item_typ_size_in_bytes in - begin match ctx.ask (Queries.EvalLength dest) with - | `Lifted arr_len -> - let arr_len_casted = ID.cast_to (Cilfacade.ptrdiff_ikind ()) arr_len in - begin - try `Lifted (ID.mul item_typ_size_in_bytes arr_len_casted) - with IntDomain.ArithmeticOnIntegerBot _ -> `Bot - end - | `Bot -> `Bot - | `Top -> `Top - end - | _ -> - let type_size_in_bytes = (bitsSizeOf v.vtype) / 8 in - `Lifted (intdom_of_int type_size_in_bytes) - end - | _ -> `Top - end - in - (* Map each points-to-set element to its size *) - let pts_sizes = List.map pts_elems_to_sizes pts_list in - (* Take the smallest of all sizes that ptr's contents may have *) - begin match pts_sizes with - | [] -> `Bot - | [x] -> x - | x::xs -> List.fold_left ValueDomainQueries.ID.join x xs - end - | _ -> - M.warn "Pointer %a has a points-to-set of top. An invalid memory access might occur" d_exp dest; - `Top - - (* Used for memset() and memcpy() out-of-bounds checks *) - let check_count ctx fun_name dest n = - let (behavior:MessageCategory.behavior) = Undefined MemoryOutOfBoundsAccess in - let cwe_number = 823 in - let dest_size = get_size_of_dest ctx dest in - let eval_n = ctx.ask (Queries.EvalInt n) in - match dest_size, eval_n with - | `Top, _ -> - AnalysisState.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest %a in function %s is unknown. Memory out-of-bounds access might occur" d_exp dest fun_name - | _, `Top -> - AnalysisState.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Count parameter, passed to function %s is unknown. Memory out-of-bounds access might occur" fun_name - | `Bot, _ -> - AnalysisState.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest %a in function %s is bottom. Memory out-of-bounds access might occur" d_exp dest fun_name - | _, `Bot -> - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Count parameter, passed to function %s is bottom" fun_name - | `Lifted ds, `Lifted en -> - let casted_ds = ID.cast_to (Cilfacade.ptrdiff_ikind ()) ds in - let casted_en = ID.cast_to (Cilfacade.ptrdiff_ikind ()) en in - let dest_size_lt_count = - begin - try ID.lt casted_ds casted_en - with IntDomain.ArithmeticOnIntegerBot _ -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () - end - in - begin match ID.to_bool dest_size_lt_count with - | Some true -> - AnalysisState.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest in function %s is %a (in bytes). Count is %a (in bytes). Memory out-of-bounds access may occur" fun_name ValueDomainQueries.ID.pretty dest_size ValueDomainQueries.ID.pretty eval_n - | Some false -> () - | None -> - AnalysisState.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Could not compare size of dest (%a) with count (%a) in function %s. Memory out-of-bounds access may occur" ID.pretty ds ID.pretty en fun_name - end - let special ctx (lv:lval option) (f: varinfo) (args: exp list) = let invalidate_ret_lv st = match lv with @@ -2187,8 +2094,6 @@ struct in let st = match desc.special args, f.vname with | Memset { dest; ch; count; }, _ -> - (* Check count *) - check_count ctx f.vname dest count; let eval_ch = eval_rv (Analyses.ask_of_ctx ctx) gs st ch in let dest_a, dest_typ = addr_type_of_exp dest in let value = @@ -2206,8 +2111,6 @@ struct let value = VD.zero_init_value dest_typ in set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value | Memcpy { dest = dst; src; n; }, _ -> - (* Check n *) - check_count ctx f.vname dst n; memory_copying dst src (* strcpy(dest, src); *) | Strcpy { dest = dst; src; n = None }, _ -> diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 94c16e9c94..ae0faedb9a 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -6,6 +6,7 @@ open MessageCategory module AS = AnalysisState module VDQ = ValueDomainQueries +module ID = IntDomain.IntDomTuple (* Note: @@ -27,11 +28,11 @@ struct (* HELPER FUNCTIONS *) let intdom_of_int x = - IntDomain.IntDomTuple.of_int (Cilfacade.ptrdiff_ikind ()) (Z.of_int x) + ID.of_int (Cilfacade.ptrdiff_ikind ()) (Z.of_int x) let to_index ?typ offs = let idx_of_int x = - IntDomain.IntDomTuple.of_int (Cilfacade.ptrdiff_ikind ()) (Z.of_int (x / 8)) + ID.of_int (Cilfacade.ptrdiff_ikind ()) (Z.of_int (x / 8)) in let rec offset_to_index_offset ?typ offs = match offs with | `NoOffset -> idx_of_int 0 @@ -40,7 +41,7 @@ struct let bits_offset, _size = GoblintCil.bitsOffset (TComp (field.fcomp, [])) field_as_offset in let bits_offset = idx_of_int bits_offset in let remaining_offset = offset_to_index_offset ~typ:field.ftype o in - IntDomain.IntDomTuple.add bits_offset remaining_offset + ID.add bits_offset remaining_offset | `Index (x, o) -> let (item_typ, item_size_in_bits) = match Option.map unrollType typ with @@ -48,11 +49,11 @@ struct let item_size_in_bits = bitsSizeOf item_typ in (Some item_typ, idx_of_int item_size_in_bits) | _ -> - (None, IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind ()) + (None, ID.top_of @@ Cilfacade.ptrdiff_ikind ()) in - let bits_offset = IntDomain.IntDomTuple.mul item_size_in_bits x in + let bits_offset = ID.mul item_size_in_bits x in let remaining_offset = offset_to_index_offset ?typ:item_typ o in - IntDomain.IntDomTuple.add bits_offset remaining_offset + ID.add bits_offset remaining_offset in offset_to_index_offset ?typ offs @@ -115,30 +116,33 @@ struct let item_typ_size_in_bytes = (bitsSizeOf item_typ) / 8 in let item_typ_size_in_bytes = intdom_of_int item_typ_size_in_bytes in begin match ctx.ask (Queries.EvalLength ptr) with - | `Lifted arr_len -> `Lifted (IntDomain.IntDomTuple.mul item_typ_size_in_bytes arr_len) - | `Bot -> VDQ.ID.bot () - | `Top -> VDQ.ID.top () + | `Lifted arr_len -> + let arr_len_casted = ID.cast_to (Cilfacade.ptrdiff_ikind ()) arr_len in + begin + try `Lifted (ID.mul item_typ_size_in_bytes arr_len_casted) + with IntDomain.ArithmeticOnIntegerBot _ -> `Bot + end + | `Bot -> `Bot + | `Top -> `Top end | _ -> let type_size_in_bytes = (bitsSizeOf v.vtype) / 8 in `Lifted (intdom_of_int type_size_in_bytes) end - | _ -> VDQ.ID.top () + | _ -> `Top end in (* Map each points-to-set element to its size *) let pts_sizes = List.map pts_elems_to_sizes pts_list in (* Take the smallest of all sizes that ptr's contents may have *) begin match pts_sizes with - | [] -> VDQ.ID.bot () + | [] -> `Bot | [x] -> x - | x::xs -> List.fold_left (fun acc elem -> - if VDQ.ID.compare acc elem >= 0 then elem else acc - ) x xs + | x::xs -> List.fold_left VDQ.ID.join x xs end | _ -> M.warn "Pointer %a has a points-to-set of top. An invalid memory access might occur" d_exp ptr; - VDQ.ID.top () + `Top let get_ptr_deref_type ptr_typ = match ptr_typ with @@ -155,7 +159,7 @@ struct let eval_offset = VDQ.ID.of_int (Cilfacade.ptrdiff_ikind ()) eval_offset in let ptr_contents_typ_size_in_bytes = size_of_type_in_bytes ptr_contents_typ in match eval_offset with - | `Lifted i -> `Lifted (IntDomain.IntDomTuple.mul i ptr_contents_typ_size_in_bytes) + | `Lifted i -> `Lifted (ID.mul i ptr_contents_typ_size_in_bytes) | `Top -> `Top | `Bot -> `Bot @@ -167,12 +171,12 @@ struct let bits_offset, _size = GoblintCil.bitsOffset (TComp (field.fcomp, [])) field_as_offset in let bytes_offset = intdom_of_int (bits_offset / 8) in let remaining_offset = offs_to_idx field.ftype o in - IntDomain.IntDomTuple.add bytes_offset remaining_offset + ID.add bytes_offset remaining_offset | `Index (x, o) -> let typ_size_in_bytes = size_of_type_in_bytes typ in - let bytes_offset = IntDomain.IntDomTuple.mul typ_size_in_bytes x in + let bytes_offset = ID.mul typ_size_in_bytes x in let remaining_offset = offs_to_idx typ o in - IntDomain.IntDomTuple.add bytes_offset remaining_offset + ID.add bytes_offset remaining_offset let rec get_addr_offs ctx ptr = match ctx.ask (Queries.MayPointTo ptr) with @@ -183,17 +187,17 @@ struct begin match VDQ.AD.is_empty a with | true -> M.warn "Pointer %a has an empty points-to-set" d_exp ptr; - IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind () + ID.top_of @@ Cilfacade.ptrdiff_ikind () | false -> if VDQ.AD.exists (function - | Addr (_, o) -> IntDomain.IntDomTuple.is_bot @@ offs_to_idx t o + | Addr (_, o) -> ID.is_bot @@ offs_to_idx t o | _ -> false ) a then ( (* TODO: Uncomment once staging-memsafety branch changes are applied *) (* set_mem_safety_flag InvalidDeref; *) M.warn "Pointer %a has a bot address offset. An invalid memory access may occur" d_exp ptr ) else if VDQ.AD.exists (function - | Addr (_, o) -> IntDomain.IntDomTuple.is_bot @@ offs_to_idx t o + | Addr (_, o) -> ID.is_bot @@ offs_to_idx t o | _ -> false ) a then ( (* TODO: Uncomment once staging-memsafety branch changes are applied *) @@ -204,16 +208,16 @@ struct (* Hence, we can just pick one element and obtain its offset *) begin match VDQ.AD.choose a with | Addr (_, o) -> offs_to_idx t o - | _ -> IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind () + | _ -> ID.top_of @@ Cilfacade.ptrdiff_ikind () end end | None -> M.error "Expression %a doesn't have pointer type" d_exp ptr; - IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind () + ID.top_of @@ Cilfacade.ptrdiff_ikind () end | _ -> M.warn "Pointer %a has a points-to-set of top. An invalid memory access might occur" d_exp ptr; - IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind () + ID.top_of @@ Cilfacade.ptrdiff_ikind () and check_lval_for_oob_access ctx ?(is_implicitly_derefed = false) lval = if not @@ lval_contains_a_ptr lval then () @@ -242,15 +246,30 @@ struct let ptr_contents_type = get_ptr_deref_type ptr_type in match ptr_contents_type with | Some t -> - begin match VDQ.ID.is_top ptr_size with - | true -> + begin match ptr_size, addr_offs with + | `Top, _ -> AS.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer %a not known. Memory out-of-bounds access might occur due to pointer arithmetic" d_exp lval_exp - | false -> - let offs = `Lifted addr_offs in - if ptr_size < offs then begin - AS.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer is %a (in bytes). It is offset by %a (in bytes) due to pointer arithmetic. Memory out-of-bounds access must occur" VDQ.ID.pretty ptr_size VDQ.ID.pretty offs + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer %a is top. Memory out-of-bounds access might occur due to pointer arithmetic" d_exp lval_exp + | `Bot, _ -> + AS.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer %a is bot. Memory out-of-bounds access might occur due to pointer arithmetic" d_exp lval_exp + | `Lifted ps, ao -> + let casted_ps = ID.cast_to (Cilfacade.ptrdiff_ikind ()) ps in + let casted_ao = ID.cast_to (Cilfacade.ptrdiff_ikind ()) ao in + let ptr_size_lt_offs = + begin + try ID.lt casted_ps casted_ao + with IntDomain.ArithmeticOnIntegerBot _ -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () + end + in + begin match ID.to_bool ptr_size_lt_offs with + | Some true -> + AnalysisState.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer is %a (in bytes). It is offset by %a (in bytes) due to pointer arithmetic. Memory out-of-bounds access must occur" ID.pretty casted_ps ID.pretty casted_ao + | Some false -> () + | None -> + AnalysisState.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Could not compare size of pointer (%a) (in bytes) with offset by (%a) (in bytes). Memory out-of-bounds access might occur" ID.pretty casted_ps ID.pretty casted_ao end end | _ -> M.error "Expression %a is not a pointer" d_exp lval_exp @@ -296,27 +315,87 @@ struct let offset_size = eval_ptr_offset_in_binop ctx e2 t in (* Make sure to add the address offset to the binop offset *) let offset_size_with_addr_size = match offset_size with - | `Lifted os -> `Lifted (IntDomain.IntDomTuple.add os addr_offs) + | `Lifted os -> + let casted_os = ID.cast_to (Cilfacade.ptrdiff_ikind ()) os in + let casted_ao = ID.cast_to (Cilfacade.ptrdiff_ikind ()) addr_offs in + `Lifted (ID.add casted_os casted_ao) | `Top -> `Top | `Bot -> `Bot in - begin match VDQ.ID.is_top ptr_size, VDQ.ID.is_top offset_size_with_addr_size with - | true, _ -> + begin match ptr_size, offset_size_with_addr_size with + | `Top, _ -> + AS.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer %a in expression %a is top. Memory out-of-bounds access might occur" d_exp e1 d_exp binopexp + | _, `Top -> AS.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer %a in expression %a not known. Memory out-of-bounds access might occur" d_exp e1 d_exp binopexp - | _, true -> + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Operand value for pointer arithmetic in expression %a is top. Memory out-of-bounds access might occur" d_exp binopexp + | `Bot, _ -> AS.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Operand value for pointer arithmetic in expression %a not known. Memory out-of-bounds access might occur" d_exp binopexp - | false, false -> - if ptr_size < offset_size_with_addr_size then begin - AS.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer in expression %a is %a (in bytes). It is offset by %a (in bytes). Memory out-of-bounds access must occur" d_exp binopexp VDQ.ID.pretty ptr_size VDQ.ID.pretty offset_size_with_addr_size + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer %a in expression %a is bottom. Memory out-of-bounds access might occur" d_exp e1 d_exp binopexp + | _, `Bot -> + AS.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Operand value for pointer arithmetic in expression %a is bottom. Memory out-of-bounds access might occur" d_exp binopexp + | `Lifted ps, `Lifted o -> + let casted_ps = ID.cast_to (Cilfacade.ptrdiff_ikind ()) ps in + let casted_o = ID.cast_to (Cilfacade.ptrdiff_ikind ()) o in + let ptr_size_lt_offs = + begin + try ID.lt casted_ps casted_o + with IntDomain.ArithmeticOnIntegerBot _ -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () + end + in + begin match ID.to_bool ptr_size_lt_offs with + | Some true -> + AS.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer in expression %a is %a (in bytes). It is offset by %a (in bytes). Memory out-of-bounds access must occur" d_exp binopexp ID.pretty casted_ps ID.pretty casted_o + | Some false -> () + | None -> + AnalysisState.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Could not compare pointer size (%a) with offset (%a). Memory out-of-bounds access may occur" ID.pretty casted_ps ID.pretty casted_o end end | _ -> M.error "Binary expression %a doesn't have a pointer" d_exp binopexp end | _ -> () + let check_count ctx fun_name dest n = + let (behavior:MessageCategory.behavior) = Undefined MemoryOutOfBoundsAccess in + let cwe_number = 823 in + let dest_size = get_size_of_ptr_target ctx dest in + let eval_n = ctx.ask (Queries.EvalInt n) in + let addr_offs = get_addr_offs ctx dest in + match dest_size, eval_n with + | `Top, _ -> + AnalysisState.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest %a in function %s is unknown. Memory out-of-bounds access might occur" d_exp dest fun_name + | _, `Top -> + AnalysisState.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Count parameter, passed to function %s is unknown. Memory out-of-bounds access might occur" fun_name + | `Bot, _ -> + AnalysisState.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest %a in function %s is bottom. Memory out-of-bounds access might occur" d_exp dest fun_name + | _, `Bot -> + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Count parameter, passed to function %s is bottom" fun_name + | `Lifted ds, `Lifted en -> + let casted_ds = ID.cast_to (Cilfacade.ptrdiff_ikind ()) ds in + let casted_en = ID.cast_to (Cilfacade.ptrdiff_ikind ()) en in + let casted_ao = ID.cast_to (Cilfacade.ptrdiff_ikind ()) addr_offs in + let dest_size_lt_count = + begin + try ID.lt casted_ds (ID.add casted_en casted_ao) + with IntDomain.ArithmeticOnIntegerBot _ -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () + end + in + begin match ID.to_bool dest_size_lt_count with + | Some true -> + AnalysisState.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest in function %s is %a (in bytes) with an address offset of %a (in bytes). Count is %a (in bytes). Memory out-of-bounds access may occur" fun_name ID.pretty casted_ds ID.pretty casted_ao ID.pretty casted_en + | Some false -> () + | None -> + AnalysisState.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Could not compare size of dest (%a) with address offset (%a) count (%a) in function %s. Memory out-of-bounds access may occur" ID.pretty casted_ds ID.pretty casted_ao ID.pretty casted_en fun_name + end + (* TRANSFER FUNCTIONS *) @@ -344,6 +423,11 @@ struct in Option.iter (fun x -> check_lval_for_oob_access ctx x) lval; List.iter (fun arg -> check_exp_for_oob_access ctx ~is_implicitly_derefed:(is_arg_implicitly_derefed arg) arg) arglist; + (* Check calls to memset and memcpy for out-of-bounds-accesses *) + match desc.special arglist with + | Memset { dest; ch; count; } -> check_count ctx f.vname dest count; + | Memcpy { dest; src; n = count; } -> check_count ctx f.vname dest count; + | _ -> (); ctx.local let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = From 8bf3705240f1ae479f71f4eaf9346c87768d856b Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 1 Oct 2023 17:16:14 +0200 Subject: [PATCH 0734/1312] Remove unused function --- src/analyses/memOutOfBounds.ml | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index ae0faedb9a..f9a0439333 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -30,33 +30,6 @@ struct let intdom_of_int x = ID.of_int (Cilfacade.ptrdiff_ikind ()) (Z.of_int x) - let to_index ?typ offs = - let idx_of_int x = - ID.of_int (Cilfacade.ptrdiff_ikind ()) (Z.of_int (x / 8)) - in - let rec offset_to_index_offset ?typ offs = match offs with - | `NoOffset -> idx_of_int 0 - | `Field (field, o) -> - let field_as_offset = Field (field, NoOffset) in - let bits_offset, _size = GoblintCil.bitsOffset (TComp (field.fcomp, [])) field_as_offset in - let bits_offset = idx_of_int bits_offset in - let remaining_offset = offset_to_index_offset ~typ:field.ftype o in - ID.add bits_offset remaining_offset - | `Index (x, o) -> - let (item_typ, item_size_in_bits) = - match Option.map unrollType typ with - | Some TArray(item_typ, _, _) -> - let item_size_in_bits = bitsSizeOf item_typ in - (Some item_typ, idx_of_int item_size_in_bits) - | _ -> - (None, ID.top_of @@ Cilfacade.ptrdiff_ikind ()) - in - let bits_offset = ID.mul item_size_in_bits x in - let remaining_offset = offset_to_index_offset ?typ:item_typ o in - ID.add bits_offset remaining_offset - in - offset_to_index_offset ?typ offs - let rec exp_contains_a_ptr (exp:exp) = match exp with | Const _ From 30279106115eed26c94a2b84bc5cf14d153c3400 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 1 Oct 2023 17:42:51 +0200 Subject: [PATCH 0735/1312] Add further exception handling --- src/analyses/memOutOfBounds.ml | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index f9a0439333..f2b2780f58 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -128,11 +128,14 @@ struct let eval_ptr_offset_in_binop ctx exp ptr_contents_typ = let eval_offset = ctx.ask (Queries.EvalInt exp) in - let eval_offset = Option.get @@ VDQ.ID.to_int eval_offset in - let eval_offset = VDQ.ID.of_int (Cilfacade.ptrdiff_ikind ()) eval_offset in let ptr_contents_typ_size_in_bytes = size_of_type_in_bytes ptr_contents_typ in match eval_offset with - | `Lifted i -> `Lifted (ID.mul i ptr_contents_typ_size_in_bytes) + | `Lifted eo -> + let casted_eo = ID.cast_to (Cilfacade.ptrdiff_ikind ()) eo in + begin + try `Lifted (ID.mul casted_eo ptr_contents_typ_size_in_bytes) + with IntDomain.ArithmeticOnIntegerBot _ -> `Bot + end | `Top -> `Top | `Bot -> `Bot @@ -144,12 +147,18 @@ struct let bits_offset, _size = GoblintCil.bitsOffset (TComp (field.fcomp, [])) field_as_offset in let bytes_offset = intdom_of_int (bits_offset / 8) in let remaining_offset = offs_to_idx field.ftype o in - ID.add bytes_offset remaining_offset + begin + try ID.add bytes_offset remaining_offset + with IntDomain.ArithmeticOnIntegerBot _ -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () + end | `Index (x, o) -> - let typ_size_in_bytes = size_of_type_in_bytes typ in - let bytes_offset = ID.mul typ_size_in_bytes x in - let remaining_offset = offs_to_idx typ o in - ID.add bytes_offset remaining_offset + begin try + let typ_size_in_bytes = size_of_type_in_bytes typ in + let bytes_offset = ID.mul typ_size_in_bytes x in + let remaining_offset = offs_to_idx typ o in + ID.add bytes_offset remaining_offset + with IntDomain.ArithmeticOnIntegerBot _ -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () + end let rec get_addr_offs ctx ptr = match ctx.ask (Queries.MayPointTo ptr) with @@ -291,7 +300,10 @@ struct | `Lifted os -> let casted_os = ID.cast_to (Cilfacade.ptrdiff_ikind ()) os in let casted_ao = ID.cast_to (Cilfacade.ptrdiff_ikind ()) addr_offs in - `Lifted (ID.add casted_os casted_ao) + begin + try `Lifted (ID.add casted_os casted_ao) + with IntDomain.ArithmeticOnIntegerBot _ -> `Bot + end | `Top -> `Top | `Bot -> `Bot in @@ -331,6 +343,7 @@ struct end | _ -> () + (* For memset() and memcpy() *) let check_count ctx fun_name dest n = let (behavior:MessageCategory.behavior) = Undefined MemoryOutOfBoundsAccess in let cwe_number = 823 in From 62b20a2839a4df6b24b1e2908893e51daa2e2e7b Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 1 Oct 2023 17:43:49 +0200 Subject: [PATCH 0736/1312] Add further memset/memcpy test --- .../77-mem-oob/09-memset-memcpy-addr-offs.c | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 tests/regression/77-mem-oob/09-memset-memcpy-addr-offs.c diff --git a/tests/regression/77-mem-oob/09-memset-memcpy-addr-offs.c b/tests/regression/77-mem-oob/09-memset-memcpy-addr-offs.c new file mode 100644 index 0000000000..725024946e --- /dev/null +++ b/tests/regression/77-mem-oob/09-memset-memcpy-addr-offs.c @@ -0,0 +1,20 @@ +// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval --disable warn.info +// TODO: The "--disable warn.info" part is a temporary fix and needs to be removed once the MacOS CI job is fixed +#include +#include + +int main(int argc, char const *argv[]) { + int *a = malloc(10 * sizeof(int)); //Size is 40 bytes, assuming a 4-byte int + int *b = malloc(15 * sizeof(int)); //Size is 60 bytes, assuming a 4-byte int + + memset(a, 0, 40); //NOWARN + memcpy(a, b, 40); //NOWARN + + a += 3; + + memset(a, 0, 40); //WARN + memcpy(a, b, 40); //WARN + + memset(a, 0, 37); //NOWARN + memcpy(a, b, 37); //NOWARN +} \ No newline at end of file From 5e683d45c059370eb2fd496073378bcb6232b015 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 1 Oct 2023 17:46:14 +0200 Subject: [PATCH 0737/1312] Fix comment --- src/analyses/memOutOfBounds.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index f2b2780f58..fe4b854923 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -375,7 +375,7 @@ struct begin match ID.to_bool dest_size_lt_count with | Some true -> AnalysisState.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest in function %s is %a (in bytes) with an address offset of %a (in bytes). Count is %a (in bytes). Memory out-of-bounds access may occur" fun_name ID.pretty casted_ds ID.pretty casted_ao ID.pretty casted_en + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest in function %s is %a (in bytes) with an address offset of %a (in bytes). Count is %a (in bytes). Memory out-of-bounds access must occur" fun_name ID.pretty casted_ds ID.pretty casted_ao ID.pretty casted_en | Some false -> () | None -> AnalysisState.svcomp_may_invalid_deref := true; From f03f81ff2ba28715e677c49507b2edea168f3b37 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 1 Oct 2023 18:16:00 +0200 Subject: [PATCH 0738/1312] Remove some bot checks for ID arithmetic --- src/analyses/memOutOfBounds.ml | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index fe4b854923..45048acc93 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -238,12 +238,7 @@ struct | `Lifted ps, ao -> let casted_ps = ID.cast_to (Cilfacade.ptrdiff_ikind ()) ps in let casted_ao = ID.cast_to (Cilfacade.ptrdiff_ikind ()) ao in - let ptr_size_lt_offs = - begin - try ID.lt casted_ps casted_ao - with IntDomain.ArithmeticOnIntegerBot _ -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () - end - in + let ptr_size_lt_offs = ID.lt casted_ps casted_ao in begin match ID.to_bool ptr_size_lt_offs with | Some true -> AnalysisState.svcomp_may_invalid_deref := true; @@ -323,12 +318,7 @@ struct | `Lifted ps, `Lifted o -> let casted_ps = ID.cast_to (Cilfacade.ptrdiff_ikind ()) ps in let casted_o = ID.cast_to (Cilfacade.ptrdiff_ikind ()) o in - let ptr_size_lt_offs = - begin - try ID.lt casted_ps casted_o - with IntDomain.ArithmeticOnIntegerBot _ -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () - end - in + let ptr_size_lt_offs = ID.lt casted_ps casted_o in begin match ID.to_bool ptr_size_lt_offs with | Some true -> AS.svcomp_may_invalid_deref := true; @@ -366,12 +356,7 @@ struct let casted_ds = ID.cast_to (Cilfacade.ptrdiff_ikind ()) ds in let casted_en = ID.cast_to (Cilfacade.ptrdiff_ikind ()) en in let casted_ao = ID.cast_to (Cilfacade.ptrdiff_ikind ()) addr_offs in - let dest_size_lt_count = - begin - try ID.lt casted_ds (ID.add casted_en casted_ao) - with IntDomain.ArithmeticOnIntegerBot _ -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () - end - in + let dest_size_lt_count = ID.lt casted_ds (ID.add casted_en casted_ao) in begin match ID.to_bool dest_size_lt_count with | Some true -> AnalysisState.svcomp_may_invalid_deref := true; From 3da92055ae48f55f827aed5e4f8eae0b2102e009 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 1 Oct 2023 18:33:38 +0200 Subject: [PATCH 0739/1312] Use size_of_type_in_bytes where possible --- src/analyses/memOutOfBounds.ml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 45048acc93..7015e6f143 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -30,6 +30,10 @@ struct let intdom_of_int x = ID.of_int (Cilfacade.ptrdiff_ikind ()) (Z.of_int x) + let size_of_type_in_bytes typ = + let typ_size_in_bytes = (bitsSizeOf typ) / 8 in + intdom_of_int typ_size_in_bytes + let rec exp_contains_a_ptr (exp:exp) = match exp with | Const _ @@ -86,8 +90,7 @@ struct | Addr (v, _) -> begin match v.vtype with | TArray (item_typ, _, _) -> - let item_typ_size_in_bytes = (bitsSizeOf item_typ) / 8 in - let item_typ_size_in_bytes = intdom_of_int item_typ_size_in_bytes in + let item_typ_size_in_bytes = size_of_type_in_bytes item_typ in begin match ctx.ask (Queries.EvalLength ptr) with | `Lifted arr_len -> let arr_len_casted = ID.cast_to (Cilfacade.ptrdiff_ikind ()) arr_len in @@ -99,8 +102,8 @@ struct | `Top -> `Top end | _ -> - let type_size_in_bytes = (bitsSizeOf v.vtype) / 8 in - `Lifted (intdom_of_int type_size_in_bytes) + let type_size_in_bytes = size_of_type_in_bytes v.vtype in + `Lifted type_size_in_bytes end | _ -> `Top end @@ -122,10 +125,6 @@ struct | TPtr (t, _) -> Some t | _ -> None - let size_of_type_in_bytes typ = - let typ_size_in_bytes = (bitsSizeOf typ) / 8 in - intdom_of_int typ_size_in_bytes - let eval_ptr_offset_in_binop ctx exp ptr_contents_typ = let eval_offset = ctx.ask (Queries.EvalInt exp) in let ptr_contents_typ_size_in_bytes = size_of_type_in_bytes ptr_contents_typ in From 89abb1a9ade5352f19fc2c042c4ef2b387c5fa38 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 1 Oct 2023 20:56:16 +0200 Subject: [PATCH 0740/1312] Keep TODO about count of memset in base.ml --- src/analyses/base.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 57c32fa3bc..232ee36d68 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2094,6 +2094,7 @@ struct in let st = match desc.special args, f.vname with | Memset { dest; ch; count; }, _ -> + (* TODO: check count *) let eval_ch = eval_rv (Analyses.ask_of_ctx ctx) gs st ch in let dest_a, dest_typ = addr_type_of_exp dest in let value = From dee2a60ba7855321f2d67513ca97be8cdbe3320d Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Mon, 2 Oct 2023 01:02:38 +0200 Subject: [PATCH 0741/1312] Set SV-COMP memory safety global flags at all necessary locations --- src/analyses/base.ml | 10 +++++--- src/analyses/memLeak.ml | 4 ++-- src/analyses/memOutOfBounds.ml | 42 +++++++++++++++++----------------- src/analyses/useAfterFree.ml | 6 +++-- 4 files changed, 34 insertions(+), 28 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index d9cfba7b18..a624fd0b40 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2027,12 +2027,16 @@ struct in match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local ptr with | Address a -> - if AD.is_top a then + if AD.is_top a then ( + AnalysisStateUtil.set_mem_safety_flag InvalidFree; M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Points-to set for pointer %a in function %s is top. Potentially invalid memory deallocation may occur" d_exp ptr special_fn.vname - else if has_non_heap_var a then + ) else if has_non_heap_var a then ( + AnalysisStateUtil.set_mem_safety_flag InvalidFree; M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Free of non-dynamically allocated memory in function %s for pointer %a" special_fn.vname d_exp ptr - else if has_non_zero_offset a then + ) else if has_non_zero_offset a then ( + AnalysisStateUtil.set_mem_safety_flag InvalidFree; M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 761] "Free of memory not at start of buffer in function %s for pointer %a" special_fn.vname d_exp ptr + ) | _ -> M.warn ~category:MessageCategory.Analyzer "Pointer %a in function %s doesn't evaluate to a valid address." d_exp ptr special_fn.vname diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index 56cbbbff3d..eb38b97a1c 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -30,10 +30,10 @@ struct if not @@ D.is_empty state then match assert_exp_imprecise, exp with | true, Some exp -> - AnalysisStateUtil.set_mem_safety_flag InvalidMemTrack; + set_mem_safety_flag InvalidMemTrack; M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "assert expression %a is unknown. Memory leak might possibly occur for heap variables: %a" d_exp exp D.pretty state | _ -> - AnalysisStateUtil.set_mem_safety_flag InvalidMemTrack; + set_mem_safety_flag InvalidMemTrack; M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory leak detected for heap variables: %a" D.pretty state (* TRANSFER FUNCTIONS *) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 9ee022fe08..db86a63414 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -118,6 +118,7 @@ struct | x::xs -> List.fold_left VDQ.ID.join x xs end | _ -> + set_mem_safety_flag InvalidDeref; M.warn "Pointer %a has a points-to-set of top. An invalid memory access might occur" d_exp ptr; `Top @@ -172,7 +173,7 @@ struct | _ -> true in if may_contain_unknown_addr then begin - (* set_mem_safety_flag InvalidDeref; *) + set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior (Undefined Other)) "Pointer %a contains an unknown address. Invalid dereference may occur" d_exp ptr end @@ -201,15 +202,13 @@ struct | Addr (_, o) -> ID.is_bot @@ offs_to_idx t o | _ -> false ) a then ( - (* TODO: Uncomment once staging-memsafety branch changes are applied *) - (* set_mem_safety_flag InvalidDeref; *) + set_mem_safety_flag InvalidDeref; M.warn "Pointer %a has a bot address offset. An invalid memory access may occur" d_exp ptr ) else if VDQ.AD.exists (function | Addr (_, o) -> ID.is_bot @@ offs_to_idx t o | _ -> false ) a then ( - (* TODO: Uncomment once staging-memsafety branch changes are applied *) - (* set_mem_safety_flag InvalidDeref; *) + set_mem_safety_flag InvalidDeref; M.warn "Pointer %a has a top address offset. An invalid memory access may occur" d_exp ptr ); (* Offset should be the same for all elements in the points-to set *) @@ -224,7 +223,7 @@ struct ID.top_of @@ Cilfacade.ptrdiff_ikind () end | _ -> - (* set_mem_safety_flag InvalidDeref; *) + set_mem_safety_flag InvalidDeref; M.warn "Pointer %a has a points-to-set of top. An invalid memory access might occur" d_exp ptr; ID.top_of @@ Cilfacade.ptrdiff_ikind () @@ -259,10 +258,10 @@ struct | Some t -> begin match ptr_size, addr_offs with | `Top, _ -> - AS.svcomp_may_invalid_deref := true; + set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer %a is top. Memory out-of-bounds access might occur due to pointer arithmetic" d_exp lval_exp | `Bot, _ -> - AS.svcomp_may_invalid_deref := true; + set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer %a is bot. Memory out-of-bounds access might occur due to pointer arithmetic" d_exp lval_exp | `Lifted ps, ao -> let casted_ps = ID.cast_to (Cilfacade.ptrdiff_ikind ()) ps in @@ -270,11 +269,11 @@ struct let ptr_size_lt_offs = ID.lt casted_ps casted_ao in begin match ID.to_bool ptr_size_lt_offs with | Some true -> - AnalysisState.svcomp_may_invalid_deref := true; + set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer is %a (in bytes). It is offset by %a (in bytes) due to pointer arithmetic. Memory out-of-bounds access must occur" ID.pretty casted_ps ID.pretty casted_ao | Some false -> () | None -> - AnalysisState.svcomp_may_invalid_deref := true; + set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Could not compare size of pointer (%a) (in bytes) with offset by (%a) (in bytes). Memory out-of-bounds access might occur" ID.pretty casted_ps ID.pretty casted_ao end end @@ -334,16 +333,16 @@ struct in begin match ptr_size, offset_size_with_addr_size with | `Top, _ -> - AS.svcomp_may_invalid_deref := true; + set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer %a in expression %a is top. Memory out-of-bounds access might occur" d_exp e1 d_exp binopexp | _, `Top -> - AS.svcomp_may_invalid_deref := true; + set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Operand value for pointer arithmetic in expression %a is top. Memory out-of-bounds access might occur" d_exp binopexp | `Bot, _ -> - AS.svcomp_may_invalid_deref := true; + set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer %a in expression %a is bottom. Memory out-of-bounds access might occur" d_exp e1 d_exp binopexp | _, `Bot -> - AS.svcomp_may_invalid_deref := true; + set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Operand value for pointer arithmetic in expression %a is bottom. Memory out-of-bounds access might occur" d_exp binopexp | `Lifted ps, `Lifted o -> let casted_ps = ID.cast_to (Cilfacade.ptrdiff_ikind ()) ps in @@ -351,11 +350,11 @@ struct let ptr_size_lt_offs = ID.lt casted_ps casted_o in begin match ID.to_bool ptr_size_lt_offs with | Some true -> - AS.svcomp_may_invalid_deref := true; + set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer in expression %a is %a (in bytes). It is offset by %a (in bytes). Memory out-of-bounds access must occur" d_exp binopexp ID.pretty casted_ps ID.pretty casted_o | Some false -> () | None -> - AnalysisState.svcomp_may_invalid_deref := true; + set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Could not compare pointer size (%a) with offset (%a). Memory out-of-bounds access may occur" ID.pretty casted_ps ID.pretty casted_o end end @@ -372,15 +371,16 @@ struct let addr_offs = get_addr_offs ctx dest in match dest_size, eval_n with | `Top, _ -> - AnalysisState.svcomp_may_invalid_deref := true; + set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest %a in function %s is unknown. Memory out-of-bounds access might occur" d_exp dest fun_name | _, `Top -> - AnalysisState.svcomp_may_invalid_deref := true; + set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Count parameter, passed to function %s is unknown. Memory out-of-bounds access might occur" fun_name | `Bot, _ -> - AnalysisState.svcomp_may_invalid_deref := true; + set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest %a in function %s is bottom. Memory out-of-bounds access might occur" d_exp dest fun_name | _, `Bot -> + set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Count parameter, passed to function %s is bottom" fun_name | `Lifted ds, `Lifted en -> let casted_ds = ID.cast_to (Cilfacade.ptrdiff_ikind ()) ds in @@ -389,11 +389,11 @@ struct let dest_size_lt_count = ID.lt casted_ds (ID.add casted_en casted_ao) in begin match ID.to_bool dest_size_lt_count with | Some true -> - AnalysisState.svcomp_may_invalid_deref := true; + set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest in function %s is %a (in bytes) with an address offset of %a (in bytes). Count is %a (in bytes). Memory out-of-bounds access must occur" fun_name ID.pretty casted_ds ID.pretty casted_ao ID.pretty casted_en | Some false -> () | None -> - AnalysisState.svcomp_may_invalid_deref := true; + set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Could not compare size of dest (%a) with address offset (%a) count (%a) in function %s. Memory out-of-bounds access may occur" ID.pretty casted_ds ID.pretty casted_ao ID.pretty casted_en fun_name end diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index dfbab87a29..2f4b54f000 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -76,7 +76,7 @@ struct M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Current thread is not unique and a %s might occur for heap variable %a" bug_name CilType.Varinfo.pretty heap_var end else if HeapVars.mem heap_var (snd ctx.local) then begin - set_global_svcomp_var is_double_free; + if is_double_free then set_mem_safety_flag InvalidFree else set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "%s might occur in current unique thread %a for heap variable %a" bug_name ThreadIdDomain.FlagConfiguredTID.pretty current CilType.Varinfo.pretty heap_var end end @@ -110,8 +110,10 @@ struct begin match ctx.ask (Queries.MayPointTo lval_to_query) with | ad when not (Queries.AD.is_top ad) -> let warn_for_heap_var v = - if HeapVars.mem v (snd state) then + if HeapVars.mem v (snd state) then begin + if is_double_free then set_mem_safety_flag InvalidFree else set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "lval (%s) in \"%s\" points to a maybe freed memory region" v.vname transfer_fn_name + end in let pointed_to_heap_vars = Queries.AD.fold (fun addr vars -> From da45e40b56d3a049656cfb2993b462e6b797c5ac Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Mon, 2 Oct 2023 01:05:18 +0200 Subject: [PATCH 0742/1312] Clean up UAF analysis a bit --- src/analyses/useAfterFree.ml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index 2f4b54f000..a28591e273 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -15,7 +15,7 @@ module ThreadIdToJoinedThreadsMap = MapDomain.MapBot(ThreadIdDomain.ThreadLifted module Spec : Analyses.MCPSpec = struct - include Analyses.DefaultSpec + include Analyses.IdentitySpec let name () = "useAfterFree" @@ -24,7 +24,6 @@ struct module G = ThreadIdToJoinedThreadsMap module V = VarinfoV - (** TODO: Try out later in benchmarks to see how we perform with and without context-sensititivty *) let context _ _ = () @@ -176,9 +175,6 @@ struct warn_exp_might_contain_freed "branch" ctx exp; ctx.local - let body ctx (f:fundec) : D.t = - ctx.local - let return ctx (exp:exp option) (f:fundec) : D.t = Option.iter (fun x -> warn_exp_might_contain_freed "return" ctx x) exp; ctx.local @@ -248,9 +244,6 @@ struct end | _ -> state - let threadenter ctx lval f args = [ctx.local] - let threadspawn ctx lval f args fctx = ctx.local - let startstate v = D.bot () let exitstate v = D.top () From 15e6c3d636bde630a5df20d31af2c812355b68a2 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 2 Oct 2023 10:37:21 +0300 Subject: [PATCH 0743/1312] Simplify is_not_heap_alloc_var and add TODO --- src/analyses/base.ml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 6442ed4f8a..ea595ad96d 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1123,7 +1123,8 @@ struct (* interpreter end *) let is_not_heap_alloc_var ctx v = - (not (ctx.ask (Queries.IsAllocVar v))) || (ctx.ask (Queries.IsAllocVar v) && not (ctx.ask (Queries.IsHeapVar v))) + let is_alloc = ctx.ask (Queries.IsAllocVar v) in + not is_alloc || (is_alloc && not (ctx.ask (Queries.IsHeapVar v))) let query_invariant ctx context = let cpa = ctx.local.BaseDomain.cpa in @@ -2114,7 +2115,7 @@ struct let dest_a, dest_typ = addr_type_of_exp dest in let value = VD.zero_init_value dest_typ in set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value - | Memcpy { dest = dst; src; n; }, _ -> + | Memcpy { dest = dst; src; n; }, _ -> (* TODO: use n *) memory_copying dst src (* strcpy(dest, src); *) | Strcpy { dest = dst; src; n = None }, _ -> From 1335123d60b556e9759a4e4147d72095142a9452 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Mon, 2 Oct 2023 09:45:49 +0200 Subject: [PATCH 0744/1312] Recognize mem-safety props in any order --- src/witness/svcompSpec.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/witness/svcompSpec.ml b/src/witness/svcompSpec.ml index f066610953..fbe206dabb 100644 --- a/src/witness/svcompSpec.ml +++ b/src/witness/svcompSpec.ml @@ -32,7 +32,8 @@ let of_string s = let global1 = Str.matched_group 1 s in let global2 = Str.matched_group 2 s in let global3 = Str.matched_group 3 s in - if global1 = "valid-free" && global2 = "valid-deref" && global3 = "valid-memtrack" then + let mem_safety_props = ["valid-free"; "valid-deref"; "valid-memtrack";] in + if (global1 <> global2 && global1 <> global3 && global2 <> global3) && List.for_all (fun x -> List.mem x mem_safety_props) [global1; global2; global3] then MemorySafety (* if global = "valid-free" then ValidFree From 1384f733d812996399648b4a846389fcbac29afb Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Mon, 2 Oct 2023 09:57:38 +0200 Subject: [PATCH 0745/1312] Add support for SV-COMP's valid-memcleanup property --- src/autoTune.ml | 1 + src/framework/analysisState.ml | 3 +++ src/util/analysisStateUtil.ml | 4 +++- src/witness/svcomp.ml | 1 + src/witness/svcompSpec.ml | 13 +++++++++++-- src/witness/witness.ml | 30 ++++++++++++++++++++++++++++++ 6 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/autoTune.ml b/src/autoTune.ml index 9e3508ccd2..ac7c150546 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -227,6 +227,7 @@ let focusOnSpecification () = | ValidDeref | ValidMemtrack -> () | MemorySafety -> () (* TODO: This is here for now just to complete the pattern match *) + | ValidMemcleanup -> () (*Detect enumerations and enable the "ana.int.enums" option*) exception EnumFound diff --git a/src/framework/analysisState.ml b/src/framework/analysisState.ml index ca619d4dfb..1416f99f69 100644 --- a/src/framework/analysisState.ml +++ b/src/framework/analysisState.ml @@ -16,6 +16,9 @@ let svcomp_may_invalid_deref = ref false (** Whether an invalid memtrack happened *) let svcomp_may_invalid_memtrack = ref false +(** Whether an invalid memcleanup happened *) +let svcomp_may_invalid_memcleanup = ref false + (** A hack to see if we are currently doing global inits *) let global_initialization = ref false diff --git a/src/util/analysisStateUtil.ml b/src/util/analysisStateUtil.ml index 25914f8c8e..a34be33f18 100644 --- a/src/util/analysisStateUtil.ml +++ b/src/util/analysisStateUtil.ml @@ -2,10 +2,12 @@ type mem_safety_violation = | InvalidFree | InvalidDeref | InvalidMemTrack + | InvalidMemcleanup let set_mem_safety_flag violation_type = if !AnalysisState.postsolving then match violation_type with | InvalidFree -> AnalysisState.svcomp_may_invalid_free := true | InvalidDeref -> AnalysisState.svcomp_may_invalid_deref := true - | InvalidMemTrack -> AnalysisState.svcomp_may_invalid_memtrack := true \ No newline at end of file + | InvalidMemTrack -> AnalysisState.svcomp_may_invalid_memtrack := true + | InvalidMemcleanup -> AnalysisState.svcomp_may_invalid_memcleanup := true \ No newline at end of file diff --git a/src/witness/svcomp.ml b/src/witness/svcomp.ml index 15d41c0210..22543d48a9 100644 --- a/src/witness/svcomp.ml +++ b/src/witness/svcomp.ml @@ -56,6 +56,7 @@ struct | ValidDeref -> "valid-deref" | ValidMemtrack -> "valid-memtrack" | MemorySafety -> "memory-safety" (* TODO: Currently here only to complete the pattern match *) + | ValidMemcleanup -> "valid-memcleanup" in "false(" ^ result_spec ^ ")" | Unknown -> "unknown" diff --git a/src/witness/svcompSpec.ml b/src/witness/svcompSpec.ml index fbe206dabb..25a9f522bc 100644 --- a/src/witness/svcompSpec.ml +++ b/src/witness/svcompSpec.ml @@ -10,10 +10,12 @@ type t = | ValidDeref | ValidMemtrack | MemorySafety (* Internal property for use in Goblint; serves as a summary for ValidFree, ValidDeref and ValidMemtrack *) + | ValidMemcleanup let of_string s = let s = String.strip s in - let regexp = Str.regexp "CHECK( init(main()), LTL(G \\(.*\\)) )\nCHECK( init(main()), LTL(G \\(.*\\)) )\nCHECK( init(main()), LTL(G \\(.*\\)) )" in + let regexp_multiple = Str.regexp "CHECK( init(main()), LTL(G \\(.*\\)) )\nCHECK( init(main()), LTL(G \\(.*\\)) )\nCHECK( init(main()), LTL(G \\(.*\\)) )" in + let regexp_single = Str.regexp "CHECK( init(main()), LTL(G \\(.*\\)) )" in let regexp_negated = Str.regexp "CHECK( init(main()), LTL(G ! \\(.*\\)) )" in if Str.string_match regexp_negated s 0 then let global_not = Str.matched_group 1 s in @@ -28,7 +30,7 @@ let of_string s = UnreachCall f else failwith "Svcomp.Specification.of_string: unknown global not expression" - else if Str.string_match regexp s 0 then + else if Str.string_match regexp_multiple s 0 then let global1 = Str.matched_group 1 s in let global2 = Str.matched_group 2 s in let global3 = Str.matched_group 3 s in @@ -43,6 +45,12 @@ let of_string s = ValidMemtrack *) else failwith "Svcomp.Specification.of_string: unknown global expression" + else if Str.string_match regexp_single s 0 then + let global = Str.matched_group 1 s in + if global = "valid-memcleanup" then + ValidMemcleanup + else + failwith "Svcomp.Specification.of_string: unknown global expression" else failwith "Svcomp.Specification.of_string: unknown expression" @@ -72,5 +80,6 @@ let to_string spec = | ValidDeref -> "valid-deref", false | ValidMemtrack -> "valid-memtrack", false | MemorySafety -> "memory-safety", false (* TODO: That's false, it's currently here just to complete the pattern match *) + | ValidMemcleanup -> "valid-memcleanup", false in print_output spec_str is_neg diff --git a/src/witness/witness.ml b/src/witness/witness.ml index 17dd14472e..35d932210d 100644 --- a/src/witness/witness.ml +++ b/src/witness/witness.ml @@ -625,6 +625,36 @@ struct in (module TaskResult:WitnessTaskResult) ) + | ValidMemcleanup -> + let module TrivialArg = + struct + include Arg + let next _ = [] + end + in + if not !AnalysisState.svcomp_may_invalid_memcleanup then ( + let module TaskResult = + struct + module Arg = Arg + let result = Result.True + let invariant _ = Invariant.none + let is_violation _ = false + let is_sink _ = false + end + in + (module TaskResult:WitnessTaskResult) + ) else ( + let module TaskResult = + struct + module Arg = TrivialArg + let result = Result.Unknown + let invariant _ = Invariant.none + let is_violation _ = false + let is_sink _ = false + end + in + (module TaskResult:WitnessTaskResult) + ) let write entrystates = From 4e70422c2c305de6408ef68177dddb1f3c9e9833 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Mon, 2 Oct 2023 09:59:08 +0200 Subject: [PATCH 0746/1312] Set SV-COMP global flag for invalid-memcleanup --- src/analyses/memLeak.ml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index eb38b97a1c..dbaa2d69fc 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -22,6 +22,7 @@ struct let warn_for_multi_threaded ctx = if not (ctx.ask (Queries.MustBeSingleThreaded { since_start = true })) then ( set_mem_safety_flag InvalidMemTrack; + set_mem_safety_flag InvalidMemcleanup; M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Program isn't running in single-threaded mode. A memory leak might occur due to multi-threading" ) @@ -31,9 +32,11 @@ struct match assert_exp_imprecise, exp with | true, Some exp -> set_mem_safety_flag InvalidMemTrack; + set_mem_safety_flag InvalidMemcleanup; M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "assert expression %a is unknown. Memory leak might possibly occur for heap variables: %a" d_exp exp D.pretty state | _ -> set_mem_safety_flag InvalidMemTrack; + set_mem_safety_flag InvalidMemcleanup; M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory leak detected for heap variables: %a" D.pretty state (* TRANSFER FUNCTIONS *) From 00cc9b541ae6e6270906c16cbba94498692ab6f3 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Mon, 2 Oct 2023 10:58:45 +0200 Subject: [PATCH 0747/1312] Enable memory safety analyses in autoTune --- src/autoTune.ml | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/autoTune.ml b/src/autoTune.ml index ac7c150546..f73a71ed40 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -223,11 +223,18 @@ let focusOnSpecification () = let uafAna = ["useAfterFree"] in print_endline @@ "Specification: ValidFree -> enabling useAfterFree analysis \"" ^ (String.concat ", " uafAna) ^ "\""; enableAnalyses uafAna - (* TODO: Finish these two below later *) - | ValidDeref - | ValidMemtrack -> () - | MemorySafety -> () (* TODO: This is here for now just to complete the pattern match *) - | ValidMemcleanup -> () + | ValidDeref -> (* Enable the memOutOfBounds analysis *) + let memOobAna = ["memOutOfBounds"] in + print_endline @@ "Specification: ValidDeref -> enabling memOutOfBounds analysis \"" ^ (String.concat ", " memOobAna) ^ "\""; + enableAnalyses memOobAna + | ValidMemtrack + | ValidMemcleanup -> (* Enable the memLeak analysis *) + let memLeakAna = ["memLeak"] in + print_endline @@ "Specification: ValidDeref and ValidMemcleanup -> enabling memLeak analysis \"" ^ (String.concat ", " memLeakAna) ^ "\""; + enableAnalyses memLeakAna + | MemorySafety -> (* TODO: This is a temporary solution for the memory safety category *) + let memSafetyAnas = ["memOutOfBounds"; "memLeak"; "useAfterFree";] in + enableAnalyses memSafetyAnas (*Detect enumerations and enable the "ana.int.enums" option*) exception EnumFound From 2c8e927f4e5f88b0a4eb003da6fd24ac55a0e004 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Mon, 2 Oct 2023 10:59:36 +0200 Subject: [PATCH 0748/1312] Rename UAF tests from number 78 to 74 --- .../{78-use_after_free => 74-use_after_free}/01-simple-uaf.c | 0 .../{78-use_after_free => 74-use_after_free}/02-conditional-uaf.c | 0 .../{78-use_after_free => 74-use_after_free}/03-nested-ptr-uaf.c | 0 .../04-function-call-uaf.c | 0 .../05-uaf-free-in-wrapper-fun.c | 0 .../{78-use_after_free => 74-use_after_free}/06-uaf-struct.c | 0 .../{78-use_after_free => 74-use_after_free}/07-itc-double-free.c | 0 .../08-itc-no-double-free.c | 0 .../{78-use_after_free => 74-use_after_free}/09-juliet-uaf.c | 0 .../10-juliet-double-free.c | 0 .../11-wrapper-funs-uaf.c | 0 .../12-multi-threaded-uaf.c | 0 .../13-multi-threaded-uaf-with-joined-thread.c | 0 .../{78-use_after_free => 74-use_after_free}/14-alloca-uaf.c | 0 14 files changed, 0 insertions(+), 0 deletions(-) rename tests/regression/{78-use_after_free => 74-use_after_free}/01-simple-uaf.c (100%) rename tests/regression/{78-use_after_free => 74-use_after_free}/02-conditional-uaf.c (100%) rename tests/regression/{78-use_after_free => 74-use_after_free}/03-nested-ptr-uaf.c (100%) rename tests/regression/{78-use_after_free => 74-use_after_free}/04-function-call-uaf.c (100%) rename tests/regression/{78-use_after_free => 74-use_after_free}/05-uaf-free-in-wrapper-fun.c (100%) rename tests/regression/{78-use_after_free => 74-use_after_free}/06-uaf-struct.c (100%) rename tests/regression/{78-use_after_free => 74-use_after_free}/07-itc-double-free.c (100%) rename tests/regression/{78-use_after_free => 74-use_after_free}/08-itc-no-double-free.c (100%) rename tests/regression/{78-use_after_free => 74-use_after_free}/09-juliet-uaf.c (100%) rename tests/regression/{78-use_after_free => 74-use_after_free}/10-juliet-double-free.c (100%) rename tests/regression/{78-use_after_free => 74-use_after_free}/11-wrapper-funs-uaf.c (100%) rename tests/regression/{78-use_after_free => 74-use_after_free}/12-multi-threaded-uaf.c (100%) rename tests/regression/{78-use_after_free => 74-use_after_free}/13-multi-threaded-uaf-with-joined-thread.c (100%) rename tests/regression/{78-use_after_free => 74-use_after_free}/14-alloca-uaf.c (100%) diff --git a/tests/regression/78-use_after_free/01-simple-uaf.c b/tests/regression/74-use_after_free/01-simple-uaf.c similarity index 100% rename from tests/regression/78-use_after_free/01-simple-uaf.c rename to tests/regression/74-use_after_free/01-simple-uaf.c diff --git a/tests/regression/78-use_after_free/02-conditional-uaf.c b/tests/regression/74-use_after_free/02-conditional-uaf.c similarity index 100% rename from tests/regression/78-use_after_free/02-conditional-uaf.c rename to tests/regression/74-use_after_free/02-conditional-uaf.c diff --git a/tests/regression/78-use_after_free/03-nested-ptr-uaf.c b/tests/regression/74-use_after_free/03-nested-ptr-uaf.c similarity index 100% rename from tests/regression/78-use_after_free/03-nested-ptr-uaf.c rename to tests/regression/74-use_after_free/03-nested-ptr-uaf.c diff --git a/tests/regression/78-use_after_free/04-function-call-uaf.c b/tests/regression/74-use_after_free/04-function-call-uaf.c similarity index 100% rename from tests/regression/78-use_after_free/04-function-call-uaf.c rename to tests/regression/74-use_after_free/04-function-call-uaf.c diff --git a/tests/regression/78-use_after_free/05-uaf-free-in-wrapper-fun.c b/tests/regression/74-use_after_free/05-uaf-free-in-wrapper-fun.c similarity index 100% rename from tests/regression/78-use_after_free/05-uaf-free-in-wrapper-fun.c rename to tests/regression/74-use_after_free/05-uaf-free-in-wrapper-fun.c diff --git a/tests/regression/78-use_after_free/06-uaf-struct.c b/tests/regression/74-use_after_free/06-uaf-struct.c similarity index 100% rename from tests/regression/78-use_after_free/06-uaf-struct.c rename to tests/regression/74-use_after_free/06-uaf-struct.c diff --git a/tests/regression/78-use_after_free/07-itc-double-free.c b/tests/regression/74-use_after_free/07-itc-double-free.c similarity index 100% rename from tests/regression/78-use_after_free/07-itc-double-free.c rename to tests/regression/74-use_after_free/07-itc-double-free.c diff --git a/tests/regression/78-use_after_free/08-itc-no-double-free.c b/tests/regression/74-use_after_free/08-itc-no-double-free.c similarity index 100% rename from tests/regression/78-use_after_free/08-itc-no-double-free.c rename to tests/regression/74-use_after_free/08-itc-no-double-free.c diff --git a/tests/regression/78-use_after_free/09-juliet-uaf.c b/tests/regression/74-use_after_free/09-juliet-uaf.c similarity index 100% rename from tests/regression/78-use_after_free/09-juliet-uaf.c rename to tests/regression/74-use_after_free/09-juliet-uaf.c diff --git a/tests/regression/78-use_after_free/10-juliet-double-free.c b/tests/regression/74-use_after_free/10-juliet-double-free.c similarity index 100% rename from tests/regression/78-use_after_free/10-juliet-double-free.c rename to tests/regression/74-use_after_free/10-juliet-double-free.c diff --git a/tests/regression/78-use_after_free/11-wrapper-funs-uaf.c b/tests/regression/74-use_after_free/11-wrapper-funs-uaf.c similarity index 100% rename from tests/regression/78-use_after_free/11-wrapper-funs-uaf.c rename to tests/regression/74-use_after_free/11-wrapper-funs-uaf.c diff --git a/tests/regression/78-use_after_free/12-multi-threaded-uaf.c b/tests/regression/74-use_after_free/12-multi-threaded-uaf.c similarity index 100% rename from tests/regression/78-use_after_free/12-multi-threaded-uaf.c rename to tests/regression/74-use_after_free/12-multi-threaded-uaf.c diff --git a/tests/regression/78-use_after_free/13-multi-threaded-uaf-with-joined-thread.c b/tests/regression/74-use_after_free/13-multi-threaded-uaf-with-joined-thread.c similarity index 100% rename from tests/regression/78-use_after_free/13-multi-threaded-uaf-with-joined-thread.c rename to tests/regression/74-use_after_free/13-multi-threaded-uaf-with-joined-thread.c diff --git a/tests/regression/78-use_after_free/14-alloca-uaf.c b/tests/regression/74-use_after_free/14-alloca-uaf.c similarity index 100% rename from tests/regression/78-use_after_free/14-alloca-uaf.c rename to tests/regression/74-use_after_free/14-alloca-uaf.c From 7857a683163de4cc6e1df31f4b25dd80a99b2189 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 2 Oct 2023 16:34:03 +0300 Subject: [PATCH 0749/1312] Fix incorrect unlock in witness/tm-inv-transfer tests --- .../regression/56-witness/60-tm-inv-transfer-protection.c | 6 +++--- tests/regression/56-witness/61-tm-inv-transfer-mine.c | 8 ++++---- .../56-witness/62-tm-inv-transfer-protection-witness.c | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/regression/56-witness/60-tm-inv-transfer-protection.c b/tests/regression/56-witness/60-tm-inv-transfer-protection.c index 3d5bcbc871..07260adbdd 100644 --- a/tests/regression/56-witness/60-tm-inv-transfer-protection.c +++ b/tests/regression/56-witness/60-tm-inv-transfer-protection.c @@ -35,12 +35,12 @@ int main(void) { __goblint_check(g >= 40); __goblint_check(g <= 41); // UNKNOWN (lacks expressivity) pthread_mutex_unlock(&C); - pthread_mutex_unlock(&C); - + pthread_mutex_unlock(&B); + pthread_mutex_lock(&C); __goblint_check(g >= 40); __goblint_check(g <= 42); // UNKNOWN (widen) pthread_mutex_unlock(&C); - + return 0; } diff --git a/tests/regression/56-witness/61-tm-inv-transfer-mine.c b/tests/regression/56-witness/61-tm-inv-transfer-mine.c index 8f912bc2d9..cd8301fb39 100644 --- a/tests/regression/56-witness/61-tm-inv-transfer-mine.c +++ b/tests/regression/56-witness/61-tm-inv-transfer-mine.c @@ -35,12 +35,12 @@ int main(void) { __goblint_check(g >= 40); __goblint_check(g <= 41); pthread_mutex_unlock(&C); - pthread_mutex_unlock(&C); - + pthread_mutex_unlock(&B); + pthread_mutex_lock(&C); - __goblint_check(g >= 40); + __goblint_check(g >= 40); // TODO why? __goblint_check(g <= 42); pthread_mutex_unlock(&C); - + return 0; } \ No newline at end of file diff --git a/tests/regression/56-witness/62-tm-inv-transfer-protection-witness.c b/tests/regression/56-witness/62-tm-inv-transfer-protection-witness.c index 7be5bcf53e..68aada7394 100644 --- a/tests/regression/56-witness/62-tm-inv-transfer-protection-witness.c +++ b/tests/regression/56-witness/62-tm-inv-transfer-protection-witness.c @@ -35,12 +35,12 @@ int main(void) { __goblint_check(g >= 40); __goblint_check(g <= 41); // UNKNOWN (lacks expressivity) pthread_mutex_unlock(&C); - pthread_mutex_unlock(&C); - + pthread_mutex_unlock(&B); + pthread_mutex_lock(&C); __goblint_check(g >= 40); __goblint_check(g <= 42); pthread_mutex_unlock(&C); - + return 0; } \ No newline at end of file From 6c8a05b223858413b2ca695473f9c0a9ada9eb09 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 2 Oct 2023 16:39:40 +0300 Subject: [PATCH 0750/1312] Revert "Disable pins for v2.2.0 release" This reverts commit b46aeda9eaecd3ec42ec14c977d1550faad0de23. --- goblint.opam | 2 +- goblint.opam.locked | 5 +++++ goblint.opam.template | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/goblint.opam b/goblint.opam index c84e7a56f0..d019379dd4 100644 --- a/goblint.opam +++ b/goblint.opam @@ -77,7 +77,7 @@ available: os-distribution != "alpine" & arch != "arm64" pin-depends: [ [ "goblint-cil.2.0.2" "git+https://github.com/goblint/cil.git#398dca3d94a06a9026b3737aabf100ee3498229f" ] # TODO: add back after release, only pinned for optimization (https://github.com/ocaml-ppx/ppx_deriving/pull/252) - # [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] + [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] ] post-messages: [ "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} diff --git a/goblint.opam.locked b/goblint.opam.locked index 8556e9eebb..ebe024dadd 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -128,9 +128,14 @@ conflicts: [ post-messages: [ "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} ] +# TODO: manually reordered to avoid opam pin crash: https://github.com/ocaml/opam/issues/4936 pin-depends: [ [ "goblint-cil.2.0.2" "git+https://github.com/goblint/cil.git#398dca3d94a06a9026b3737aabf100ee3498229f" ] + [ + "ppx_deriving.5.2.1" + "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" + ] ] diff --git a/goblint.opam.template b/goblint.opam.template index d9e1ebf477..a493861e96 100644 --- a/goblint.opam.template +++ b/goblint.opam.template @@ -4,7 +4,7 @@ available: os-distribution != "alpine" & arch != "arm64" pin-depends: [ [ "goblint-cil.2.0.2" "git+https://github.com/goblint/cil.git#398dca3d94a06a9026b3737aabf100ee3498229f" ] # TODO: add back after release, only pinned for optimization (https://github.com/ocaml-ppx/ppx_deriving/pull/252) - # [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] + [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] ] post-messages: [ "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} From 7e57562220e049434db9af6a095c7da1312b4618 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 2 Oct 2023 17:05:18 +0300 Subject: [PATCH 0751/1312] Add final message for exp.single-threaded --- src/analyses/mCP.ml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/analyses/mCP.ml b/src/analyses/mCP.ml index 1b6a7e5a1d..9eb21b77ef 100644 --- a/src/analyses/mCP.ml +++ b/src/analyses/mCP.ml @@ -144,7 +144,9 @@ struct let spawn_one v d = List.iter (fun (lval, args) -> ctx.spawn lval v args) d in - if not (get_bool "exp.single-threaded") then + if get_bool "exp.single-threaded" then + M.msg_final Error ~category:Unsound "Thread not spawned" + else iter (uncurry spawn_one) @@ group_assoc_eq Basetype.Variables.equal xs let do_sideg ctx (xs:(V.t * (WideningTokens.TS.t * G.t)) list) = From f87285462b6cdcff1a2081b0d80b266ce248a358 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 3 Oct 2023 12:57:44 +0300 Subject: [PATCH 0752/1312] Fix print for valid-memtrack and valid-memcleanup in `autoTune.ml` Co-authored-by: Michael Schwarz --- src/autoTune.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/autoTune.ml b/src/autoTune.ml index f73a71ed40..e093c63f08 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -230,7 +230,7 @@ let focusOnSpecification () = | ValidMemtrack | ValidMemcleanup -> (* Enable the memLeak analysis *) let memLeakAna = ["memLeak"] in - print_endline @@ "Specification: ValidDeref and ValidMemcleanup -> enabling memLeak analysis \"" ^ (String.concat ", " memLeakAna) ^ "\""; + print_endline @@ "Specification: ValidMemtrack and ValidMemcleanup -> enabling memLeak analysis \"" ^ (String.concat ", " memLeakAna) ^ "\""; enableAnalyses memLeakAna | MemorySafety -> (* TODO: This is a temporary solution for the memory safety category *) let memSafetyAnas = ["memOutOfBounds"; "memLeak"; "useAfterFree";] in From edaef4230d6db75def47f41b4a748c9cbd5aa83d Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 3 Oct 2023 12:02:01 +0200 Subject: [PATCH 0753/1312] Set `ana.malloc.unique_address_count` to `1` when activating memLeak analysis --- src/autoTune.ml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/autoTune.ml b/src/autoTune.ml index e093c63f08..0441fe0b4f 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -230,6 +230,8 @@ let focusOnSpecification () = | ValidMemtrack | ValidMemcleanup -> (* Enable the memLeak analysis *) let memLeakAna = ["memLeak"] in + print_endline "Setting \"ana.malloc.unique_address_count\" to 1"; + set_int "ana.malloc.unique_address_count" 1; print_endline @@ "Specification: ValidMemtrack and ValidMemcleanup -> enabling memLeak analysis \"" ^ (String.concat ", " memLeakAna) ^ "\""; enableAnalyses memLeakAna | MemorySafety -> (* TODO: This is a temporary solution for the memory safety category *) From 7f0b43ccf1db2d6c3bec95ce1ea759dff84e4fba Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 3 Oct 2023 12:09:00 +0200 Subject: [PATCH 0754/1312] Improve some `AnalysisState` global flag comments --- src/framework/analysisState.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/framework/analysisState.ml b/src/framework/analysisState.ml index 1416f99f69..05a93741f8 100644 --- a/src/framework/analysisState.ml +++ b/src/framework/analysisState.ml @@ -13,10 +13,10 @@ let svcomp_may_invalid_free = ref false (** Whether an invalid pointer dereference happened *) let svcomp_may_invalid_deref = ref false -(** Whether an invalid memtrack happened *) +(** Whether a memory leak occurred and there's no reference to the leaked memory *) let svcomp_may_invalid_memtrack = ref false -(** Whether an invalid memcleanup happened *) +(** Whether a memory leak occurred *) let svcomp_may_invalid_memcleanup = ref false (** A hack to see if we are currently doing global inits *) From a975702f700511c3c1f0b71979502462192d9bc2 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 3 Oct 2023 12:25:33 +0200 Subject: [PATCH 0755/1312] Clean up some commented out code --- src/witness/svcompSpec.ml | 6 --- src/witness/witness.ml | 93 ++------------------------------------- 2 files changed, 3 insertions(+), 96 deletions(-) diff --git a/src/witness/svcompSpec.ml b/src/witness/svcompSpec.ml index 25a9f522bc..4a3da23d9b 100644 --- a/src/witness/svcompSpec.ml +++ b/src/witness/svcompSpec.ml @@ -37,12 +37,6 @@ let of_string s = let mem_safety_props = ["valid-free"; "valid-deref"; "valid-memtrack";] in if (global1 <> global2 && global1 <> global3 && global2 <> global3) && List.for_all (fun x -> List.mem x mem_safety_props) [global1; global2; global3] then MemorySafety - (* if global = "valid-free" then - ValidFree - else if global = "valid-deref" then - ValidDeref - else if global = "valid-memtrack" then - ValidMemtrack *) else failwith "Svcomp.Specification.of_string: unknown global expression" else if Str.string_match regexp_single s 0 then diff --git a/src/witness/witness.ml b/src/witness/witness.ml index 35d932210d..a28f69f76a 100644 --- a/src/witness/witness.ml +++ b/src/witness/witness.ml @@ -505,96 +505,9 @@ struct in (module TaskResult:WitnessTaskResult) ) - | ValidFree (*->*) - (* let module TrivialArg = - struct - include Arg - let next _ = [] - end - in - if not !AnalysisState.svcomp_may_invalid_free then ( - let module TaskResult = - struct - module Arg = Arg - let result = Result.True - let invariant _ = Invariant.none - let is_violation _ = false - let is_sink _ = false - end - in - (module TaskResult:WitnessTaskResult) - ) else ( - let module TaskResult = - struct - module Arg = TrivialArg - let result = Result.Unknown - let invariant _ = Invariant.none - let is_violation _ = false - let is_sink _ = false - end - in - (module TaskResult:WitnessTaskResult) - ) *) - | ValidDeref (*->*) - (* let module TrivialArg = - struct - include Arg - let next _ = [] - end - in - if not !AnalysisState.svcomp_may_invalid_deref then ( - let module TaskResult = - struct - module Arg = Arg - let result = Result.True - let invariant _ = Invariant.none - let is_violation _ = false - let is_sink _ = false - end - in - (module TaskResult:WitnessTaskResult) - ) else ( - let module TaskResult = - struct - module Arg = TrivialArg - let result = Result.Unknown - let invariant _ = Invariant.none - let is_violation _ = false - let is_sink _ = false - end - in - (module TaskResult:WitnessTaskResult) - ) *) - | ValidMemtrack (*->*) - (* let module TrivialArg = - struct - include Arg - let next _ = [] - end - in - if not !AnalysisState.svcomp_may_invalid_memtrack then ( - let module TaskResult = - struct - module Arg = Arg - let result = Result.True - let invariant _ = Invariant.none - let is_violation _ = false - let is_sink _ = false - end - in - (module TaskResult:WitnessTaskResult) - ) else ( - let module TaskResult = - struct - module Arg = TrivialArg - let result = Result.Unknown - let invariant _ = Invariant.none - let is_violation _ = false - let is_sink _ = false - end - in - (module TaskResult:WitnessTaskResult) - ) *) + | ValidFree + | ValidDeref + | ValidMemtrack | MemorySafety -> let module TrivialArg = struct From b26010ac042143b4cdeb806b2a35b67054db1d76 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 3 Oct 2023 12:31:31 +0200 Subject: [PATCH 0756/1312] Set `ana.malloc.unique_address_count` to `1` only if it's not already set to a value >= 1 --- src/autoTune.ml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/autoTune.ml b/src/autoTune.ml index 0441fe0b4f..4912d645ce 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -230,8 +230,10 @@ let focusOnSpecification () = | ValidMemtrack | ValidMemcleanup -> (* Enable the memLeak analysis *) let memLeakAna = ["memLeak"] in - print_endline "Setting \"ana.malloc.unique_address_count\" to 1"; - set_int "ana.malloc.unique_address_count" 1; + if (get_int "ana.malloc.unique_address_count") < 1 then ( + print_endline "Setting \"ana.malloc.unique_address_count\" to 1"; + set_int "ana.malloc.unique_address_count" 1; + ); print_endline @@ "Specification: ValidMemtrack and ValidMemcleanup -> enabling memLeak analysis \"" ^ (String.concat ", " memLeakAna) ^ "\""; enableAnalyses memLeakAna | MemorySafety -> (* TODO: This is a temporary solution for the memory safety category *) From 89d68af6d7c68a647e13dd77e876580dbc8e71b1 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 3 Oct 2023 12:35:30 +0200 Subject: [PATCH 0757/1312] Add a valid-memcleanup.prp file --- tests/sv-comp/valid-memcleanup.prp | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 tests/sv-comp/valid-memcleanup.prp diff --git a/tests/sv-comp/valid-memcleanup.prp b/tests/sv-comp/valid-memcleanup.prp new file mode 100644 index 0000000000..778c49e5dc --- /dev/null +++ b/tests/sv-comp/valid-memcleanup.prp @@ -0,0 +1,2 @@ +CHECK( init(main()), LTL(G valid-memcleanup) ) + From 738ebfad83dbd3645b1a4dc76165391284c9267b Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 3 Oct 2023 14:08:14 +0200 Subject: [PATCH 0758/1312] Bump goblint-cil --- goblint.opam | 2 +- goblint.opam.locked | 2 +- goblint.opam.template | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/goblint.opam b/goblint.opam index d019379dd4..bf51924626 100644 --- a/goblint.opam +++ b/goblint.opam @@ -75,7 +75,7 @@ dev-repo: "git+https://github.com/goblint/analyzer.git" # also remember to generate/adjust goblint.opam.locked! available: os-distribution != "alpine" & arch != "arm64" pin-depends: [ - [ "goblint-cil.2.0.2" "git+https://github.com/goblint/cil.git#398dca3d94a06a9026b3737aabf100ee3498229f" ] + [ "goblint-cil.2.0.2" "git+https://github.com/goblint/cil.git#c7ffc37ad83216a84d90fdbf427cc02a68ea5331" ] # TODO: add back after release, only pinned for optimization (https://github.com/ocaml-ppx/ppx_deriving/pull/252) [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] ] diff --git a/goblint.opam.locked b/goblint.opam.locked index ebe024dadd..2744d2fe92 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -132,7 +132,7 @@ post-messages: [ pin-depends: [ [ "goblint-cil.2.0.2" - "git+https://github.com/goblint/cil.git#398dca3d94a06a9026b3737aabf100ee3498229f" + "git+https://github.com/goblint/cil.git#c7ffc37ad83216a84d90fdbf427cc02a68ea5331" ] [ "ppx_deriving.5.2.1" diff --git a/goblint.opam.template b/goblint.opam.template index a493861e96..d8e25cde38 100644 --- a/goblint.opam.template +++ b/goblint.opam.template @@ -2,7 +2,7 @@ # also remember to generate/adjust goblint.opam.locked! available: os-distribution != "alpine" & arch != "arm64" pin-depends: [ - [ "goblint-cil.2.0.2" "git+https://github.com/goblint/cil.git#398dca3d94a06a9026b3737aabf100ee3498229f" ] + [ "goblint-cil.2.0.2" "git+https://github.com/goblint/cil.git#c7ffc37ad83216a84d90fdbf427cc02a68ea5331" ] # TODO: add back after release, only pinned for optimization (https://github.com/ocaml-ppx/ppx_deriving/pull/252) [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] ] From a4af447d8c8fbe6c3d35471206d431892f7c8b80 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 3 Oct 2023 14:19:16 +0200 Subject: [PATCH 0759/1312] Add desired cram tests --- tests/regression/00-sanity/37-long-double.c | 4 ++++ tests/regression/00-sanity/37-long-double.t | 6 ++++++ 2 files changed, 10 insertions(+) create mode 100644 tests/regression/00-sanity/37-long-double.c create mode 100644 tests/regression/00-sanity/37-long-double.t diff --git a/tests/regression/00-sanity/37-long-double.c b/tests/regression/00-sanity/37-long-double.c new file mode 100644 index 0000000000..01c9b8bb9b --- /dev/null +++ b/tests/regression/00-sanity/37-long-double.c @@ -0,0 +1,4 @@ +int main() { + long double l = 0.0L; + return (int)l; +} diff --git a/tests/regression/00-sanity/37-long-double.t b/tests/regression/00-sanity/37-long-double.t new file mode 100644 index 0000000000..60cdb8f612 --- /dev/null +++ b/tests/regression/00-sanity/37-long-double.t @@ -0,0 +1,6 @@ +Testing that there is warning about treating long double as double constant. + $ goblint 37-long-double.c + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 3 + dead: 0 + total lines: 3 From 557b878d09e140647d501fd115e7ed9ca9a1a30d Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 3 Oct 2023 14:21:22 +0200 Subject: [PATCH 0760/1312] Silence `long double` warnings --- src/util/cilfacade.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/util/cilfacade.ml b/src/util/cilfacade.ml index 6d55211c8d..7f6116f604 100644 --- a/src/util/cilfacade.ml +++ b/src/util/cilfacade.ml @@ -51,7 +51,8 @@ let init () = (* lineDirectiveStyle := None; *) RmUnused.keepUnused := true; print_CIL_Input := true; - Cabs2cil.allowDuplication := false + Cabs2cil.allowDuplication := false; + Cabs2cil.silenceLongDoubleWarning := true let current_file = ref dummyFile From baf62e5df576b5bab5e88a01fe84bd7ad89b9d11 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 3 Oct 2023 14:26:28 +0200 Subject: [PATCH 0761/1312] `assert false` if we encounter a CReal without a string representation --- src/analyses/base.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index ea595ad96d..e83a352df3 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -761,7 +761,7 @@ struct (match str with Some x -> M.tracel "casto" "CInt (%s, %a, %s)\n" (Z.to_string num) d_ikind ikind x | None -> ()); Int (ID.cast_to ikind (IntDomain.of_const (num,ikind,str))) | Const (CReal (_,fkind, Some str)) when not (Cilfacade.isComplexFKind fkind) -> Float (FD.of_string fkind str) (* prefer parsing from string due to higher precision *) - | Const (CReal (num, fkind, None)) when not (Cilfacade.isComplexFKind fkind) -> Float (FD.of_const fkind num) + | Const (CReal (num, fkind, None)) when not (Cilfacade.isComplexFKind fkind) -> assert false (* Cil does nor create CReal without string representation *) (* String literals *) | Const (CStr (x,_)) -> Address (AD.of_string x) (* normal 8-bit strings, type: char* *) | Const (CWStr (xs,_) as c) -> (* wide character strings, type: wchar_t* *) From f542edec92cf8c05eece7938c9a24fdf02870659 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 3 Oct 2023 14:35:16 +0200 Subject: [PATCH 0762/1312] Allow constant zero in CReal case --- src/analyses/base.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index e83a352df3..7b87d3ff51 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -761,7 +761,8 @@ struct (match str with Some x -> M.tracel "casto" "CInt (%s, %a, %s)\n" (Z.to_string num) d_ikind ikind x | None -> ()); Int (ID.cast_to ikind (IntDomain.of_const (num,ikind,str))) | Const (CReal (_,fkind, Some str)) when not (Cilfacade.isComplexFKind fkind) -> Float (FD.of_string fkind str) (* prefer parsing from string due to higher precision *) - | Const (CReal (num, fkind, None)) when not (Cilfacade.isComplexFKind fkind) -> assert false (* Cil does nor create CReal without string representation *) + | Const (CReal (num, fkind, None)) when not (Cilfacade.isComplexFKind fkind) && num = 0.0 -> Float (FD.of_const fkind num) (* constant 0 is ok, CIL creates these for zero-initializers; it is safe across float types *) + | Const (CReal (_, fkind, None)) when not (Cilfacade.isComplexFKind fkind) -> assert false (* Cil does not create other CReal without string representation *) (* String literals *) | Const (CStr (x,_)) -> Address (AD.of_string x) (* normal 8-bit strings, type: char* *) | Const (CWStr (xs,_) as c) -> (* wide character strings, type: wchar_t* *) From 171ba57b315044c20252fbff625a652c0f2d328f Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 3 Oct 2023 15:13:00 +0200 Subject: [PATCH 0763/1312] Comment out reachability filter in useAfterFree's `enter` --- src/analyses/useAfterFree.ml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index a28591e273..805bf1792c 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -182,7 +182,8 @@ struct let enter ctx (lval:lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = let caller_state = ctx.local in List.iter (fun arg -> warn_exp_might_contain_freed "enter" ctx arg) args; - if AllocaVars.is_empty (fst caller_state) && HeapVars.is_empty (snd caller_state) then + [(caller_state, caller_state)] + (* if AllocaVars.is_empty (fst caller_state) && HeapVars.is_empty (snd caller_state) then [caller_state, caller_state] else ( let reachable_from_args = List.fold_left (fun ad arg -> Queries.AD.join ad (ctx.ask (ReachableFrom arg))) (Queries.AD.empty ()) args in @@ -192,7 +193,7 @@ struct let reachable_vars = Queries.AD.to_var_may reachable_from_args in let callee_state = (AllocaVars.empty (), HeapVars.filter (fun var -> List.mem var reachable_vars) (snd caller_state)) in (* TODO: use AD.mem directly *) [caller_state, callee_state] - ) + ) *) let combine_env ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (callee_local:D.t) (f_ask:Queries.ask) : D.t = let (caller_stack_state, caller_heap_state) = ctx.local in From 4a1c038b5bd32674b8dff3bd9499d4a6ddbb2f85 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 3 Oct 2023 15:25:31 +0200 Subject: [PATCH 0764/1312] Fix wrong callee_state in enter --- src/analyses/useAfterFree.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index 805bf1792c..521f17d97f 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -182,7 +182,7 @@ struct let enter ctx (lval:lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = let caller_state = ctx.local in List.iter (fun arg -> warn_exp_might_contain_freed "enter" ctx arg) args; - [(caller_state, caller_state)] + [caller_state, (AllocaVars.empty (), snd caller_state)] (* if AllocaVars.is_empty (fst caller_state) && HeapVars.is_empty (snd caller_state) then [caller_state, caller_state] else ( From bb2091e5038a437c23db3c8711d38f19a301ef05 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 3 Oct 2023 15:25:45 +0200 Subject: [PATCH 0765/1312] Add test case for UAF in callee through a global var --- .../15-juliet-uaf-global-var.c | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 tests/regression/74-use_after_free/15-juliet-uaf-global-var.c diff --git a/tests/regression/74-use_after_free/15-juliet-uaf-global-var.c b/tests/regression/74-use_after_free/15-juliet-uaf-global-var.c new file mode 100644 index 0000000000..9cb3b2b29a --- /dev/null +++ b/tests/regression/74-use_after_free/15-juliet-uaf-global-var.c @@ -0,0 +1,24 @@ +//PARAM: --set ana.activated[+] useAfterFree --set ana.activated[+] threadJoins +#include +#include +#include + +int *global; + +void other(void) +{ + int *data = global; + free((void *)data); + return; +} + +int main(int argc, char **argv) +{ + int *data = (int *)malloc(400UL); + free((void *)data); + + global = data; + other(); + + return 0; +} \ No newline at end of file From fb4ad24d4f71f93181c3b908398bc785c5c32dfe Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 3 Oct 2023 16:32:48 +0200 Subject: [PATCH 0766/1312] Fix typo for warning about top address offsets --- src/analyses/memOutOfBounds.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index db86a63414..ca2cf82116 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -205,7 +205,7 @@ struct set_mem_safety_flag InvalidDeref; M.warn "Pointer %a has a bot address offset. An invalid memory access may occur" d_exp ptr ) else if VDQ.AD.exists (function - | Addr (_, o) -> ID.is_bot @@ offs_to_idx t o + | Addr (_, o) -> ID.is_top_of (Cilfacade.ptrdiff_ikind ()) (offs_to_idx t o) | _ -> false ) a then ( set_mem_safety_flag InvalidDeref; From 9d64db11a69abf60b0c777ee591860416c49666d Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 3 Oct 2023 16:33:09 +0200 Subject: [PATCH 0767/1312] Add test case with invalid deref in complex loop --- .../regression/77-mem-oob/10-oob-two-loops.c | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 tests/regression/77-mem-oob/10-oob-two-loops.c diff --git a/tests/regression/77-mem-oob/10-oob-two-loops.c b/tests/regression/77-mem-oob/10-oob-two-loops.c new file mode 100644 index 0000000000..2661f3dff7 --- /dev/null +++ b/tests/regression/77-mem-oob/10-oob-two-loops.c @@ -0,0 +1,20 @@ +// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval +int main() { + int *p = malloc(1048 * sizeof(int)); + + for (int i = 0; i < 1048; ++i) { + p[i] = __VERIFIER_nondet_int(); //NOWARN + } + + int *q = p; + + while (*q >= 0 && q < p + 1048 * sizeof(int)) { //WARN + if (__VERIFIER_nondet_int()) { + q++; + } else { + (*q)--; //WARN + } + } + free(p); + return 0; +} From 1afac024c34280a8c1b6bfbeae254654800d5919 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 3 Oct 2023 16:39:30 +0200 Subject: [PATCH 0768/1312] Disable info messages which confuse `make test` in 77/10 --- tests/regression/77-mem-oob/10-oob-two-loops.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression/77-mem-oob/10-oob-two-loops.c b/tests/regression/77-mem-oob/10-oob-two-loops.c index 2661f3dff7..6737a212bf 100644 --- a/tests/regression/77-mem-oob/10-oob-two-loops.c +++ b/tests/regression/77-mem-oob/10-oob-two-loops.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval +// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval --disable warn.info int main() { int *p = malloc(1048 * sizeof(int)); From f5c197e119e44fa997022daebaaae98d8e07ecf0 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 3 Oct 2023 17:28:47 +0200 Subject: [PATCH 0769/1312] Add option for enabling the marking of variables as pulled out of scope --- src/util/cilfacade.ml | 4 +++- src/util/options.schema.json | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/util/cilfacade.ml b/src/util/cilfacade.ml index eb7330aa19..32b6627319 100644 --- a/src/util/cilfacade.ml +++ b/src/util/cilfacade.ml @@ -51,7 +51,9 @@ let init () = (* lineDirectiveStyle := None; *) RmUnused.keepUnused := true; print_CIL_Input := true; - Cabs2cil.allowDuplication := false + Cabs2cil.allowDuplication := false; + if get_bool "cil.addNestedScopeAttr" then + Cabs2cil.addNestedScopeAttr := true let current_file = ref dummyFile diff --git a/src/util/options.schema.json b/src/util/options.schema.json index 1b9c7d3fd5..400dde06dc 100644 --- a/src/util/options.schema.json +++ b/src/util/options.schema.json @@ -288,6 +288,12 @@ "type": "boolean", "description": "Indicates whether gnu89 semantic should be used for inline functions.", "default": false + }, + "addNestedScopeAttr": { + "title": "cil.addNestedScopeAttr", + "type": "boolean", + "description": "Indicates whether variables that CIL pulls out of their scope should be marked.", + "default": false } }, "additionalProperties": false From 003b8148f3cb5035f083ee8c8e3ab339b3d9538e Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 3 Oct 2023 17:29:17 +0200 Subject: [PATCH 0770/1312] Warn for accesses to variables pulled out of scope --- src/analyses/memOutOfBounds.ml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index ca2cf82116..9db6e58e5f 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -228,6 +228,7 @@ struct ID.top_of @@ Cilfacade.ptrdiff_ikind () and check_lval_for_oob_access ctx ?(is_implicitly_derefed = false) lval = + check_lval_out_of_scope_access lval; (* If the lval does not contain a pointer or if it does contain a pointer, but only points to string addresses, then no need to WARN *) if (not @@ lval_contains_a_ptr lval) || ptr_only_has_str_addr ctx (Lval lval) then () else @@ -246,6 +247,15 @@ struct | _ -> check_exp_for_oob_access ctx ~is_implicitly_derefed e end + and check_lval_out_of_scope_access lval = + match lval with + | (Var v, _) -> + if hasAttribute "goblint_cil_nested" v.vattr then ( + set_mem_safety_flag InvalidDeref; + M.warn "Lvalue %a is potentially accessed out-of-scope. Invalid memory access may occur" d_lval lval + ) + | _ -> () + and check_no_binop_deref ctx lval_exp = check_unknown_addr_deref ctx lval_exp; let behavior = Undefined MemoryOutOfBoundsAccess in From ac7dd717c0dc8f353130c1395dc0f7910aaf8f24 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 3 Oct 2023 17:29:44 +0200 Subject: [PATCH 0771/1312] Automatically set `cil.addNestedScopeAttr` in autoTune when running `memOutOfBounds` --- src/autoTune.ml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/autoTune.ml b/src/autoTune.ml index 4912d645ce..4e0080d0bd 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -225,6 +225,8 @@ let focusOnSpecification () = enableAnalyses uafAna | ValidDeref -> (* Enable the memOutOfBounds analysis *) let memOobAna = ["memOutOfBounds"] in + print_endline "Setting \"cil.addNestedScopeAttr\" to true"; + set_bool "cil.addNestedScopeAttr" true; print_endline @@ "Specification: ValidDeref -> enabling memOutOfBounds analysis \"" ^ (String.concat ", " memOobAna) ^ "\""; enableAnalyses memOobAna | ValidMemtrack From c95a846617c7a73c86d5a2e4655b5e0f16d4f32e Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 3 Oct 2023 17:59:18 +0200 Subject: [PATCH 0772/1312] Bump goblint-cil --- goblint.opam | 6 +++--- goblint.opam.locked | 3 +++ goblint.opam.template | 6 +++--- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/goblint.opam b/goblint.opam index 661222805b..88289820c1 100644 --- a/goblint.opam +++ b/goblint.opam @@ -74,12 +74,12 @@ dev-repo: "git+https://github.com/goblint/analyzer.git" # on `dune build` goblint.opam will be generated from goblint.opam.template and dune-project # also remember to generate/adjust goblint.opam.locked! available: os-distribution != "alpine" & arch != "arm64" -# pin-depends: [ +pin-depends: [ # published goblint-cil 2.0.2 is currently up-to-date, so no pin needed - # [ "goblint-cil.2.0.2" "git+https://github.com/goblint/cil.git#98598d94f796a63751e5a9d39c6b3a9fe1f32330" ] + [ "goblint-cil.2.0.2" "git+https://github.com/goblint/cil.git#c7ffc37ad83216a84d90fdbf427cc02a68ea5331" ] # TODO: add back after release, only pinned for optimization (https://github.com/ocaml-ppx/ppx_deriving/pull/252) # [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] -# ] +] post-messages: [ "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} ] diff --git a/goblint.opam.locked b/goblint.opam.locked index bb59c41dd1..31acfdd4ea 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -122,6 +122,9 @@ build: [ ] dev-repo: "git+https://github.com/goblint/analyzer.git" available: os-distribution != "alpine" & arch != "arm64" +pin-depends: [ + [ "goblint-cil.2.0.2" "git+https://github.com/goblint/cil.git#c7ffc37ad83216a84d90fdbf427cc02a68ea5331" ] +] conflicts: [ "result" {< "1.5"} ] diff --git a/goblint.opam.template b/goblint.opam.template index 6259c4d498..3476f5befe 100644 --- a/goblint.opam.template +++ b/goblint.opam.template @@ -1,12 +1,12 @@ # on `dune build` goblint.opam will be generated from goblint.opam.template and dune-project # also remember to generate/adjust goblint.opam.locked! available: os-distribution != "alpine" & arch != "arm64" -# pin-depends: [ +pin-depends: [ # published goblint-cil 2.0.2 is currently up-to-date, so no pin needed - # [ "goblint-cil.2.0.2" "git+https://github.com/goblint/cil.git#98598d94f796a63751e5a9d39c6b3a9fe1f32330" ] + [ "goblint-cil.2.0.2" "git+https://github.com/goblint/cil.git#c7ffc37ad83216a84d90fdbf427cc02a68ea5331" ] # TODO: add back after release, only pinned for optimization (https://github.com/ocaml-ppx/ppx_deriving/pull/252) # [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] -# ] +] post-messages: [ "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} ] From c9a846b6a74f1dd37f53d15d9909adb291ec82da Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 3 Oct 2023 18:48:01 +0200 Subject: [PATCH 0773/1312] set also for meta property --- src/autoTune.ml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/autoTune.ml b/src/autoTune.ml index 4e0080d0bd..10c9570d46 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -239,8 +239,10 @@ let focusOnSpecification () = print_endline @@ "Specification: ValidMemtrack and ValidMemcleanup -> enabling memLeak analysis \"" ^ (String.concat ", " memLeakAna) ^ "\""; enableAnalyses memLeakAna | MemorySafety -> (* TODO: This is a temporary solution for the memory safety category *) - let memSafetyAnas = ["memOutOfBounds"; "memLeak"; "useAfterFree";] in - enableAnalyses memSafetyAnas + (print_endline "Setting \"cil.addNestedScopeAttr\" to true"; + set_bool "cil.addNestedScopeAttr" true; + let memSafetyAnas = ["memOutOfBounds"; "memLeak"; "useAfterFree";] in + enableAnalyses memSafetyAnas) (*Detect enumerations and enable the "ana.int.enums" option*) exception EnumFound From 9efae6f8bef52e96d99052443f6a6a31a50f4dca Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 3 Oct 2023 23:50:32 +0200 Subject: [PATCH 0774/1312] Fix address offset calculation in `memOutOfBounds` --- src/analyses/memOutOfBounds.ml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 9db6e58e5f..aadf433a6e 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -211,11 +211,16 @@ struct set_mem_safety_flag InvalidDeref; M.warn "Pointer %a has a top address offset. An invalid memory access may occur" d_exp ptr ); - (* Offset should be the same for all elements in the points-to set *) - (* Hence, we can just pick one element and obtain its offset *) - begin match VDQ.AD.choose a with - | Addr (_, o) -> offs_to_idx t o - | _ -> ID.top_of @@ Cilfacade.ptrdiff_ikind () + (* Get the address offsets of all points-to set elements *) + let addr_offsets = + VDQ.AD.filter (function Addr (v, o) -> true | _ -> false) a + |> VDQ.AD.to_mval + |> List.map (fun (_, o) -> offs_to_idx t o) + in + begin match addr_offsets with + | [] -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () + | [x] -> x + | x::xs -> List.fold_left ID.join x xs end end | None -> From dcc3d5c3c608414092171f55fda99ebcd18d29c4 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 3 Oct 2023 23:53:12 +0200 Subject: [PATCH 0775/1312] Adapt 77/10 test case to correctly account for address offsets --- tests/regression/77-mem-oob/10-oob-two-loops.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression/77-mem-oob/10-oob-two-loops.c b/tests/regression/77-mem-oob/10-oob-two-loops.c index 6737a212bf..5208da5a0b 100644 --- a/tests/regression/77-mem-oob/10-oob-two-loops.c +++ b/tests/regression/77-mem-oob/10-oob-two-loops.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval --disable warn.info +// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval --disable warn.info --set sem.int.signed_overflow assume_none int main() { int *p = malloc(1048 * sizeof(int)); From a64e9c37e0b12eaa767e5f43f28b952c41ffa318 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 3 Oct 2023 23:53:36 +0200 Subject: [PATCH 0776/1312] Add further test case to check address offset calculation in `memOutOfBounds` --- .../77-mem-oob/11-address-offset-oob.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 tests/regression/77-mem-oob/11-address-offset-oob.c diff --git a/tests/regression/77-mem-oob/11-address-offset-oob.c b/tests/regression/77-mem-oob/11-address-offset-oob.c new file mode 100644 index 0000000000..ba01a12873 --- /dev/null +++ b/tests/regression/77-mem-oob/11-address-offset-oob.c @@ -0,0 +1,16 @@ +// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval --disable warn.info --set sem.int.signed_overflow assume_none +int main() { + int *p = malloc(2 * sizeof(int)); + int *q = p; + int x; + + if (x) { + q++; + q++; + q++; + x = *q; //WARN + } + + x = *q; //WARN + return 0; +} From 44476ce76e283c8adbdcb445c1799186cc1c3320 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 3 Oct 2023 23:56:46 +0200 Subject: [PATCH 0777/1312] Set `ana.malloc.unique_address_count` for `MemorySafety` as well --- src/autoTune.ml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/autoTune.ml b/src/autoTune.ml index 10c9570d46..822195b1f6 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -241,6 +241,10 @@ let focusOnSpecification () = | MemorySafety -> (* TODO: This is a temporary solution for the memory safety category *) (print_endline "Setting \"cil.addNestedScopeAttr\" to true"; set_bool "cil.addNestedScopeAttr" true; + if (get_int "ana.malloc.unique_address_count") < 1 then ( + print_endline "Setting \"ana.malloc.unique_address_count\" to 1"; + set_int "ana.malloc.unique_address_count" 1; + ); let memSafetyAnas = ["memOutOfBounds"; "memLeak"; "useAfterFree";] in enableAnalyses memSafetyAnas) From 80b3b72df749337a3b68435f45b3ff90a2a74dac Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 4 Oct 2023 11:49:49 +0300 Subject: [PATCH 0778/1312] Add ldv_kzalloc to svcomp malloc wrappers --- conf/svcomp.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/conf/svcomp.json b/conf/svcomp.json index 913d43784b..d6c07387a8 100644 --- a/conf/svcomp.json +++ b/conf/svcomp.json @@ -52,7 +52,8 @@ "ldv_xmalloc", "ldv_xzalloc", - "ldv_calloc" + "ldv_calloc", + "ldv_kzalloc" ] }, "base": { From 35f6d0000f61e2565e9e9ab86bf2279fd8ebce7a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 4 Oct 2023 11:54:54 +0300 Subject: [PATCH 0779/1312] Disable free races in svcomp They should be considered MemSafety issues instead. --- conf/svcomp.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/conf/svcomp.json b/conf/svcomp.json index d6c07387a8..16c4ef338e 100644 --- a/conf/svcomp.json +++ b/conf/svcomp.json @@ -61,6 +61,9 @@ "domain": "partitioned" } }, + "race": { + "free": false + }, "autotune": { "enabled": true, "activated": [ From b6dfb14231e30c869e9c3a139b6ce7b609960a38 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 4 Oct 2023 11:55:39 +0300 Subject: [PATCH 0780/1312] Make threadid path sensitive in svcomp This is required for some ldv-races/ no-data-race tasks. --- conf/svcomp.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/conf/svcomp.json b/conf/svcomp.json index 16c4ef338e..f51c7a52ee 100644 --- a/conf/svcomp.json +++ b/conf/svcomp.json @@ -32,6 +32,14 @@ "thread", "threadJoins" ], + "path_sens": [ + "mutex", + "malloc_null", + "uninit", + "expsplit", + "activeSetjmp", + "threadid" + ], "context": { "widen": false }, From e052544e9d225c6292ecce05a0468f3dad7b5f31 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 4 Oct 2023 11:14:50 +0200 Subject: [PATCH 0781/1312] Move `cil.addNestedScopeAttr` setting to `init_options` --- src/util/cilfacade.ml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/util/cilfacade.ml b/src/util/cilfacade.ml index 8eaef280c6..6ca9023324 100644 --- a/src/util/cilfacade.ml +++ b/src/util/cilfacade.ml @@ -40,7 +40,9 @@ let is_first_field x = match x.fcomp.cfields with let init_options () = Mergecil.merge_inlines := get_bool "cil.merge.inlines"; Cil.cstd := Cil.cstd_of_string (get_string "cil.cstd"); - Cil.gnu89inline := get_bool "cil.gnu89inline" + Cil.gnu89inline := get_bool "cil.gnu89inline"; + if get_bool "cil.addNestedScopeAttr" = true then + Cabs2cil.addNestedScopeAttr := true let init () = initCIL (); @@ -51,9 +53,7 @@ let init () = (* lineDirectiveStyle := None; *) RmUnused.keepUnused := true; print_CIL_Input := true; - Cabs2cil.allowDuplication := false; - if get_bool "cil.addNestedScopeAttr" then - Cabs2cil.addNestedScopeAttr := true + Cabs2cil.allowDuplication := false let current_file = ref dummyFile From 11164fd7c7d709206c2e6483edd79492450af3c5 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 4 Oct 2023 12:15:54 +0300 Subject: [PATCH 0782/1312] Use exp.architecture for SV-COMP preprocessing Avoids a large number or CIL warnings about mismatching types. --- src/maingoblint.ml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/maingoblint.ml b/src/maingoblint.ml index 155faa0e76..ef548fb83a 100644 --- a/src/maingoblint.ml +++ b/src/maingoblint.ml @@ -252,6 +252,15 @@ let preprocess_files () = (* Preprocessor flags *) let cppflags = ref (get_string_list "pre.cppflags") in + if get_bool "ana.sv-comp.enabled" then ( + let architecture_flag = match get_string "exp.architecture" with + | "32bit" -> "-m32" + | "64bit" -> "-m64" + | _ -> assert false + in + cppflags := architecture_flag :: !cppflags + ); + (* the base include directory *) (* TODO: any better way? dune executable promotion doesn't add _build sites *) let source_lib_dirs = From a9f2bae279a99bedd1a83a10e87f0296d1e24068 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 4 Oct 2023 11:17:43 +0200 Subject: [PATCH 0783/1312] Port ~50 library specifications to the new system (#1200) * `atoi` and friends * `htonl` and friends * `sleep` an friends * `strchr` and friends * `strcasecmp` and friend * `__builtin_strchr` * `memcmp` * `memchr` * `{set,get}priority` * `execl` * `clock` * `sem_init` * `sem_wait` and `sem_trywait` * Make `sem_wait*` consistent * `sem_post` * `sem_destroy` * `sched_yield` * `srand` * `getpid` * `getuid`, `geteuid` * `getpgrp` * `{set,get}rlimit` * `setsid` * `getdtablesize` * `isatty` * `__VERIFIER_nondet_int` * `sigemptyset` and friends * `sigprocmask` * `fork` * dynamic linking * `inet_addr` * `setsockopt` * `getsockname` * `socket` * `gethostbyname_r` * `gethostname` * `getpeername` * `uname` * `getopt_long` * Enable `svcomp` library functions where `racemacros.h` is included * 36/15 activate sv-comp libraries * Comment on `Sem*` specials that they are unused * Move `str[n]casecmp` to Posix group * Punctuation * Apply suggestions from code review Co-authored-by: Karoliine Holter <44437975+karoliineh@users.noreply.github.com> Co-authored-by: Simmo Saan * Do not record accesses to `sem` * Move `getdtablesize` to `glibc` * Add `sema_init` back * Remove TODO --------- Co-authored-by: Karoliine Holter <44437975+karoliineh@users.noreply.github.com> Co-authored-by: Simmo Saan --- src/analyses/libraryDesc.ml | 5 + src/analyses/libraryFunctions.ml | 120 ++++++++++-------- .../28-race_reach/01-simple_racing.c | 3 +- .../28-race_reach/02-simple_racefree.c | 3 +- .../28-race_reach/03-munge_racing.c | 3 +- .../28-race_reach/04-munge_racefree.c | 3 +- .../28-race_reach/05-lockfuns_racefree.c | 3 +- .../28-race_reach/06-cond_racing1.c | 5 +- .../28-race_reach/07-cond_racing2.c | 1 + .../28-race_reach/08-cond_racefree.c | 1 + .../28-race_reach/09-ptrmunge_racing.c | 3 +- .../28-race_reach/10-ptrmunge_racefree.c | 3 +- .../regression/28-race_reach/11-ptr_racing.c | 3 +- .../28-race_reach/12-ptr_racefree.c | 5 +- .../28-race_reach/19-callback_racing.c | 1 + .../28-race_reach/20-callback_racefree.c | 1 + .../28-race_reach/21-deref_read_racing.c | 1 + .../28-race_reach/22-deref_read_racefree.c | 1 + .../28-race_reach/23-sound_unlock_racing.c | 1 + .../28-race_reach/24-sound_lock_racing.c | 1 + .../28-race_reach/27-funptr_racing.c | 7 +- .../28-race_reach/28-funptr_racefree.c | 9 +- .../28-race_reach/36-indirect_racefree.c | 1 + .../28-race_reach/37-indirect_racing.c | 1 + .../28-race_reach/40-trylock_racing.c | 3 +- .../28-race_reach/41-trylock_racefree.c | 3 +- .../28-race_reach/42-trylock2_racefree.c | 3 +- .../28-race_reach/45-escape_racing.c | 1 + .../28-race_reach/46-escape_racefree.c | 1 + .../28-race_reach/51-mutexptr_racefree.c | 1 + .../28-race_reach/60-invariant_racefree.c | 1 + .../28-race_reach/61-invariant_racing.c | 1 + .../28-race_reach/70-funloop_racefree.c | 2 +- .../28-race_reach/71-funloop_racing.c | 2 +- .../28-race_reach/72-funloop_hard_racing.c | 2 +- .../28-race_reach/73-funloop_hard_racefree.c | 2 +- .../74-tricky_address1_racefree.c | 2 +- .../75-tricky_address2_racefree.c | 2 +- .../76-tricky_address3_racefree.c | 2 +- .../28-race_reach/77-tricky_address4_racing.c | 2 +- .../regression/28-race_reach/78-equ_racing.c | 2 +- .../28-race_reach/79-equ_racefree.c | 2 +- .../regression/28-race_reach/81-list_racing.c | 2 +- .../28-race_reach/82-list_racefree.c | 2 +- .../28-race_reach/83-list2_racing1.c | 2 +- .../28-race_reach/84-list2_racing2.c | 2 +- .../28-race_reach/85-list2_racefree.c | 2 +- .../28-race_reach/86-lists_racing.c | 2 +- .../28-race_reach/87-lists_racefree.c | 2 +- .../28-race_reach/90-arrayloop2_racing.c | 2 +- .../28-race_reach/91-arrayloop2_racefree.c | 2 +- .../28-race_reach/92-evilcollapse_racing.c | 2 +- .../28-race_reach/93-evilcollapse_racefree.c | 2 +- .../28-race_reach/94-alloc_region_racing.c | 2 +- tests/regression/36-apron/15-globals-st.c | 2 +- 55 files changed, 142 insertions(+), 103 deletions(-) diff --git a/src/analyses/libraryDesc.ml b/src/analyses/libraryDesc.ml index 96d3341aea..72a4261cb5 100644 --- a/src/analyses/libraryDesc.ml +++ b/src/analyses/libraryDesc.ml @@ -58,6 +58,11 @@ type special = | Broadcast of Cil.exp | MutexAttrSetType of { attr:Cil.exp; typ: Cil.exp; } | MutexInit of { mutex:Cil.exp; attr: Cil.exp; } + (* All Sem specials are not used yet. *) + | SemInit of { sem: Cil.exp; pshared: Cil.exp; value: Cil.exp; } + | SemWait of { sem: Cil.exp; try_:bool; timeout: Cil.exp option;} + | SemPost of Cil.exp + | SemDestroy of Cil.exp | Wait of { cond: Cil.exp; mutex: Cil.exp; } | TimedWait of { cond: Cil.exp; mutex: Cil.exp; abstime: Cil.exp; (** Unused *) } | Math of { fun_args: math; } diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 102459e572..1c509e7660 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -31,6 +31,8 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("strncat", special [__ "dest" [r; w]; __ "src" [r]; __ "n" []] @@ fun dest src n -> Strcat { dest; src; n = Some n; }); ("__builtin_strncat", special [__ "dest" [r; w]; __ "src" [r]; __ "n" []] @@ fun dest src n -> Strcat { dest; src; n = Some n; }); ("__builtin___strncat_chk", special [__ "dest" [r; w]; __ "src" [r]; __ "n" []; drop "os" []] @@ fun dest src n -> Strcat { dest; src; n = Some n; }); + ("memcmp", unknown [drop "s1" [r]; drop "s2" [r]; drop "n" []]); + ("memchr", unknown [drop "s" [r]; drop "c" []; drop "n" []]); ("asctime", unknown ~attrs:[ThreadUnsafe] [drop "time_ptr" [r_deep]]); ("fclose", unknown [drop "stream" [r_deep; w_deep; f_deep]]); ("feof", unknown [drop "stream" [r_deep; w_deep]]); @@ -64,6 +66,9 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("strtok", unknown ~attrs:[ThreadUnsafe] [drop "str" [r; w]; drop "delim" [r]]); ("__builtin_strcmp", special [__ "s1" [r]; __ "s2" [r]] @@ fun s1 s2 -> Strcmp { s1; s2; n = None; }); ("strncmp", special [__ "s1" [r]; __ "s2" [r]; __ "n" []] @@ fun s1 s2 n -> Strcmp { s1; s2; n = Some n; }); + ("strchr", unknown [drop "s" [r]; drop "c" []]); + ("__builtin_strchr", unknown [drop "s" [r]; drop "c" []]); + ("strrchr", unknown [drop "s" [r]; drop "c" []]); ("malloc", special [__ "size" []] @@ fun size -> Malloc size); ("calloc", special [__ "n" []; __ "size" []] @@ fun n size -> Calloc {count = n; size}); ("realloc", special [__ "ptr" [r; f]; __ "size" []] @@ fun ptr size -> Realloc { ptr; size }); @@ -86,6 +91,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("getchar", unknown []); ("putchar", unknown [drop "ch" []]); ("puts", unknown [drop "s" [r]]); + ("srand", unknown [drop "seed" []]); ("rand", special ~attrs:[ThreadUnsafe] [] Rand); ("strerror", unknown ~attrs:[ThreadUnsafe] [drop "errnum" []]); ("strspn", unknown [drop "s" [r]; drop "accept" [r]]); @@ -127,6 +133,11 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("setjmp", special [__ "env" [w]] @@ fun env -> Setjmp { env }); ("longjmp", special [__ "env" [r]; __ "value" []] @@ fun env value -> Longjmp { env; value }); ("atexit", unknown [drop "function" [s]]); + ("atoi", unknown [drop "nptr" [r]]); + ("atol", unknown [drop "nptr" [r]]); + ("atoll", unknown [drop "nptr" [r]]); + ("setlocale", unknown [drop "category" []; drop "locale" [r]]); + ("clock", unknown []); ("atomic_flag_clear", unknown [drop "obj" [w]]); ("atomic_flag_clear_explicit", unknown [drop "obj" [w]; drop "order" []]); ("atomic_flag_test_and_set", unknown [drop "obj" [r; w]]); @@ -154,7 +165,6 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("dbm_nextkey", unknown ~attrs:[ThreadUnsafe] [drop "db" [r_deep]]); ("dbm_open", unknown ~attrs:[ThreadUnsafe] [drop "file" [r; w]; drop "open_flags" []; drop "file_mode" []]); ("dbm_store", unknown ~attrs:[ThreadUnsafe] [drop "db" [r_deep; w_deep]; drop "key" []; drop "content" []; drop "store_mode" []]); - ("dlerror", unknown ~attrs:[ThreadUnsafe] []); ("drand48", unknown ~attrs:[ThreadUnsafe] []); ("encrypt", unknown ~attrs:[ThreadUnsafe] [drop "block" [r; w]; drop "edflag" []]); ("setkey", unknown ~attrs:[ThreadUnsafe] [drop "key" [r]]); @@ -213,6 +223,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("fileno", unknown [drop "stream" [r_deep; w_deep]]); ("fdopen", unknown [drop "fd" []; drop "mode" [r]]); ("getopt", unknown ~attrs:[ThreadUnsafe] [drop "argc" []; drop "argv" [r_deep]; drop "optstring" [r]]); + ("getopt_long", unknown ~attrs:[ThreadUnsafe] [drop "argc" []; drop "argv" [r_deep]; drop "optstring" [r_deep]; drop "longopts" [r]; drop "longindex" [w]]); ("iconv_open", unknown [drop "tocode" [r]; drop "fromcode" [r]]); ("iconv", unknown [drop "cd" [r]; drop "inbuf" [r]; drop "inbytesleft" [r;w]; drop "outbuf" [w]; drop "outbytesleft" [r;w]]); ("iconv_close", unknown [drop "cd" [f]]); @@ -239,9 +250,15 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("hstrerror", unknown [drop "err" []]); ("inet_ntoa", unknown ~attrs:[ThreadUnsafe] [drop "in" []]); ("getsockopt", unknown [drop "sockfd" []; drop "level" []; drop "optname" []; drop "optval" [w]; drop "optlen" [w]]); + ("setsockopt", unknown [drop "sockfd" []; drop "level" []; drop "optname" []; drop "optval" [r]; drop "optlen" []]); + ("getsockname", unknown [drop "sockfd" []; drop "addr" [w_deep]; drop "addrlen" [w]]); ("gethostbyaddr", unknown ~attrs:[ThreadUnsafe] [drop "addr" [r_deep]; drop "len" []; drop "type" []]); ("gethostbyaddr_r", unknown [drop "addr" [r_deep]; drop "len" []; drop "type" []; drop "ret" [w_deep]; drop "buf" [w]; drop "buflen" []; drop "result" [w]; drop "h_errnop" [w]]); ("gethostbyname", unknown ~attrs:[ThreadUnsafe] [drop "name" [r]]); + ("gethostbyname_r", unknown [drop "name" [r]; drop "result_buf" [w_deep]; drop "buf" [w]; drop "buflen" []; drop "result" [w]; drop "h_errnop" [w]]); + ("gethostname", unknown [drop "name" [w]; drop "len" []]); + ("getpeername", unknown [drop "sockfd" []; drop "addr" [w_deep]; drop "addrlen" [r; w]]); + ("socket", unknown [drop "domain" []; drop "type" []; drop "protocol" []]); ("sigaction", unknown [drop "signum" []; drop "act" [r_deep; s_deep]; drop "oldact" [w_deep]]); ("tcgetattr", unknown [drop "fd" []; drop "termios_p" [w_deep]]); ("tcsetattr", unknown [drop "fd" []; drop "optional_actions" []; drop "termios_p" [r_deep]]); @@ -314,14 +331,48 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("ffs", unknown [drop "i" []]); ("_exit", special [drop "status" []] Abort); ("execvp", unknown [drop "file" [r]; drop "argv" [r_deep]]); + ("execl", unknown (drop "path" [r] :: drop "arg" [r] :: VarArgs (drop' [r]))); ("statvfs", unknown [drop "path" [r]; drop "buf" [w]]); ("readlink", unknown [drop "path" [r]; drop "buf" [w]; drop "bufsz" []]); ("wcswidth", unknown [drop "s" [r]; drop "n" []]); ("link", unknown [drop "oldpath" [r]; drop "newpath" [r]]); ("renameat", unknown [drop "olddirfd" []; drop "oldpath" [r]; drop "newdirfd" []; drop "newpath" [r]]); ("posix_fadvise", unknown [drop "fd" []; drop "offset" []; drop "len" []; drop "advice" []]); - ("getppid", unknown []); ("lockf", unknown [drop "fd" []; drop "cmd" []; drop "len" []]); + ("htonl", unknown [drop "hostlong" []]); + ("htons", unknown [drop "hostshort" []]); + ("ntohl", unknown [drop "netlong" []]); + ("ntohs", unknown [drop "netshort" []]); + ("sleep", unknown [drop "seconds" []]); + ("usleep", unknown [drop "usec" []]); + ("nanosleep", unknown [drop "req" [r]; drop "rem" [w]]); + ("setpriority", unknown [drop "which" []; drop "who" []; drop "prio" []]); + ("getpriority", unknown [drop "which" []; drop "who" []]); + ("sched_yield", unknown []); + ("getpid", unknown []); + ("getppid", unknown []); + ("getuid", unknown []); + ("geteuid", unknown []); + ("getpgrp", unknown []); + ("setrlimit", unknown [drop "resource" []; drop "rlim" [r]]); + ("getrlimit", unknown [drop "resource" []; drop "rlim" [w]]); + ("setsid", unknown []); + ("isatty", unknown [drop "fd" []]); + ("sigemptyset", unknown [drop "set" [w]]); + ("sigfillset", unknown [drop "set" [w]]); + ("sigaddset", unknown [drop "set" [r; w]; drop "signum" []]); + ("sigdelset", unknown [drop "set" [r; w]; drop "signum" []]); + ("sigismember", unknown [drop "set" [r]; drop "signum" []]); + ("sigprocmask", unknown [drop "how" []; drop "set" [r]; drop "oldset" [w]]); + ("fork", unknown []); + ("dlopen", unknown [drop "filename" [r]; drop "flag" []]); + ("dlerror", unknown ~attrs:[ThreadUnsafe] []); + ("dlsym", unknown [drop "handle" [r]; drop "symbol" [r]]); + ("dlclose", unknown [drop "handle" [r]]); + ("inet_addr", unknown [drop "cp" [r]]); + ("uname", unknown [drop "buf" [w_deep]]); + ("strcasecmp", unknown [drop "s1" [r]; drop "s2" [r]]); + ("strncasecmp", unknown [drop "s1" [r]; drop "s2" [r]; drop "n" []]); ] (** Pthread functions. *) @@ -388,9 +439,15 @@ let pthread_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("pthread_condattr_setclock", unknown [drop "attr" [w]; drop "clock_id" []]); ("pthread_mutexattr_destroy", unknown [drop "attr" [f]]); ("pthread_attr_setschedparam", unknown [drop "attr" [r; w]; drop "param" [r]]); - ("sem_timedwait", unknown [drop "sem" [r]; drop "abs_timeout" [r]]); (* no write accesses to sem because sync primitive itself has no race *) ("pthread_setaffinity_np", unknown [drop "thread" []; drop "cpusetsize" []; drop "cpuset" [r]]); ("pthread_getaffinity_np", unknown [drop "thread" []; drop "cpusetsize" []; drop "cpuset" [w]]); + (* Not recording read accesses to sem as these are thread-safe anyway not to clutter messages (as for mutexes) **) + ("sem_init", special [__ "sem" []; __ "pshared" []; __ "value" []] @@ fun sem pshared value -> SemInit {sem; pshared; value}); + ("sem_wait", special [__ "sem" []] @@ fun sem -> SemWait {sem; try_ = false; timeout = None}); + ("sem_trywait", special [__ "sem" []] @@ fun sem -> SemWait {sem; try_ = true; timeout = None}); + ("sem_timedwait", special [__ "sem" []; __ "abs_timeout" [r]] @@ fun sem abs_timeout-> SemWait {sem; try_ = true; timeout = Some abs_timeout}); (* no write accesses to sem because sync primitive itself has no race *) + ("sem_post", special [__ "sem" []] @@ fun sem -> SemPost sem); + ("sem_destroy", special [__ "sem" []] @@ fun sem -> SemDestroy sem); ] (** GCC builtin functions. @@ -506,6 +563,9 @@ let glibc_desc_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("memmem", unknown [drop "haystack" [r]; drop "haystacklen" []; drop "needle" [r]; drop "needlelen" [r]]); ("getifaddrs", unknown [drop "ifap" [w]]); ("freeifaddrs", unknown [drop "ifa" [f_deep]]); + ("atoq", unknown [drop "nptr" [r]]); + ("strchrnul", unknown [drop "s" [r]; drop "c" []]); + ("getdtablesize", unknown []); ] let linux_userspace_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ @@ -855,6 +915,7 @@ let svcomp_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__VERIFIER_atomic_begin", special [] @@ Lock { lock = verifier_atomic; try_ = false; write = true; return_on_success = true }); ("__VERIFIER_atomic_end", special [] @@ Unlock verifier_atomic); ("__VERIFIER_nondet_loff_t", unknown []); (* cannot give it in sv-comp.c without including stdlib or similar *) + ("__VERIFIER_nondet_int", unknown []); (* declare invalidate actions to prevent invalidating globals when extern in regression tests *) ] let ncurses_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ @@ -1039,7 +1100,6 @@ open Invalidate * We assume that no known functions that are reachable are executed/spawned. For that we use ThreadCreate above. *) (* WTF: why are argument numbers 1-indexed (in partition)? *) let invalidate_actions = [ - "atoi", readsAll; (*safe*) "connect", readsAll; (*safe*) "__printf_chk", readsAll;(*safe*) "printk", readsAll;(*safe*) @@ -1052,34 +1112,19 @@ let invalidate_actions = [ "__ctype_b_loc", readsAll;(*safe*) "__errno", readsAll;(*safe*) "__errno_location", readsAll;(*safe*) - "sigfillset", writesAll; (*unsafe*) - "sigprocmask", writesAll; (*unsafe*) - "uname", writesAll;(*unsafe*) - "getopt_long", writesAllButFirst 2 readsAll;(*drop 2*) "__strdup", readsAll;(*safe*) "strtoul__extinline", readsAll;(*safe*) - "geteuid", readsAll;(*safe*) "readdir_r", writesAll;(*unsafe*) "atoi__extinline", readsAll;(*safe*) - "getpid", readsAll;(*safe*) "_IO_getc", writesAll;(*unsafe*) "pipe", writesAll;(*unsafe*) "close", writesAll;(*unsafe*) - "setsid", readsAll;(*safe*) "strerror_r", writesAll;(*unsafe*) - "sigemptyset", writesAll;(*unsafe*) - "sigaddset", writesAll;(*unsafe*) "raise", writesAll;(*unsafe*) "_strlen", readsAll;(*safe*) - "dlopen", readsAll;(*safe*) - "dlsym", readsAll;(*safe*) - "dlclose", readsAll;(*safe*) "stat__extinline", writesAllButFirst 1 readsAll;(*drop 1*) "lstat__extinline", writesAllButFirst 1 readsAll;(*drop 1*) - "__builtin_strchr", readsAll;(*safe*) - "getpgrp", readsAll;(*safe*) "umount2", readsAll;(*safe*) - "memchr", readsAll;(*safe*) "waitpid", readsAll;(*safe*) "statfs", writes [1;3;4];(*keep [1;3;4]*) "mount", readsAll;(*safe*) @@ -1088,27 +1133,18 @@ let invalidate_actions = [ "ioctl", writesAll;(*unsafe*) "fstat__extinline", writesAll;(*unsafe*) "umount", readsAll;(*safe*) - "strrchr", readsAll;(*safe*) "scandir", writes [1;3;4];(*keep [1;3;4]*) "unlink", readsAll;(*safe*) - "sched_yield", readsAll;(*safe*) - "nanosleep", writesAllButFirst 1 readsAll;(*drop 1*) - "sigdelset", readsAll;(*safe*) "sigwait", writesAllButFirst 1 readsAll;(*drop 1*) - "setlocale", readsAll;(*safe*) "bindtextdomain", readsAll;(*safe*) "textdomain", readsAll;(*safe*) "dcgettext", readsAll;(*safe*) "putw", readsAll;(*safe*) "__getdelim", writes [3];(*keep [3]*) - "gethostbyname_r", readsAll;(*safe*) "__h_errno_location", readsAll;(*safe*) "__fxstat", readsAll;(*safe*) - "getuid", readsAll;(*safe*) "openlog", readsAll;(*safe*) - "getdtablesize", readsAll;(*safe*) "umask", readsAll;(*safe*) - "socket", readsAll;(*safe*) "clntudp_create", writesAllButFirst 3 readsAll;(*drop 3*) "svctcp_create", readsAll;(*safe*) "clntudp_bufcreate", writesAll;(*unsafe*) @@ -1120,24 +1156,15 @@ let invalidate_actions = [ "bind", readsAll;(*safe*) "svcudp_create", readsAll;(*safe*) "svc_register", writesAll;(*unsafe*) - "sleep", readsAll;(*safe*) - "usleep", readsAll; "svc_run", writesAll;(*unsafe*) "dup", readsAll; (*safe*) "__builtin___vsnprintf", writesAllButFirst 3 readsAll; (*drop 3*) "__builtin___vsnprintf_chk", writesAllButFirst 3 readsAll; (*drop 3*) - "strcasecmp", readsAll; (*safe*) - "strchr", readsAll; (*safe*) "__error", readsAll; (*safe*) "__maskrune", writesAll; (*unsafe*) - "inet_addr", readsAll; (*safe*) - "setsockopt", readsAll; (*safe*) "listen", readsAll; (*safe*) - "getsockname", writes [1;3]; (*keep [1;3]*) - "execl", readsAll; (*safe*) "select", writes [1;5]; (*keep [1;5]*) "accept", writesAll; (*keep [1]*) - "getpeername", writes [1]; (*keep [1]*) "times", writesAll; (*unsafe*) "timespec_get", writes [1]; "__tolower", readsAll; (*safe*) @@ -1154,26 +1181,12 @@ let invalidate_actions = [ "compress2", writes [3]; (*keep [3]*) "__toupper", readsAll; (*safe*) "BF_set_key", writes [3]; (*keep [3]*) - "memcmp", readsAll; (*safe*) "sendto", writes [2;4]; (*keep [2;4]*) "recvfrom", writes [4;5]; (*keep [4;5]*) - "srand", readsAll; (*safe*) - "gethostname", writesAll; (*unsafe*) - "fork", readsAll; (*safe*) - "setrlimit", readsAll; (*safe*) - "getrlimit", writes [2]; (*keep [2]*) - "sem_init", readsAll; (*safe*) - "sem_destroy", readsAll; (*safe*) - "sem_wait", readsAll; (*safe*) - "sem_post", readsAll; (*safe*) "PL_NewHashTable", readsAll; (*safe*) "assert_failed", readsAll; (*safe*) - "htonl", readsAll; (*safe*) - "htons", readsAll; (*safe*) - "ntohl", readsAll; (*safe*) "munmap", readsAll;(*safe*) "mmap", readsAll;(*safe*) - "clock", readsAll; "__builtin_va_arg_pack_len", readsAll; "__open_too_many_args", readsAll; "usb_submit_urb", readsAll; (* first argument is written to but according to specification must not be read from anymore *) @@ -1182,11 +1195,6 @@ let invalidate_actions = [ "kmem_cache_create", readsAll; "idr_pre_get", readsAll; "zil_replay", writes [1;2;3;5]; - "__VERIFIER_nondet_int", readsAll; (* no args, declare invalidate actions to prevent invalidating globals when extern in regression tests *) - (* no args, declare invalidate actions to prevent invalidating globals *) - "isatty", readsAll; - "setpriority", readsAll; - "getpriority", readsAll; (* ddverify *) "sema_init", readsAll; "__goblint_assume_join", readsAll; diff --git a/tests/regression/28-race_reach/01-simple_racing.c b/tests/regression/28-race_reach/01-simple_racing.c index 16a0fb28c9..f444228690 100644 --- a/tests/regression/28-race_reach/01-simple_racing.c +++ b/tests/regression/28-race_reach/01-simple_racing.c @@ -1,3 +1,4 @@ +//PARAM: --set lib.activated[+] sv-comp #include #include "racemacros.h" @@ -19,4 +20,4 @@ int main(void) { pthread_mutex_unlock(&mutex2); join_threads(t); return 0; -} \ No newline at end of file +} diff --git a/tests/regression/28-race_reach/02-simple_racefree.c b/tests/regression/28-race_reach/02-simple_racefree.c index 4713ccd48d..0e35f8da67 100644 --- a/tests/regression/28-race_reach/02-simple_racefree.c +++ b/tests/regression/28-race_reach/02-simple_racefree.c @@ -1,3 +1,4 @@ +//PARAM: --set lib.activated[+] sv-comp #include #include "racemacros.h" @@ -19,4 +20,4 @@ int main(void) { pthread_mutex_unlock(&mutex1); join_threads(t); return 0; -} \ No newline at end of file +} diff --git a/tests/regression/28-race_reach/03-munge_racing.c b/tests/regression/28-race_reach/03-munge_racing.c index 3c279d597a..9b8536e540 100644 --- a/tests/regression/28-race_reach/03-munge_racing.c +++ b/tests/regression/28-race_reach/03-munge_racing.c @@ -1,3 +1,4 @@ +//PARAM: --set lib.activated[+] sv-comp #include #include "racemacros.h" @@ -26,4 +27,4 @@ int main(void) { create_threads(t1); create_threads(t2); join_threads(t1); join_threads(t2); return 0; -} \ No newline at end of file +} diff --git a/tests/regression/28-race_reach/04-munge_racefree.c b/tests/regression/28-race_reach/04-munge_racefree.c index 799477e6ae..86637da91f 100644 --- a/tests/regression/28-race_reach/04-munge_racefree.c +++ b/tests/regression/28-race_reach/04-munge_racefree.c @@ -1,3 +1,4 @@ +//PARAM: --set lib.activated[+] sv-comp #include #include "racemacros.h" @@ -26,4 +27,4 @@ int main(void) { create_threads(t1); create_threads(t2); join_threads(t1); join_threads(t2); return 0; -} \ No newline at end of file +} diff --git a/tests/regression/28-race_reach/05-lockfuns_racefree.c b/tests/regression/28-race_reach/05-lockfuns_racefree.c index 0faecd0217..0a904005f8 100644 --- a/tests/regression/28-race_reach/05-lockfuns_racefree.c +++ b/tests/regression/28-race_reach/05-lockfuns_racefree.c @@ -1,3 +1,4 @@ +//PARAM: --set lib.activated[+] sv-comp #include #include "racemacros.h" @@ -26,4 +27,4 @@ int main(void) { unlock(); join_threads(t); return 0; -} \ No newline at end of file +} diff --git a/tests/regression/28-race_reach/06-cond_racing1.c b/tests/regression/28-race_reach/06-cond_racing1.c index c3e87507d5..931b68f81f 100644 --- a/tests/regression/28-race_reach/06-cond_racing1.c +++ b/tests/regression/28-race_reach/06-cond_racing1.c @@ -1,3 +1,4 @@ +//PARAM: --set lib.activated[+] sv-comp #include #include @@ -31,7 +32,3 @@ int main() { join_threads(t); return 0; } - - - - diff --git a/tests/regression/28-race_reach/07-cond_racing2.c b/tests/regression/28-race_reach/07-cond_racing2.c index b13b52dd1c..5e0d3f77f5 100644 --- a/tests/regression/28-race_reach/07-cond_racing2.c +++ b/tests/regression/28-race_reach/07-cond_racing2.c @@ -1,3 +1,4 @@ +//PARAM: --set lib.activated[+] sv-comp #include #include #include "racemacros.h" diff --git a/tests/regression/28-race_reach/08-cond_racefree.c b/tests/regression/28-race_reach/08-cond_racefree.c index ce18620121..8d86e89cf5 100644 --- a/tests/regression/28-race_reach/08-cond_racefree.c +++ b/tests/regression/28-race_reach/08-cond_racefree.c @@ -1,3 +1,4 @@ +//PARAM: --set lib.activated[+] sv-comp #include #include #include "racemacros.h" diff --git a/tests/regression/28-race_reach/09-ptrmunge_racing.c b/tests/regression/28-race_reach/09-ptrmunge_racing.c index 3191ca3ead..eb6a098800 100644 --- a/tests/regression/28-race_reach/09-ptrmunge_racing.c +++ b/tests/regression/28-race_reach/09-ptrmunge_racing.c @@ -1,3 +1,4 @@ +//PARAM: --set lib.activated[+] sv-comp #include #include "racemacros.h" @@ -27,4 +28,4 @@ int main(void) { create_threads(t1); create_threads(t2); join_threads(t1); join_threads(t2); return 0; -} \ No newline at end of file +} diff --git a/tests/regression/28-race_reach/10-ptrmunge_racefree.c b/tests/regression/28-race_reach/10-ptrmunge_racefree.c index d4e2144971..b21a1e9480 100644 --- a/tests/regression/28-race_reach/10-ptrmunge_racefree.c +++ b/tests/regression/28-race_reach/10-ptrmunge_racefree.c @@ -1,3 +1,4 @@ +//PARAM: --set lib.activated[+] sv-comp #include #include "racemacros.h" @@ -27,4 +28,4 @@ int main(void) { create_threads(t1); create_threads(t2); join_threads(t1); join_threads(t2); return 0; -} \ No newline at end of file +} diff --git a/tests/regression/28-race_reach/11-ptr_racing.c b/tests/regression/28-race_reach/11-ptr_racing.c index dc3f3c1a21..d6851afa82 100644 --- a/tests/regression/28-race_reach/11-ptr_racing.c +++ b/tests/regression/28-race_reach/11-ptr_racing.c @@ -1,3 +1,4 @@ +// PARAM: --set lib.activated[+] sv-comp #include #include "racemacros.h" @@ -20,4 +21,4 @@ int main(void) { pthread_mutex_unlock(&mutex2); join_threads(t); return 0; -} \ No newline at end of file +} diff --git a/tests/regression/28-race_reach/12-ptr_racefree.c b/tests/regression/28-race_reach/12-ptr_racefree.c index bb7bcffa3d..b22a942eda 100644 --- a/tests/regression/28-race_reach/12-ptr_racefree.c +++ b/tests/regression/28-race_reach/12-ptr_racefree.c @@ -1,3 +1,4 @@ +// PARAM: --set lib.activated[+] sv-comp #include #include "racemacros.h" @@ -16,8 +17,8 @@ void *t_fun(void *arg) { int main(void) { create_threads(t); pthread_mutex_lock(&mutex1); - assert_racefree(global); + assert_racefree(global); pthread_mutex_unlock(&mutex1); join_threads(t); return 0; -} \ No newline at end of file +} diff --git a/tests/regression/28-race_reach/19-callback_racing.c b/tests/regression/28-race_reach/19-callback_racing.c index 798d1e2783..04eaaeef4f 100644 --- a/tests/regression/28-race_reach/19-callback_racing.c +++ b/tests/regression/28-race_reach/19-callback_racing.c @@ -1,3 +1,4 @@ +// PARAM: --set lib.activated[+] sv-comp extern int __VERIFIER_nondet_int(); #include diff --git a/tests/regression/28-race_reach/20-callback_racefree.c b/tests/regression/28-race_reach/20-callback_racefree.c index 6f30ef492d..e41896f31a 100644 --- a/tests/regression/28-race_reach/20-callback_racefree.c +++ b/tests/regression/28-race_reach/20-callback_racefree.c @@ -1,3 +1,4 @@ +// PARAM: --set lib.activated[+] sv-comp extern int __VERIFIER_nondet_int(); #include diff --git a/tests/regression/28-race_reach/21-deref_read_racing.c b/tests/regression/28-race_reach/21-deref_read_racing.c index 93166f8125..880a8a4d38 100644 --- a/tests/regression/28-race_reach/21-deref_read_racing.c +++ b/tests/regression/28-race_reach/21-deref_read_racing.c @@ -1,3 +1,4 @@ +// PARAM: --set lib.activated[+] sv-comp #include #include "racemacros.h" diff --git a/tests/regression/28-race_reach/22-deref_read_racefree.c b/tests/regression/28-race_reach/22-deref_read_racefree.c index 2e4c5ebbb6..9ed4fb03cf 100644 --- a/tests/regression/28-race_reach/22-deref_read_racefree.c +++ b/tests/regression/28-race_reach/22-deref_read_racefree.c @@ -1,3 +1,4 @@ +// PARAM: --set lib.activated[+] sv-comp #include #include "racemacros.h" diff --git a/tests/regression/28-race_reach/23-sound_unlock_racing.c b/tests/regression/28-race_reach/23-sound_unlock_racing.c index da8db888db..c3ed280fbd 100644 --- a/tests/regression/28-race_reach/23-sound_unlock_racing.c +++ b/tests/regression/28-race_reach/23-sound_unlock_racing.c @@ -1,3 +1,4 @@ +// PARAM: --set lib.activated[+] sv-comp #include #include "racemacros.h" diff --git a/tests/regression/28-race_reach/24-sound_lock_racing.c b/tests/regression/28-race_reach/24-sound_lock_racing.c index 89ed5103dc..597bea716c 100644 --- a/tests/regression/28-race_reach/24-sound_lock_racing.c +++ b/tests/regression/28-race_reach/24-sound_lock_racing.c @@ -1,3 +1,4 @@ +// PARAM: --set lib.activated[+] sv-comp #include #include "racemacros.h" diff --git a/tests/regression/28-race_reach/27-funptr_racing.c b/tests/regression/28-race_reach/27-funptr_racing.c index 2c970deaf3..7210d0d56c 100644 --- a/tests/regression/28-race_reach/27-funptr_racing.c +++ b/tests/regression/28-race_reach/27-funptr_racing.c @@ -1,3 +1,4 @@ +// PARAM: --set lib.activated[+] sv-comp #include #include #include "racemacros.h" @@ -5,11 +6,11 @@ int global; pthread_mutex_t gm = PTHREAD_MUTEX_INITIALIZER; -void bad() { +void bad() { access(global); } -void good() { +void good() { pthread_mutex_lock(&gm); access(global); pthread_mutex_unlock(&gm); @@ -42,4 +43,4 @@ int main() { join_threads(t); return 0; -} \ No newline at end of file +} diff --git a/tests/regression/28-race_reach/28-funptr_racefree.c b/tests/regression/28-race_reach/28-funptr_racefree.c index 4e39156ecf..f36168aaaa 100644 --- a/tests/regression/28-race_reach/28-funptr_racefree.c +++ b/tests/regression/28-race_reach/28-funptr_racefree.c @@ -1,3 +1,4 @@ +// PARAM: --set lib.activated[+] sv-comp #include #include #include "racemacros.h" @@ -5,10 +6,10 @@ int global = 0; pthread_mutex_t gm = PTHREAD_MUTEX_INITIALIZER; -void bad() { +void bad() { access(global); -} -void good() { +} +void good() { pthread_mutex_lock(&gm); access(global); pthread_mutex_unlock(&gm); @@ -41,4 +42,4 @@ int main() { join_threads(t); return 0; -} \ No newline at end of file +} diff --git a/tests/regression/28-race_reach/36-indirect_racefree.c b/tests/regression/28-race_reach/36-indirect_racefree.c index 97dd10fc85..a2733f9df3 100644 --- a/tests/regression/28-race_reach/36-indirect_racefree.c +++ b/tests/regression/28-race_reach/36-indirect_racefree.c @@ -1,3 +1,4 @@ +// PARAM: --set lib.activated[+] sv-comp #include #include "racemacros.h" diff --git a/tests/regression/28-race_reach/37-indirect_racing.c b/tests/regression/28-race_reach/37-indirect_racing.c index e769a24836..6bf5757991 100644 --- a/tests/regression/28-race_reach/37-indirect_racing.c +++ b/tests/regression/28-race_reach/37-indirect_racing.c @@ -1,3 +1,4 @@ +// PARAM: --set lib.activated[+] sv-comp #include #include "racemacros.h" diff --git a/tests/regression/28-race_reach/40-trylock_racing.c b/tests/regression/28-race_reach/40-trylock_racing.c index 4d9c4acb98..94694bc1eb 100644 --- a/tests/regression/28-race_reach/40-trylock_racing.c +++ b/tests/regression/28-race_reach/40-trylock_racing.c @@ -1,3 +1,4 @@ +// PARAM: --set lib.activated[+] sv-comp #include #include "racemacros.h" @@ -25,4 +26,4 @@ int main(void) { pthread_mutex_unlock(&mutex); // no UB because ERRORCHECK join_threads(t); return 0; -} \ No newline at end of file +} diff --git a/tests/regression/28-race_reach/41-trylock_racefree.c b/tests/regression/28-race_reach/41-trylock_racefree.c index 0e521474c5..ce68d3abe2 100644 --- a/tests/regression/28-race_reach/41-trylock_racefree.c +++ b/tests/regression/28-race_reach/41-trylock_racefree.c @@ -1,3 +1,4 @@ +// PARAM: --set lib.activated[+] sv-comp #include #include "racemacros.h" @@ -22,4 +23,4 @@ int main(void) { pthread_mutex_unlock(&mutex); join_threads(t); return 0; -} \ No newline at end of file +} diff --git a/tests/regression/28-race_reach/42-trylock2_racefree.c b/tests/regression/28-race_reach/42-trylock2_racefree.c index 5f50097355..8b73328281 100644 --- a/tests/regression/28-race_reach/42-trylock2_racefree.c +++ b/tests/regression/28-race_reach/42-trylock2_racefree.c @@ -1,3 +1,4 @@ +// PARAM: --set lib.activated[+] sv-comp #include #include "racemacros.h" @@ -35,4 +36,4 @@ int main(void) { join_threads(t); return 0; -} \ No newline at end of file +} diff --git a/tests/regression/28-race_reach/45-escape_racing.c b/tests/regression/28-race_reach/45-escape_racing.c index a27db9a9df..31cb5fcacc 100644 --- a/tests/regression/28-race_reach/45-escape_racing.c +++ b/tests/regression/28-race_reach/45-escape_racing.c @@ -1,3 +1,4 @@ +// PARAM: --set lib.activated[+] sv-comp #include #include "racemacros.h" diff --git a/tests/regression/28-race_reach/46-escape_racefree.c b/tests/regression/28-race_reach/46-escape_racefree.c index af4874534e..731a61483e 100644 --- a/tests/regression/28-race_reach/46-escape_racefree.c +++ b/tests/regression/28-race_reach/46-escape_racefree.c @@ -1,3 +1,4 @@ +// PARAM: --set lib.activated[+] sv-comp #include #include "racemacros.h" diff --git a/tests/regression/28-race_reach/51-mutexptr_racefree.c b/tests/regression/28-race_reach/51-mutexptr_racefree.c index 972cd6e667..c57b58eb61 100644 --- a/tests/regression/28-race_reach/51-mutexptr_racefree.c +++ b/tests/regression/28-race_reach/51-mutexptr_racefree.c @@ -1,3 +1,4 @@ +// PARAM: --set lib.activated[+] sv-comp #include #include "racemacros.h" diff --git a/tests/regression/28-race_reach/60-invariant_racefree.c b/tests/regression/28-race_reach/60-invariant_racefree.c index d396e349bc..b8b86a86ca 100644 --- a/tests/regression/28-race_reach/60-invariant_racefree.c +++ b/tests/regression/28-race_reach/60-invariant_racefree.c @@ -1,3 +1,4 @@ +// PARAM: --set lib.activated[+] sv-comp #include #include "racemacros.h" diff --git a/tests/regression/28-race_reach/61-invariant_racing.c b/tests/regression/28-race_reach/61-invariant_racing.c index 22277557f9..b61f34ba25 100644 --- a/tests/regression/28-race_reach/61-invariant_racing.c +++ b/tests/regression/28-race_reach/61-invariant_racing.c @@ -1,3 +1,4 @@ +// PARAM: --set lib.activated[+] sv-comp #include #include "racemacros.h" diff --git a/tests/regression/28-race_reach/70-funloop_racefree.c b/tests/regression/28-race_reach/70-funloop_racefree.c index 11f44100cd..2ff0cdf9e5 100644 --- a/tests/regression/28-race_reach/70-funloop_racefree.c +++ b/tests/regression/28-race_reach/70-funloop_racefree.c @@ -1,4 +1,4 @@ -// PARAM: --enable ana.race.direct-arithmetic --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" +// PARAM: --enable ana.race.direct-arithmetic --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" --set lib.activated[+] sv-comp #include #include #include "racemacros.h" diff --git a/tests/regression/28-race_reach/71-funloop_racing.c b/tests/regression/28-race_reach/71-funloop_racing.c index d34be23175..ac20711401 100644 --- a/tests/regression/28-race_reach/71-funloop_racing.c +++ b/tests/regression/28-race_reach/71-funloop_racing.c @@ -1,4 +1,4 @@ -// PARAM: --enable ana.race.direct-arithmetic --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" +// PARAM: --enable ana.race.direct-arithmetic --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" --set lib.activated[+] sv-comp #include #include #include "racemacros.h" diff --git a/tests/regression/28-race_reach/72-funloop_hard_racing.c b/tests/regression/28-race_reach/72-funloop_hard_racing.c index d913bb16a6..78e24279f9 100644 --- a/tests/regression/28-race_reach/72-funloop_hard_racing.c +++ b/tests/regression/28-race_reach/72-funloop_hard_racing.c @@ -1,4 +1,4 @@ -// PARAM: --enable ana.race.direct-arithmetic --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" +// PARAM: --enable ana.race.direct-arithmetic --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" --set lib.activated[+] sv-comp #include #include #include "racemacros.h" diff --git a/tests/regression/28-race_reach/73-funloop_hard_racefree.c b/tests/regression/28-race_reach/73-funloop_hard_racefree.c index 33571b8c4d..cc48865d24 100644 --- a/tests/regression/28-race_reach/73-funloop_hard_racefree.c +++ b/tests/regression/28-race_reach/73-funloop_hard_racefree.c @@ -1,4 +1,4 @@ -// PARAM: --enable ana.race.direct-arithmetic --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" +// PARAM: --enable ana.race.direct-arithmetic --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" --set lib.activated[+] sv-comp #include #include #include "racemacros.h" diff --git a/tests/regression/28-race_reach/74-tricky_address1_racefree.c b/tests/regression/28-race_reach/74-tricky_address1_racefree.c index 0fdacd23c2..f9ce5d8b4d 100644 --- a/tests/regression/28-race_reach/74-tricky_address1_racefree.c +++ b/tests/regression/28-race_reach/74-tricky_address1_racefree.c @@ -1,4 +1,4 @@ -// PARAM: --enable ana.race.direct-arithmetic --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" +// PARAM: --enable ana.race.direct-arithmetic --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" --set lib.activated[+] sv-comp #include #include #include "racemacros.h" diff --git a/tests/regression/28-race_reach/75-tricky_address2_racefree.c b/tests/regression/28-race_reach/75-tricky_address2_racefree.c index 76b3b3752a..162eb8d13a 100644 --- a/tests/regression/28-race_reach/75-tricky_address2_racefree.c +++ b/tests/regression/28-race_reach/75-tricky_address2_racefree.c @@ -1,4 +1,4 @@ -// PARAM: --enable ana.race.direct-arithmetic --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" +// PARAM: --enable ana.race.direct-arithmetic --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" --set lib.activated[+] sv-comp #include #include #include "racemacros.h" diff --git a/tests/regression/28-race_reach/76-tricky_address3_racefree.c b/tests/regression/28-race_reach/76-tricky_address3_racefree.c index 1a782b670e..70c3d033b2 100644 --- a/tests/regression/28-race_reach/76-tricky_address3_racefree.c +++ b/tests/regression/28-race_reach/76-tricky_address3_racefree.c @@ -1,4 +1,4 @@ -// PARAM: --enable ana.race.direct-arithmetic --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" +// PARAM: --enable ana.race.direct-arithmetic --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" --set lib.activated[+] sv-comp #include #include #include "racemacros.h" diff --git a/tests/regression/28-race_reach/77-tricky_address4_racing.c b/tests/regression/28-race_reach/77-tricky_address4_racing.c index 5b189aa221..5fea171553 100644 --- a/tests/regression/28-race_reach/77-tricky_address4_racing.c +++ b/tests/regression/28-race_reach/77-tricky_address4_racing.c @@ -1,4 +1,4 @@ -// PARAM: --enable ana.race.direct-arithmetic --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" +// PARAM: --enable ana.race.direct-arithmetic --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" --set lib.activated[+] sv-comp #include #include #include "racemacros.h" diff --git a/tests/regression/28-race_reach/78-equ_racing.c b/tests/regression/28-race_reach/78-equ_racing.c index 32e10d5a02..b21d76b889 100644 --- a/tests/regression/28-race_reach/78-equ_racing.c +++ b/tests/regression/28-race_reach/78-equ_racing.c @@ -1,4 +1,4 @@ -// PARAM: --enable ana.race.direct-arithmetic --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" +// PARAM: --enable ana.race.direct-arithmetic --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" --set lib.activated[+] sv-comp #include #include #include "racemacros.h" diff --git a/tests/regression/28-race_reach/79-equ_racefree.c b/tests/regression/28-race_reach/79-equ_racefree.c index ba9affb71f..5b8744c322 100644 --- a/tests/regression/28-race_reach/79-equ_racefree.c +++ b/tests/regression/28-race_reach/79-equ_racefree.c @@ -1,4 +1,4 @@ -// PARAM: --enable ana.race.direct-arithmetic --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" +// PARAM: --enable ana.race.direct-arithmetic --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" --set lib.activated[+] sv-comp #include #include #include "racemacros.h" diff --git a/tests/regression/28-race_reach/81-list_racing.c b/tests/regression/28-race_reach/81-list_racing.c index c131e5c2a1..8b231f843c 100644 --- a/tests/regression/28-race_reach/81-list_racing.c +++ b/tests/regression/28-race_reach/81-list_racing.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.activated[+] "'region'" +// PARAM: --set ana.activated[+] "'region'" --set lib.activated[+] sv-comp #include #include "racemacros.h" diff --git a/tests/regression/28-race_reach/82-list_racefree.c b/tests/regression/28-race_reach/82-list_racefree.c index 67470cf8e0..5bab3c5c31 100644 --- a/tests/regression/28-race_reach/82-list_racefree.c +++ b/tests/regression/28-race_reach/82-list_racefree.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.activated[+] "'region'" +// PARAM: --set ana.activated[+] "'region'" --set lib.activated[+] sv-comp #include #include "racemacros.h" diff --git a/tests/regression/28-race_reach/83-list2_racing1.c b/tests/regression/28-race_reach/83-list2_racing1.c index 6002bc73d4..f0d9f9744b 100644 --- a/tests/regression/28-race_reach/83-list2_racing1.c +++ b/tests/regression/28-race_reach/83-list2_racing1.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.activated[+] "'region'" +// PARAM: --set ana.activated[+] "'region'" --set lib.activated[+] sv-comp #include #include "racemacros.h" diff --git a/tests/regression/28-race_reach/84-list2_racing2.c b/tests/regression/28-race_reach/84-list2_racing2.c index d807e2d0ac..ce15b2ddf5 100644 --- a/tests/regression/28-race_reach/84-list2_racing2.c +++ b/tests/regression/28-race_reach/84-list2_racing2.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.activated[+] "'region'" +// PARAM: --set ana.activated[+] "'region'" --set lib.activated[+] sv-comp #include #include "racemacros.h" diff --git a/tests/regression/28-race_reach/85-list2_racefree.c b/tests/regression/28-race_reach/85-list2_racefree.c index b0fb1baa83..cef0e1cb08 100644 --- a/tests/regression/28-race_reach/85-list2_racefree.c +++ b/tests/regression/28-race_reach/85-list2_racefree.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.activated[+] "'region'" +// PARAM: --set ana.activated[+] "'region'" --set lib.activated[+] sv-comp #include #include "racemacros.h" diff --git a/tests/regression/28-race_reach/86-lists_racing.c b/tests/regression/28-race_reach/86-lists_racing.c index 0548dcab37..e90b699212 100644 --- a/tests/regression/28-race_reach/86-lists_racing.c +++ b/tests/regression/28-race_reach/86-lists_racing.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.activated[+] "'region'" +// PARAM: --set ana.activated[+] "'region'" --set lib.activated[+] sv-comp #include #include "racemacros.h" diff --git a/tests/regression/28-race_reach/87-lists_racefree.c b/tests/regression/28-race_reach/87-lists_racefree.c index 0d05e5b2c2..8e51670b61 100644 --- a/tests/regression/28-race_reach/87-lists_racefree.c +++ b/tests/regression/28-race_reach/87-lists_racefree.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.activated[+] "'region'" +// PARAM: --set ana.activated[+] "'region'" --set lib.activated[+] sv-comp #include #include "racemacros.h" diff --git a/tests/regression/28-race_reach/90-arrayloop2_racing.c b/tests/regression/28-race_reach/90-arrayloop2_racing.c index 4859ed5a95..184d79af89 100644 --- a/tests/regression/28-race_reach/90-arrayloop2_racing.c +++ b/tests/regression/28-race_reach/90-arrayloop2_racing.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" --set ana.activated[+] "'region'" --set exp.region-offsets true +// PARAM: --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" --set ana.activated[+] "'region'" --set exp.region-offsets true --set lib.activated[+] sv-comp #include #include #include "racemacros.h" diff --git a/tests/regression/28-race_reach/91-arrayloop2_racefree.c b/tests/regression/28-race_reach/91-arrayloop2_racefree.c index 30ffa83e78..4461e78459 100644 --- a/tests/regression/28-race_reach/91-arrayloop2_racefree.c +++ b/tests/regression/28-race_reach/91-arrayloop2_racefree.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" --set ana.activated[+] "'region'" --set exp.region-offsets true +// PARAM: --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" --set ana.activated[+] "'region'" --set exp.region-offsets true --set lib.activated[+] sv-comp #include #include #include "racemacros.h" diff --git a/tests/regression/28-race_reach/92-evilcollapse_racing.c b/tests/regression/28-race_reach/92-evilcollapse_racing.c index af5bae0830..a33eb630f5 100644 --- a/tests/regression/28-race_reach/92-evilcollapse_racing.c +++ b/tests/regression/28-race_reach/92-evilcollapse_racing.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" --set ana.activated[+] "'region'" --set exp.region-offsets true +// PARAM: --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" --set ana.activated[+] "'region'" --set exp.region-offsets true --set lib.activated[+] sv-comp #include #include #include "racemacros.h" diff --git a/tests/regression/28-race_reach/93-evilcollapse_racefree.c b/tests/regression/28-race_reach/93-evilcollapse_racefree.c index e4ca831199..dee7d67659 100644 --- a/tests/regression/28-race_reach/93-evilcollapse_racefree.c +++ b/tests/regression/28-race_reach/93-evilcollapse_racefree.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" --set ana.activated[+] "'region'" --set exp.region-offsets true +// PARAM: --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" --set ana.activated[+] "'region'" --set exp.region-offsets true --set lib.activated[+] sv-comp #include #include #include "racemacros.h" diff --git a/tests/regression/28-race_reach/94-alloc_region_racing.c b/tests/regression/28-race_reach/94-alloc_region_racing.c index 74333bcab4..f985a9d91e 100644 --- a/tests/regression/28-race_reach/94-alloc_region_racing.c +++ b/tests/regression/28-race_reach/94-alloc_region_racing.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.activated[+] "'region'" --set exp.region-offsets true +// PARAM: --set ana.activated[+] "'region'" --set exp.region-offsets true --set lib.activated[+] sv-comp #include #include #include diff --git a/tests/regression/36-apron/15-globals-st.c b/tests/regression/36-apron/15-globals-st.c index 692d66f299..4c167ad742 100644 --- a/tests/regression/36-apron/15-globals-st.c +++ b/tests/regression/36-apron/15-globals-st.c @@ -1,4 +1,4 @@ -// SKIP PARAM: --set ana.activated[+] apron --set ana.path_sens[+] threadflag --disable ana.int.interval +// SKIP PARAM: --set ana.activated[+] apron --set ana.path_sens[+] threadflag --disable ana.int.interval --set lib.activated[+] sv-comp extern int __VERIFIER_nondet_int(); #include From 33414703562868d35f974bc2201238733959874c Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 4 Oct 2023 11:36:10 +0200 Subject: [PATCH 0784/1312] Try with `cil.addNestedScopeAttr` always set to `true` --- src/util/cilfacade.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/cilfacade.ml b/src/util/cilfacade.ml index 6ca9023324..7009512adf 100644 --- a/src/util/cilfacade.ml +++ b/src/util/cilfacade.ml @@ -41,8 +41,8 @@ let init_options () = Mergecil.merge_inlines := get_bool "cil.merge.inlines"; Cil.cstd := Cil.cstd_of_string (get_string "cil.cstd"); Cil.gnu89inline := get_bool "cil.gnu89inline"; - if get_bool "cil.addNestedScopeAttr" = true then - Cabs2cil.addNestedScopeAttr := true + (* if get_bool "cil.addNestedScopeAttr" = true then *) + Cabs2cil.addNestedScopeAttr := true let init () = initCIL (); From 1211a9fe95fb881e8d53a7ab474498c31d675b7e Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 4 Oct 2023 12:25:10 +0200 Subject: [PATCH 0785/1312] Use rand() in 77/10 --- tests/regression/77-mem-oob/10-oob-two-loops.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/regression/77-mem-oob/10-oob-two-loops.c b/tests/regression/77-mem-oob/10-oob-two-loops.c index 5208da5a0b..303aac242e 100644 --- a/tests/regression/77-mem-oob/10-oob-two-loops.c +++ b/tests/regression/77-mem-oob/10-oob-two-loops.c @@ -1,15 +1,17 @@ // PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval --disable warn.info --set sem.int.signed_overflow assume_none +#include + int main() { int *p = malloc(1048 * sizeof(int)); for (int i = 0; i < 1048; ++i) { - p[i] = __VERIFIER_nondet_int(); //NOWARN + p[i] = rand(); //NOWARN } int *q = p; while (*q >= 0 && q < p + 1048 * sizeof(int)) { //WARN - if (__VERIFIER_nondet_int()) { + if (rand()) { q++; } else { (*q)--; //WARN From 058990eb5a9413b08997fbc2d6ebdf52203290e3 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 4 Oct 2023 12:38:27 +0200 Subject: [PATCH 0786/1312] Fix test description --- tests/regression/00-sanity/37-long-double.t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression/00-sanity/37-long-double.t b/tests/regression/00-sanity/37-long-double.t index 60cdb8f612..567db89e5a 100644 --- a/tests/regression/00-sanity/37-long-double.t +++ b/tests/regression/00-sanity/37-long-double.t @@ -1,4 +1,4 @@ -Testing that there is warning about treating long double as double constant. +Testing that there isn't a warning about treating long double as double constant. $ goblint 37-long-double.c [Info][Deadcode] Logical lines of code (LLoC) summary: live: 3 From 20396754a27385fdc788557ef30704da339ae8cc Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 4 Oct 2023 12:45:14 +0200 Subject: [PATCH 0787/1312] Activate `cil.addNestedScopeAttr` in cilfacade only conditionally --- src/util/cilfacade.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/cilfacade.ml b/src/util/cilfacade.ml index 7009512adf..7ac3e29ee0 100644 --- a/src/util/cilfacade.ml +++ b/src/util/cilfacade.ml @@ -41,8 +41,8 @@ let init_options () = Mergecil.merge_inlines := get_bool "cil.merge.inlines"; Cil.cstd := Cil.cstd_of_string (get_string "cil.cstd"); Cil.gnu89inline := get_bool "cil.gnu89inline"; - (* if get_bool "cil.addNestedScopeAttr" = true then *) - Cabs2cil.addNestedScopeAttr := true + if get_bool "cil.addNestedScopeAttr" then + Cabs2cil.addNestedScopeAttr := true let init () = initCIL (); From fb4979b567d4664c9012d3d98a9a32ba2f0b39d9 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 4 Oct 2023 12:46:11 +0200 Subject: [PATCH 0788/1312] Activate SV-COMP memory-safety-related options before CIL has completely parsed the program --- src/autoTune.ml | 21 +++++++++++++-------- src/goblint.ml | 2 ++ 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/autoTune.ml b/src/autoTune.ml index 822195b1f6..186d930189 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -210,15 +210,8 @@ let activateLongjmpAnalysesWhenRequired () = enableAnalyses longjmpAnalyses; ) -let focusOnSpecification () = +let focusOnMemSafetySpecification () = match Svcomp.Specification.of_option () with - | UnreachCall s -> () - | NoDataRace -> (*enable all thread analyses*) - print_endline @@ "Specification: NoDataRace -> enabling thread analyses \"" ^ (String.concat ", " notNeccessaryThreadAnalyses) ^ "\""; - enableAnalyses notNeccessaryThreadAnalyses; - | NoOverflow -> (*We focus on integer analysis*) - set_bool "ana.int.def_exc" true; - set_bool "ana.int.interval" true | ValidFree -> (* Enable the useAfterFree analysis *) let uafAna = ["useAfterFree"] in print_endline @@ "Specification: ValidFree -> enabling useAfterFree analysis \"" ^ (String.concat ", " uafAna) ^ "\""; @@ -247,6 +240,18 @@ let focusOnSpecification () = ); let memSafetyAnas = ["memOutOfBounds"; "memLeak"; "useAfterFree";] in enableAnalyses memSafetyAnas) + | _ -> () + +let focusOnSpecification () = + match Svcomp.Specification.of_option () with + | UnreachCall s -> () + | NoDataRace -> (*enable all thread analyses*) + print_endline @@ "Specification: NoDataRace -> enabling thread analyses \"" ^ (String.concat ", " notNeccessaryThreadAnalyses) ^ "\""; + enableAnalyses notNeccessaryThreadAnalyses; + | NoOverflow -> (*We focus on integer analysis*) + set_bool "ana.int.def_exc" true; + set_bool "ana.int.interval" true + | _ -> () (*Detect enumerations and enable the "ana.int.enums" option*) exception EnumFound diff --git a/src/goblint.ml b/src/goblint.ml index 4ea3a3d242..45dae3a4c6 100644 --- a/src/goblint.ml +++ b/src/goblint.ml @@ -56,6 +56,8 @@ let main () = else None in + if AutoTune.isActivated "specification" && get_string "ana.specification" <> "" then + AutoTune.focusOnMemSafetySpecification (); (* This is run independant of the autotuner being enabled or not be sound for programs with longjmp *) AutoTune.activateLongjmpAnalysesWhenRequired (); if get_bool "ana.autotune.enabled" then AutoTune.chooseConfig file; From 1eb7309b472c1392b1812fecf4adb1b2b756bb05 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 4 Oct 2023 13:59:05 +0200 Subject: [PATCH 0789/1312] Fix again scoping check --- src/analyses/memOutOfBounds.ml | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index aadf433a6e..655ed7d3f1 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -89,6 +89,10 @@ struct let pts_elems_to_sizes (addr: Queries.AD.elt) = begin match addr with | Addr (v, _) -> + if hasAttribute "goblint_cil_nested" v.vattr then ( + set_mem_safety_flag InvalidDeref; + M.warn "Var %a is potentially accessed out-of-scope. Invalid memory access may occur" CilType.Varinfo.pretty v + ); begin match v.vtype with | TArray (item_typ, _, _) -> let item_typ_size_in_bytes = size_of_type_in_bytes item_typ in @@ -233,7 +237,6 @@ struct ID.top_of @@ Cilfacade.ptrdiff_ikind () and check_lval_for_oob_access ctx ?(is_implicitly_derefed = false) lval = - check_lval_out_of_scope_access lval; (* If the lval does not contain a pointer or if it does contain a pointer, but only points to string addresses, then no need to WARN *) if (not @@ lval_contains_a_ptr lval) || ptr_only_has_str_addr ctx (Lval lval) then () else @@ -252,15 +255,6 @@ struct | _ -> check_exp_for_oob_access ctx ~is_implicitly_derefed e end - and check_lval_out_of_scope_access lval = - match lval with - | (Var v, _) -> - if hasAttribute "goblint_cil_nested" v.vattr then ( - set_mem_safety_flag InvalidDeref; - M.warn "Lvalue %a is potentially accessed out-of-scope. Invalid memory access may occur" d_lval lval - ) - | _ -> () - and check_no_binop_deref ctx lval_exp = check_unknown_addr_deref ctx lval_exp; let behavior = Undefined MemoryOutOfBoundsAccess in From 093eb5ef686078baf906cc0d932a6a0c64c24053 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 4 Oct 2023 15:52:00 +0300 Subject: [PATCH 0790/1312] Extract cpp special path regex construction --- src/maingoblint.ml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/maingoblint.ml b/src/maingoblint.ml index 155faa0e76..7808cbcd3f 100644 --- a/src/maingoblint.ml +++ b/src/maingoblint.ml @@ -410,6 +410,10 @@ let preprocess_files () = ); preprocessed +(** Regex for special "paths" in cpp output: + , , but also translations! *) +let special_path_regexp = Str.regexp "<.+>" + (** Parse preprocessed files *) let parse_preprocessed preprocessed = (* get the AST *) @@ -417,7 +421,8 @@ let parse_preprocessed preprocessed = let goblint_cwd = GobFpath.cwd () in let get_ast_and_record_deps (preprocessed_file, task_opt) = - let transform_file (path_str, system_header) = if Str.string_match (Str.regexp "<.+>") path_str 0 then + let transform_file (path_str, system_header) = + if Str.string_match special_path_regexp path_str 0 then (path_str, system_header) (* ignore special "paths" *) else let path = Fpath.v path_str in From a840e412a7eedcd3cd6a2c8b3166e80acb4b8f2b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 4 Oct 2023 16:40:37 +0300 Subject: [PATCH 0791/1312] Extract most library extensions to std dune library --- src/dune | 19 ++++++++++--------- src/goblint_lib.ml | 17 +++-------------- src/util/std/dune | 17 +++++++++++++++++ src/util/{ => std}/gobFpath.ml | 0 src/util/{ => std}/gobGc.ml | 0 src/util/{ => std}/gobHashtbl.ml | 0 src/util/{ => std}/gobList.ml | 0 src/util/{ => std}/gobOption.ml | 0 src/util/{ => std}/gobPretty.ml | 0 src/util/{ => std}/gobRef.ml | 0 src/util/{ => std}/gobResult.ml | 0 src/util/{ => std}/gobSys.ml | 0 src/util/{ => std}/gobUnix.ml | 0 src/util/{ => std}/gobYaml.ml | 0 src/util/{ => std}/gobYojson.ml | 0 src/util/{ => std}/gobZ.ml | 0 src/util/std/goblint_std.ml | 24 ++++++++++++++++++++++++ 17 files changed, 54 insertions(+), 23 deletions(-) create mode 100644 src/util/std/dune rename src/util/{ => std}/gobFpath.ml (100%) rename src/util/{ => std}/gobGc.ml (100%) rename src/util/{ => std}/gobHashtbl.ml (100%) rename src/util/{ => std}/gobList.ml (100%) rename src/util/{ => std}/gobOption.ml (100%) rename src/util/{ => std}/gobPretty.ml (100%) rename src/util/{ => std}/gobRef.ml (100%) rename src/util/{ => std}/gobResult.ml (100%) rename src/util/{ => std}/gobSys.ml (100%) rename src/util/{ => std}/gobUnix.ml (100%) rename src/util/{ => std}/gobYaml.ml (100%) rename src/util/{ => std}/gobYojson.ml (100%) rename src/util/{ => std}/gobZ.ml (100%) create mode 100644 src/util/std/goblint_std.ml diff --git a/src/dune b/src/dune index 85944375ea..a8cda818b1 100644 --- a/src/dune +++ b/src/dune @@ -7,7 +7,7 @@ (name goblint_lib) (public_name goblint.lib) (modules :standard \ goblint mainspec privPrecCompare apronPrecCompare messagesCompare) - (libraries goblint.sites goblint.build-info goblint-cil.all-features batteries.unthreaded qcheck-core.runner sha json-data-encoding jsonrpc cpu arg-complete fpath yaml yaml.unix uuidm goblint_timing catapult goblint_backtrace fileutils + (libraries goblint.sites goblint.build-info goblint-cil.all-features batteries.unthreaded qcheck-core.runner sha json-data-encoding jsonrpc cpu arg-complete fpath yaml yaml.unix uuidm goblint_timing catapult goblint_backtrace fileutils goblint_std ; Conditionally compile based on whether apron optional dependency is installed or not. ; Alternative dependencies seem like the only way to optionally depend on optional dependencies. ; See: https://dune.readthedocs.io/en/stable/concepts.html#alternative-dependencies. @@ -56,6 +56,7 @@ (-> violationZ3.no-z3.ml) ) ) + (flags :standard -open Goblint_std) (foreign_stubs (language c) (names stubs)) (ocamlopt_flags :standard -no-float-const-prop) (preprocess @@ -77,33 +78,33 @@ (public_names goblint -) (modes byte native) ; https://dune.readthedocs.io/en/stable/dune-files.html#linking-modes (modules goblint mainspec) - (libraries goblint.lib goblint.sites.dune goblint.build-info.dune) + (libraries goblint.lib goblint.sites.dune goblint.build-info.dune goblint_std) (preprocess (pps ppx_deriving.std ppx_deriving_hash ppx_deriving_yojson)) - (flags :standard -linkall) + (flags :standard -linkall -open Goblint_std) ) (executable (name privPrecCompare) (modules privPrecCompare) - (libraries goblint.lib goblint.sites.dune goblint.build-info.dune) + (libraries goblint.lib goblint.sites.dune goblint.build-info.dune goblint_std) (preprocess (pps ppx_deriving.std ppx_deriving_hash ppx_deriving_yojson)) - (flags :standard -linkall) + (flags :standard -linkall -open Goblint_std) ) (executable (name apronPrecCompare) (modules apronPrecCompare) - (libraries goblint.lib goblint.sites.dune goblint.build-info.dune) + (libraries goblint.lib goblint.sites.dune goblint.build-info.dune goblint_std) (preprocess (pps ppx_deriving.std ppx_deriving_hash ppx_deriving_yojson)) - (flags :standard -linkall) + (flags :standard -linkall -open Goblint_std) ) (executable (name messagesCompare) (modules messagesCompare) - (libraries goblint.lib goblint.sites.dune goblint.build-info.dune) + (libraries goblint.lib goblint.sites.dune goblint.build-info.dune goblint_std) (preprocess (pps ppx_deriving.std ppx_deriving_hash ppx_deriving_yojson)) - (flags :standard -linkall) + (flags :standard -linkall -open Goblint_std) ) (rule diff --git a/src/goblint_lib.ml b/src/goblint_lib.ml index 6e700485dd..816a69faff 100644 --- a/src/goblint_lib.ml +++ b/src/goblint_lib.ml @@ -469,29 +469,18 @@ module ConfigVersion = ConfigVersion (** {1 Library extensions} - OCaml library extensions which are completely independent of Goblint. *) + OCaml library extensions which are completely independent of Goblint. + + See {!Goblint_std}. *) (** {2 Standard library} OCaml standard library extensions which are not provided by {!Batteries}. *) module GobFormat = GobFormat -module GobGc = GobGc -module GobHashtbl = GobHashtbl -module GobList = GobList -module GobRef = GobRef -module GobResult = GobResult -module GobOption = GobOption -module GobSys = GobSys -module GobUnix = GobUnix (** {2 Other libraries} External library extensions. *) -module GobFpath = GobFpath -module GobPretty = GobPretty -module GobYaml = GobYaml -module GobYojson = GobYojson -module GobZ = GobZ module MyCheck = MyCheck diff --git a/src/util/std/dune b/src/util/std/dune new file mode 100644 index 0000000000..c85710a8d6 --- /dev/null +++ b/src/util/std/dune @@ -0,0 +1,17 @@ +(include_subdirs no) + +(library + (name goblint_std) + (public_name goblint.std) + (libraries + batteries + zarith + goblint-cil + fpath + yojson + yaml) + (preprocess + (pps + ppx_deriving.std + ppx_deriving_hash + ppx_deriving_yojson))) diff --git a/src/util/gobFpath.ml b/src/util/std/gobFpath.ml similarity index 100% rename from src/util/gobFpath.ml rename to src/util/std/gobFpath.ml diff --git a/src/util/gobGc.ml b/src/util/std/gobGc.ml similarity index 100% rename from src/util/gobGc.ml rename to src/util/std/gobGc.ml diff --git a/src/util/gobHashtbl.ml b/src/util/std/gobHashtbl.ml similarity index 100% rename from src/util/gobHashtbl.ml rename to src/util/std/gobHashtbl.ml diff --git a/src/util/gobList.ml b/src/util/std/gobList.ml similarity index 100% rename from src/util/gobList.ml rename to src/util/std/gobList.ml diff --git a/src/util/gobOption.ml b/src/util/std/gobOption.ml similarity index 100% rename from src/util/gobOption.ml rename to src/util/std/gobOption.ml diff --git a/src/util/gobPretty.ml b/src/util/std/gobPretty.ml similarity index 100% rename from src/util/gobPretty.ml rename to src/util/std/gobPretty.ml diff --git a/src/util/gobRef.ml b/src/util/std/gobRef.ml similarity index 100% rename from src/util/gobRef.ml rename to src/util/std/gobRef.ml diff --git a/src/util/gobResult.ml b/src/util/std/gobResult.ml similarity index 100% rename from src/util/gobResult.ml rename to src/util/std/gobResult.ml diff --git a/src/util/gobSys.ml b/src/util/std/gobSys.ml similarity index 100% rename from src/util/gobSys.ml rename to src/util/std/gobSys.ml diff --git a/src/util/gobUnix.ml b/src/util/std/gobUnix.ml similarity index 100% rename from src/util/gobUnix.ml rename to src/util/std/gobUnix.ml diff --git a/src/util/gobYaml.ml b/src/util/std/gobYaml.ml similarity index 100% rename from src/util/gobYaml.ml rename to src/util/std/gobYaml.ml diff --git a/src/util/gobYojson.ml b/src/util/std/gobYojson.ml similarity index 100% rename from src/util/gobYojson.ml rename to src/util/std/gobYojson.ml diff --git a/src/util/gobZ.ml b/src/util/std/gobZ.ml similarity index 100% rename from src/util/gobZ.ml rename to src/util/std/gobZ.ml diff --git a/src/util/std/goblint_std.ml b/src/util/std/goblint_std.ml new file mode 100644 index 0000000000..e716d1df5b --- /dev/null +++ b/src/util/std/goblint_std.ml @@ -0,0 +1,24 @@ +(** OCaml library extensions which are completely independent of Goblint. *) + +(** {1 Standard library} + + OCaml standard library extensions which are not provided by {!Batteries}. *) + +module GobGc = GobGc +module GobHashtbl = GobHashtbl +module GobList = GobList +module GobRef = GobRef +module GobResult = GobResult +module GobOption = GobOption +module GobSys = GobSys +module GobUnix = GobUnix + +(** {1 Other libraries} + + External library extensions. *) + +module GobFpath = GobFpath +module GobPretty = GobPretty +module GobYaml = GobYaml +module GobYojson = GobYojson +module GobZ = GobZ From 4a4b6abc6edd18d1487084131a4f3bc038fe526f Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 4 Oct 2023 19:48:48 +0200 Subject: [PATCH 0792/1312] Fix conditional enabling of `cil.addNestedScopeAttr` --- src/goblint.ml | 2 -- src/maingoblint.ml | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/goblint.ml b/src/goblint.ml index 45dae3a4c6..4ea3a3d242 100644 --- a/src/goblint.ml +++ b/src/goblint.ml @@ -56,8 +56,6 @@ let main () = else None in - if AutoTune.isActivated "specification" && get_string "ana.specification" <> "" then - AutoTune.focusOnMemSafetySpecification (); (* This is run independant of the autotuner being enabled or not be sound for programs with longjmp *) AutoTune.activateLongjmpAnalysesWhenRequired (); if get_bool "ana.autotune.enabled" then AutoTune.chooseConfig file; diff --git a/src/maingoblint.ml b/src/maingoblint.ml index 155faa0e76..0f061a3507 100644 --- a/src/maingoblint.ml +++ b/src/maingoblint.ml @@ -185,6 +185,8 @@ let handle_options () = check_arguments (); AfterConfig.run (); Sys.set_signal (GobSys.signal_of_string (get_string "dbg.solver-signal")) Signal_ignore; (* Ignore solver-signal before solving (e.g. MyCFG), otherwise exceptions self-signal the default, which crashes instead of printing backtrace. *) + if AutoTune.isActivated "specification" && get_string "ana.specification" <> "" then + AutoTune.focusOnMemSafetySpecification (); Cilfacade.init_options (); handle_flags () From 395c30db266591bc2e20b663274628278bb3b6f0 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 4 Oct 2023 20:39:15 +0200 Subject: [PATCH 0793/1312] Warn for invalid deallocation when encountering invalid addresses --- src/analyses/base.ml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 81bd29f8d0..a9457ca41b 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2039,7 +2039,9 @@ struct AnalysisStateUtil.set_mem_safety_flag InvalidFree; M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 761] "Free of memory not at start of buffer in function %s for pointer %a" special_fn.vname d_exp ptr ) - | _ -> M.warn ~category:MessageCategory.Analyzer "Pointer %a in function %s doesn't evaluate to a valid address." d_exp ptr special_fn.vname + | _ -> + AnalysisStateUtil.set_mem_safety_flag InvalidFree; + M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Pointer %a in function %s doesn't evaluate to a valid address. Invalid memory deallocation may occur" d_exp ptr special_fn.vname let special ctx (lv:lval option) (f: varinfo) (args: exp list) = From 055d9cc01b2fa8332c00e9011183d18cd2bf2fc6 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 4 Oct 2023 20:39:39 +0200 Subject: [PATCH 0794/1312] Add invalid address test case for invalid deallocation --- .../10-invalid-dealloc-union.c | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 tests/regression/75-invalid_dealloc/10-invalid-dealloc-union.c diff --git a/tests/regression/75-invalid_dealloc/10-invalid-dealloc-union.c b/tests/regression/75-invalid_dealloc/10-invalid-dealloc-union.c new file mode 100644 index 0000000000..be1eaa056d --- /dev/null +++ b/tests/regression/75-invalid_dealloc/10-invalid-dealloc-union.c @@ -0,0 +1,42 @@ +extern void abort(void); +#include + +extern int __VERIFIER_nondet_int(void); + +int main() +{ + union { + void *p0; + + struct { + char c[2]; + int p1; + int p2; + } str; + + } data; + + // alloc 37B on heap + data.p0 = malloc(37U); + + // avoid introducing a memleak + void *ptr = data.p0; + + // this should be fine + if(__VERIFIER_nondet_int()) { + data.str.p2 = 20; + } else { + data.str.p2 = 30; + } + + if(25 > data.str.p2) { + // avoids memleak + data.str.c[1] = sizeof data.str.p1; + } + + // invalid free() + free(data.p0);//WARN + + free(ptr);//NOWARN + return 0; +} From 2c883eb32cc67f17a48bb4ff7aca73f5811a056b Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 4 Oct 2023 21:58:30 +0200 Subject: [PATCH 0795/1312] Warn if lval contains an address with a non-local var --- src/analyses/base.ml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index a9457ca41b..a323e5f270 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1051,7 +1051,18 @@ struct else if AD.may_be_null adr then ( AnalysisStateUtil.set_mem_safety_flag InvalidDeref; M.warn ~category:M.Category.Behavior.Undefined.nullpointer_dereference ~tags:[CWE 476] "May dereference NULL pointer" - ) + ); + (* Warn if any of the addresses contains a non-local variable *) + AD.iter (function + | AD.Addr.Addr (v,o) -> + if not @@ CPA.mem v st.cpa then ( + (* TODO: Not the smartest move to set the global flag within an iter *) + (* TODO: We can resort to using AD.exists instead *) + AnalysisStateUtil.set_mem_safety_flag InvalidDeref; + M.warn "lval %a points to non-local variable %a. Invalid pointer dereference may occur" d_lval lval CilType.Varinfo.pretty v + ) + | _ -> () + ) adr ); AD.map (add_offset_varinfo (convert_offset a gs st ofs)) adr | _ -> From 5cb10f61925e55932475e859569d496a630fee6a Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 4 Oct 2023 22:09:51 +0200 Subject: [PATCH 0796/1312] Don't warn for non-local vars in address set if they're globals --- src/analyses/base.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index a323e5f270..a5b60e8cca 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1055,7 +1055,7 @@ struct (* Warn if any of the addresses contains a non-local variable *) AD.iter (function | AD.Addr.Addr (v,o) -> - if not @@ CPA.mem v st.cpa then ( + if not (CPA.mem v st.cpa) && not (is_global a v) then ( (* TODO: Not the smartest move to set the global flag within an iter *) (* TODO: We can resort to using AD.exists instead *) AnalysisStateUtil.set_mem_safety_flag InvalidDeref; From ea4410d8d0939d93f5effa56fd51c9549c717641 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 4 Oct 2023 22:10:11 +0200 Subject: [PATCH 0797/1312] Add test cases for invalid deref due to scoping --- .../01-scopes-no-static.c | 22 ++++++++ .../02-scopes-global-var.c | 29 +++++++++++ .../03-scopes-static.c | 52 +++++++++++++++++++ 3 files changed, 103 insertions(+) create mode 100644 tests/regression/78-invalid-deref-scopes/01-scopes-no-static.c create mode 100644 tests/regression/78-invalid-deref-scopes/02-scopes-global-var.c create mode 100644 tests/regression/78-invalid-deref-scopes/03-scopes-static.c diff --git a/tests/regression/78-invalid-deref-scopes/01-scopes-no-static.c b/tests/regression/78-invalid-deref-scopes/01-scopes-no-static.c new file mode 100644 index 0000000000..e0c4b47b73 --- /dev/null +++ b/tests/regression/78-invalid-deref-scopes/01-scopes-no-static.c @@ -0,0 +1,22 @@ +// PARAM: --set ana.activated[+] memOutOfBounds +// TODO: I haven't checked why, but we need memOutOfBounds for this case +extern int printf ( const char * format, ... ); + +int *foo2(void) +{ + int arr[1024]; + arr[194] = 13; + return arr + 1; +} + +int *foo(void) +{ + int arr[123]; + return foo2(); +} + +int main(void) { + int *a = foo(); + printf("%d\n", *a);//WARN + return 0; +} diff --git a/tests/regression/78-invalid-deref-scopes/02-scopes-global-var.c b/tests/regression/78-invalid-deref-scopes/02-scopes-global-var.c new file mode 100644 index 0000000000..9491e1c574 --- /dev/null +++ b/tests/regression/78-invalid-deref-scopes/02-scopes-global-var.c @@ -0,0 +1,29 @@ +int array[10]; + +// function returns array of numbers +int* getNumbers(void) { + for (int i = 0; i < 10; ++i) { + array[i] = i;//NOWARN + } + + return array; +} + +int* getNumbers2(void) { + int* numbers = getNumbers(); + // numbers2 is local + int numbers2[10]; + + for (int i = 0; i < 10; ++i) { + numbers2[i] = numbers[i];//NOWARN + } + + return numbers2; +} + +int main(void) { + int *numbers = getNumbers2(); + numbers[0] = 100;//WARN + + return 0; +} diff --git a/tests/regression/78-invalid-deref-scopes/03-scopes-static.c b/tests/regression/78-invalid-deref-scopes/03-scopes-static.c new file mode 100644 index 0000000000..c13b665c84 --- /dev/null +++ b/tests/regression/78-invalid-deref-scopes/03-scopes-static.c @@ -0,0 +1,52 @@ +extern int printf (const char* format, ...); + +// function returns array of numbers +int* getNumbers() { + + static int array[10]; + + for (int i = 0; i < 10; ++i) { + array[i] = i;//NOWARN + } + + return array; +} + +int* getNumbers2() { + int* numbers = getNumbers(); + static int numbers2[10]; + for (int i = 0; i < 10; ++i) { + numbers2[i] = numbers[i];//NOWARN + } + return numbers2; +} + +int* getNumbers3() { + int* numbers = getNumbers2(); + int numbers3[10]; + for (int i = 0; i < 10; ++i) { + numbers3[i] = numbers[i];//NOWARN + } + + return numbers3; +} + +int* getNumbers4() { + int* numbers = getNumbers3(); + static int numbers4[10]; + for (int i = 0; i < 10; ++i) { + numbers4[i] = numbers[i];//WARN + } + return numbers4; +} + +int main (void) { + + int *numbers = getNumbers4(); + + for (int i = 0; i < 10; i++ ) { + printf( "%d\n", *(numbers + i));//NOWARN + } + + return 0; +} From 6745d799de882a056df9dd0c695668592a5f31eb Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 4 Oct 2023 22:49:37 +0200 Subject: [PATCH 0798/1312] Slightly refactor `check_count` and check for `src`'s size in `memcpy` --- src/analyses/memOutOfBounds.ml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 655ed7d3f1..8a2ca12467 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -372,22 +372,22 @@ struct | _ -> () (* For memset() and memcpy() *) - let check_count ctx fun_name dest n = + let check_count ctx fun_name ptr n = let (behavior:MessageCategory.behavior) = Undefined MemoryOutOfBoundsAccess in let cwe_number = 823 in - let dest_size = get_size_of_ptr_target ctx dest in + let ptr_size = get_size_of_ptr_target ctx ptr in let eval_n = ctx.ask (Queries.EvalInt n) in - let addr_offs = get_addr_offs ctx dest in - match dest_size, eval_n with + let addr_offs = get_addr_offs ctx ptr in + match ptr_size, eval_n with | `Top, _ -> set_mem_safety_flag InvalidDeref; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest %a in function %s is unknown. Memory out-of-bounds access might occur" d_exp dest fun_name + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest %a in function %s is unknown. Memory out-of-bounds access might occur" d_exp ptr fun_name | _, `Top -> set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Count parameter, passed to function %s is unknown. Memory out-of-bounds access might occur" fun_name | `Bot, _ -> set_mem_safety_flag InvalidDeref; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest %a in function %s is bottom. Memory out-of-bounds access might occur" d_exp dest fun_name + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest %a in function %s is bottom. Memory out-of-bounds access might occur" d_exp ptr fun_name | _, `Bot -> set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Count parameter, passed to function %s is bottom" fun_name @@ -399,7 +399,7 @@ struct begin match ID.to_bool dest_size_lt_count with | Some true -> set_mem_safety_flag InvalidDeref; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest in function %s is %a (in bytes) with an address offset of %a (in bytes). Count is %a (in bytes). Memory out-of-bounds access must occur" fun_name ID.pretty casted_ds ID.pretty casted_ao ID.pretty casted_en + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of %a in function %s is %a (in bytes) with an address offset of %a (in bytes). Count is %a (in bytes). Memory out-of-bounds access must occur" d_exp ptr fun_name ID.pretty casted_ds ID.pretty casted_ao ID.pretty casted_en | Some false -> () | None -> set_mem_safety_flag InvalidDeref; @@ -436,7 +436,9 @@ struct (* Check calls to memset and memcpy for out-of-bounds-accesses *) match desc.special arglist with | Memset { dest; ch; count; } -> check_count ctx f.vname dest count; - | Memcpy { dest; src; n = count; } -> check_count ctx f.vname dest count; + | Memcpy { dest; src; n = count; } -> + check_count ctx f.vname src count; + check_count ctx f.vname dest count; | _ -> (); ctx.local From 136bec0232107c61a42a3ed369a03aee90fbf2b8 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 4 Oct 2023 22:50:41 +0200 Subject: [PATCH 0799/1312] Add test case with wrong src size for memcpy --- .../regression/77-mem-oob/12-memcpy-oob-src.c | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 tests/regression/77-mem-oob/12-memcpy-oob-src.c diff --git a/tests/regression/77-mem-oob/12-memcpy-oob-src.c b/tests/regression/77-mem-oob/12-memcpy-oob-src.c new file mode 100644 index 0000000000..0f3a609fbe --- /dev/null +++ b/tests/regression/77-mem-oob/12-memcpy-oob-src.c @@ -0,0 +1,43 @@ +// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval --disable warn.info +// TODO: The "--disable warn.info" part is a temporary fix and needs to be removed once the MacOS CI job is fixed +#include +#include + +struct A { + unsigned char a; + unsigned char b:2; + unsigned char c:2; + unsigned char d:5; + unsigned char e; +} __attribute__((packed)); + +struct A d; +int main(void) +{ + struct A *p; + p = malloc(5); + d.a = 1; + d.b = 2; + d.c = 3; + d.d = 4; + d.e = 5; + // It's an OOB error, because sizeof(d) == 4 + memcpy(p, &d, 5); //WARN + if (p->a != 1) { + free(p); + } + if (p->b != 2) { + free(p); + } + if (p->c != 3) { + free(p); + } + if (p->d != 4) { + free(p); + } + if (p->e != 5) { + free(p); + } + free(p); +} + From 3a2fe3f09278fc80fe8e6b68c3697f6996bf7030 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 4 Oct 2023 22:53:50 +0200 Subject: [PATCH 0800/1312] Correct test 77/07 to warn for src OOB access in memcpy as well --- tests/regression/77-mem-oob/07-memcpy-oob.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/regression/77-mem-oob/07-memcpy-oob.c b/tests/regression/77-mem-oob/07-memcpy-oob.c index 012f92996e..5605404a87 100644 --- a/tests/regression/77-mem-oob/07-memcpy-oob.c +++ b/tests/regression/77-mem-oob/07-memcpy-oob.c @@ -31,13 +31,13 @@ int main(int argc, char const *argv[]) { memcpy(a, b, 40); //WARN memcpy(a, b, sizeof(a)); //WARN - memcpy(b, a, 60); //NOWARN + memcpy(b, a, 60); //WARN b += 1; memcpy(b, a, 60); //WARN s *s_ptr = malloc(sizeof(s)); - memcpy(s_ptr, a, sizeof(s)); //NOWARN + memcpy(s_ptr, a, sizeof(s)); //WARN memcpy(s_ptr->a, 0, sizeof(s)); //WARN memcpy(s_ptr->b, 0, sizeof(s)); //WARN From 0776dbe5423c38aa74acfbdc1cdd88c148e2051d Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 5 Oct 2023 12:02:14 +0300 Subject: [PATCH 0801/1312] Add __builtin_clzll to library functions --- src/analyses/libraryFunctions.ml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 9ee9dc8c9d..7695844dd0 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -374,6 +374,8 @@ let gcc_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__builtin_ctzl", unknown [drop "x" []]); ("__builtin_ctzll", unknown [drop "x" []]); ("__builtin_clz", unknown [drop "x" []]); + ("__builtin_clzl", unknown [drop "x" []]); + ("__builtin_clzll", unknown [drop "x" []]); ("__builtin_object_size", unknown [drop "ptr" [r]; drop' []]); ("__builtin_prefetch", unknown (drop "addr" [] :: VarArgs (drop' []))); ("__builtin_expect", special [__ "exp" []; drop' []] @@ fun exp -> Identity exp); (* Identity, because just compiler optimization annotation. *) From 7b76751a6cc2100f7c303c481fe28c0b7a4737a4 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 5 Oct 2023 12:37:54 +0300 Subject: [PATCH 0802/1312] Move config modules to build_info dune library --- src/build-info/.gitignore | 1 + ...blint_build_info.ml => dune_build_info.ml} | 0 ...blint_build_info.ml => dune_build_info.ml} | 0 src/build-info/dune | 20 ++++++++++- ...int_build_info.mli => dune_build_info.mli} | 0 src/build-info/goblint_build_info.ml | 34 +++++++++++++++++++ src/dune | 16 --------- src/framework/control.ml | 2 +- src/goblint_lib.ml | 15 -------- src/maingoblint.ml | 6 ++-- src/util/sarif.ml | 2 +- src/util/tracing.ml | 2 +- src/version.ml | 16 --------- src/witness/witness.ml | 2 +- src/witness/yamlWitness.ml | 2 +- 15 files changed, 62 insertions(+), 56 deletions(-) create mode 100644 src/build-info/.gitignore rename src/build-info/build_info_dune/{goblint_build_info.ml => dune_build_info.ml} (100%) rename src/build-info/build_info_js/{goblint_build_info.ml => dune_build_info.ml} (100%) rename src/build-info/{goblint_build_info.mli => dune_build_info.mli} (100%) create mode 100644 src/build-info/goblint_build_info.ml delete mode 100644 src/version.ml diff --git a/src/build-info/.gitignore b/src/build-info/.gitignore new file mode 100644 index 0000000000..8afff91d71 --- /dev/null +++ b/src/build-info/.gitignore @@ -0,0 +1 @@ +config*.ml diff --git a/src/build-info/build_info_dune/goblint_build_info.ml b/src/build-info/build_info_dune/dune_build_info.ml similarity index 100% rename from src/build-info/build_info_dune/goblint_build_info.ml rename to src/build-info/build_info_dune/dune_build_info.ml diff --git a/src/build-info/build_info_js/goblint_build_info.ml b/src/build-info/build_info_js/dune_build_info.ml similarity index 100% rename from src/build-info/build_info_js/goblint_build_info.ml rename to src/build-info/build_info_js/dune_build_info.ml diff --git a/src/build-info/dune b/src/build-info/dune index 89ae841778..c1de250263 100644 --- a/src/build-info/dune +++ b/src/build-info/dune @@ -8,4 +8,22 @@ (library (name goblint_build_info) (public_name goblint.build-info) - (virtual_modules goblint_build_info)) + (libraries batteries.unthreaded) + (virtual_modules dune_build_info)) + +(rule + (target configVersion.ml) + (mode (promote (until-clean) (only configVersion.ml))) ; replace existing file in source tree, even if releasing (only overrides) + (deps (universe)) ; do not cache, always regenerate + (action (pipe-stdout (bash "git describe --all --long --dirty || echo \"n/a\"") (with-stdout-to %{target} (bash "xargs printf '(* Automatically regenerated, changes do not persist! *)\nlet version = \"%s\"'"))))) + +(rule + (target configProfile.ml) + (mode (promote (until-clean) (only configProfile.ml))) ; replace existing file in source tree, even if releasing (only overrides) + (action (write-file %{target} "(* Automatically regenerated, changes do not persist! *)\nlet profile = \"%{profile}\""))) + +(rule + (target configOcaml.ml) + (mode (promote (until-clean) (only configOcaml.ml))) ; replace existing file in source tree, even if releasing (only overrides) + (action (write-file %{target} "(* Automatically regenerated, changes do not persist! *)\nlet flambda = \"%{ocaml-config:flambda}\""))) + diff --git a/src/build-info/goblint_build_info.mli b/src/build-info/dune_build_info.mli similarity index 100% rename from src/build-info/goblint_build_info.mli rename to src/build-info/dune_build_info.mli diff --git a/src/build-info/goblint_build_info.ml b/src/build-info/goblint_build_info.ml new file mode 100644 index 0000000000..cf5165d51c --- /dev/null +++ b/src/build-info/goblint_build_info.ml @@ -0,0 +1,34 @@ +(** Goblint build info. *) + +(** OCaml compiler flambda status. *) +let ocaml_flambda = ConfigOcaml.flambda + +(** Dune profile. *) +let dune_profile = ConfigProfile.profile + +(** Goblint version from git. *) +let git_version = ConfigVersion.version + +(** Goblint version from release archive. *) +let release_version = "%%VERSION_NUM%%" + +(** Goblint git commit from release archive. *) +let release_commit = "%%VCS_COMMIT_ID%%" + +(** Goblint version. *) +let version = + let commit = ConfigVersion.version in + if BatString.starts_with release_version "%" then + commit + else ( + let commit = + if commit = "n/a" then (* released archive has no .git *) + release_commit + else + commit + in + Format.sprintf "%s (%s)" release_version commit + ) + +(** Statically linked libraries with versions. *) +let statically_linked_libraries = Dune_build_info.statically_linked_libraries diff --git a/src/dune b/src/dune index a8cda818b1..5fdf58a5b2 100644 --- a/src/dune +++ b/src/dune @@ -107,22 +107,6 @@ (flags :standard -linkall -open Goblint_std) ) -(rule - (target configVersion.ml) - (mode (promote (until-clean) (only configVersion.ml))) ; replace existing file in source tree, even if releasing (only overrides) - (deps (universe)) ; do not cache, always regenerate - (action (pipe-stdout (bash "git describe --all --long --dirty || echo \"n/a\"") (with-stdout-to %{target} (bash "xargs printf '(* Automatically regenerated, changes do not persist! *)\nlet version = \"%s\"'"))))) - -(rule - (target configProfile.ml) - (mode (promote (until-clean) (only configProfile.ml))) ; replace existing file in source tree, even if releasing (only overrides) - (action (write-file %{target} "(* Automatically regenerated, changes do not persist! *)\nlet profile = \"%{profile}\""))) - -(rule - (target configOcaml.ml) - (mode (promote (until-clean) (only configOcaml.ml))) ; replace existing file in source tree, even if releasing (only overrides) - (action (write-file %{target} "(* Automatically regenerated, changes do not persist! *)\nlet flambda = \"%{ocaml-config:flambda}\""))) - (rule (alias runtest) (deps ../goblint ../scripts/update_suite.rb ../Makefile ../make.sh (source_tree ../tests/regression) (source_tree ../includes) (source_tree ../linux-headers)) diff --git a/src/framework/control.ml b/src/framework/control.ml index 5cefc1a7de..9baa2dd1ca 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -529,7 +529,7 @@ struct GobConfig.write_file config; let module Meta = struct type t = { command : string; version: string; timestamp : float; localtime : string } [@@deriving to_yojson] - let json = to_yojson { command = GobSys.command_line; version = Version.goblint; timestamp = Unix.time (); localtime = GobUnix.localtime () } + let json = to_yojson { command = GobSys.command_line; version = Goblint_build_info.version; timestamp = Unix.time (); localtime = GobUnix.localtime () } end in (* Yojson.Safe.to_file meta Meta.json; *) diff --git a/src/goblint_lib.ml b/src/goblint_lib.ml index 816a69faff..e009ecf86b 100644 --- a/src/goblint_lib.ml +++ b/src/goblint_lib.ml @@ -452,21 +452,6 @@ module PrivPrecCompareUtil = PrivPrecCompareUtil module RelationPrecCompareUtil = RelationPrecCompareUtil module ApronPrecCompareUtil = ApronPrecCompareUtil -(** {2 Build info} *) - -(** OCaml compiler info. *) -module ConfigOcaml = ConfigOcaml - -(** Dune profile info. *) -module ConfigProfile = ConfigProfile - -(** Goblint version info. *) -module Version = Version - -(** Goblint git version info. *) -module ConfigVersion = ConfigVersion - - (** {1 Library extensions} OCaml library extensions which are completely independent of Goblint. diff --git a/src/maingoblint.ml b/src/maingoblint.ml index 7808cbcd3f..98363233a2 100644 --- a/src/maingoblint.ml +++ b/src/maingoblint.ml @@ -9,11 +9,11 @@ let writeconffile = ref None (** Print version and bail. *) let print_version ch = - printf "Goblint version: %s\n" Version.goblint; + printf "Goblint version: %s\n" Goblint_build_info.version; printf "Cil version: %s\n" Cil.cilVersion; - printf "Dune profile: %s\n" ConfigProfile.profile; + printf "Dune profile: %s\n" Goblint_build_info.dune_profile; printf "OCaml version: %s\n" Sys.ocaml_version; - printf "OCaml flambda: %s\n" ConfigOcaml.flambda; + printf "OCaml flambda: %s\n" Goblint_build_info.ocaml_flambda; if get_bool "dbg.verbose" then ( printf "Library versions:\n"; List.iter (fun (name, version) -> diff --git a/src/util/sarif.ml b/src/util/sarif.ml index 4374da46d7..7620384cc4 100644 --- a/src/util/sarif.ml +++ b/src/util/sarif.ml @@ -26,7 +26,7 @@ let goblintTool: Tool.t = { fullName = "Goblint static analyser"; informationUri = "https://goblint.in.tum.de/home"; organization = "TUM - i2 and UTartu - SWS"; - version = Version.goblint; + version = Goblint_build_info.version; rules = List.map transformToReportingDescriptor (List.map (fun rule -> rule.name) rules) }; } diff --git a/src/util/tracing.ml b/src/util/tracing.ml index f9dff2c2cf..ad8892c396 100644 --- a/src/util/tracing.ml +++ b/src/util/tracing.ml @@ -10,7 +10,7 @@ open Pretty module Strs = Set.Make (String) -let tracing = ConfigProfile.profile = "trace" +let tracing = Goblint_build_info.dune_profile = "trace" let current_loc = ref locUnknown let next_loc = ref locUnknown diff --git a/src/version.ml b/src/version.ml deleted file mode 100644 index cbe2874608..0000000000 --- a/src/version.ml +++ /dev/null @@ -1,16 +0,0 @@ -let release = "%%VERSION_NUM%%" -let release_commit = "%%VCS_COMMIT_ID%%" - -let goblint = - let commit = ConfigVersion.version in - if BatString.starts_with release "%" then - commit - else ( - let commit = - if commit = "n/a" then (* released archive has no .git *) - release_commit - else - commit - in - Format.sprintf "%s (%s)" release commit - ) diff --git a/src/witness/witness.ml b/src/witness/witness.ml index 0e237716fd..fb1604f03e 100644 --- a/src/witness/witness.ml +++ b/src/witness/witness.ml @@ -118,7 +118,7 @@ let write_file filename (module Task:Task) (module TaskResult:WitnessTaskResult) | Result.Unknown -> "unknown_witness" ); GML.write_metadata g "sourcecodelang" "C"; - GML.write_metadata g "producer" (Printf.sprintf "Goblint (%s)" Version.goblint); + GML.write_metadata g "producer" (Printf.sprintf "Goblint (%s)" Goblint_build_info.version); GML.write_metadata g "specification" (Svcomp.Specification.to_string Task.specification); let programfile = (Node.location (N.cfgnode main_entry)).file in GML.write_metadata g "programfile" programfile; diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index c7106a57b5..72ff21f6bd 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -17,7 +17,7 @@ struct (* let yaml_conf: Yaml.value = Json_repr.convert (module Json_repr.Yojson) (module Json_repr.Ezjsonm) (!GobConfig.json_conf) in *) let producer: Producer.t = { name = "Goblint"; - version = Version.goblint; + version = Goblint_build_info.version; command_line = Some GobSys.command_line; } From 9b728d352169f0eea1e4a21609233413f2ae1c79 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 5 Oct 2023 13:59:19 +0200 Subject: [PATCH 0803/1312] Set `dest` in `memcpy` to top if `n` doesn't match its size --- src/analyses/base.ml | 85 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 81 insertions(+), 4 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index a5b60e8cca..2d779b8d85 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2054,6 +2054,64 @@ struct AnalysisStateUtil.set_mem_safety_flag InvalidFree; M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Pointer %a in function %s doesn't evaluate to a valid address. Invalid memory deallocation may occur" d_exp ptr special_fn.vname + let points_to_heap_only ctx ptr = + match ctx.ask (Queries.MayPointTo ptr) with + | a when not (Queries.AD.is_top a)-> + Queries.AD.for_all (function + | Addr (v, o) -> ctx.ask (Queries.IsHeapVar v) + | _ -> false + ) a + | _ -> false + + let get_size_of_ptr_target ctx ptr = + let intdom_of_int x = + ID.of_int (Cilfacade.ptrdiff_ikind ()) (Z.of_int x) + in + let size_of_type_in_bytes typ = + let typ_size_in_bytes = (bitsSizeOf typ) / 8 in + intdom_of_int typ_size_in_bytes + in + if points_to_heap_only ctx ptr then + (* Ask for BlobSize from the base address (the second component being set to true) in order to avoid BlobSize giving us bot *) + ctx.ask (Queries.BlobSize {exp = ptr; base_address = true}) + else + match ctx.ask (Queries.MayPointTo ptr) with + | a when not (Queries.AD.is_top a) -> + let pts_list = Queries.AD.elements a in + let pts_elems_to_sizes (addr: Queries.AD.elt) = + begin match addr with + | Addr (v, _) -> + begin match v.vtype with + | TArray (item_typ, _, _) -> + let item_typ_size_in_bytes = size_of_type_in_bytes item_typ in + begin match ctx.ask (Queries.EvalLength ptr) with + | `Lifted arr_len -> + let arr_len_casted = ID.cast_to (Cilfacade.ptrdiff_ikind ()) arr_len in + begin + try `Lifted (ID.mul item_typ_size_in_bytes arr_len_casted) + with IntDomain.ArithmeticOnIntegerBot _ -> `Bot + end + | `Bot -> `Bot + | `Top -> `Top + end + | _ -> + let type_size_in_bytes = size_of_type_in_bytes v.vtype in + `Lifted type_size_in_bytes + end + | _ -> `Top + end + in + (* Map each points-to-set element to its size *) + let pts_sizes = List.map pts_elems_to_sizes pts_list in + (* Take the smallest of all sizes that ptr's contents may have *) + begin match pts_sizes with + | [] -> `Bot + | [x] -> x + | x::xs -> List.fold_left ValueDomainQueries.ID.join x xs + end + | _ -> + (M.warn "Pointer %a has a points-to-set of top. An invalid memory access might occur" d_exp ptr; + `Top) let special ctx (lv:lval option) (f: varinfo) (args: exp list) = let invalidate_ret_lv st = match lv with @@ -2073,13 +2131,32 @@ struct let st: store = ctx.local in let gs = ctx.global in let desc = LF.find f in - let memory_copying dst src = + let memory_copying dst src n = + let dest_size = get_size_of_ptr_target ctx dst in + let n_intdom = match n with + | Some exp -> ctx.ask (Queries.EvalInt exp) + | None -> `Bot + in + let dest_size_equal_n = + match dest_size, n_intdom with + | `Top, `Top -> true + | `Bot, `Bot -> true + | `Lifted ds, `Lifted n -> + let casted_ds = ID.cast_to (Cilfacade.ptrdiff_ikind ()) ds in + let casted_n = ID.cast_to (Cilfacade.ptrdiff_ikind ()) n in + let ds_eq_n = ID.eq casted_ds casted_n in + begin match ID.to_bool ds_eq_n with + | Some b -> b + | None -> false + end + | _, _ -> false + in let dest_a, dest_typ = addr_type_of_exp dst in let src_lval = mkMem ~addr:(Cil.stripCasts src) ~off:NoOffset in let src_typ = eval_lv (Analyses.ask_of_ctx ctx) gs st src_lval |> AD.type_of in (* when src and destination type coincide, take value from the source, otherwise use top *) - let value = if typeSig dest_typ = typeSig src_typ then + let value = if (typeSig dest_typ = typeSig src_typ) && dest_size_equal_n then let src_cast_lval = mkMem ~addr:(Cilfacade.mkCast ~e:src ~newt:(TPtr (dest_typ, []))) ~off:NoOffset in eval_rv (Analyses.ask_of_ctx ctx) gs st (Lval src_cast_lval) else @@ -2140,13 +2217,13 @@ struct let value = VD.zero_init_value dest_typ in set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value | Memcpy { dest = dst; src; n; }, _ -> (* TODO: use n *) - memory_copying dst src + memory_copying dst src (Some n) (* strcpy(dest, src); *) | Strcpy { dest = dst; src; n = None }, _ -> let dest_a, dest_typ = addr_type_of_exp dst in (* when dest surely isn't a string literal, try copying src to dest *) if AD.string_writing_defined dest_a then - memory_copying dst src + memory_copying dst src None else (* else return top (after a warning was issued) *) set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ (VD.top_value (unrollType dest_typ)) From 48f2cfedda0a61517a69e012dfc07a08fe3646cb Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 5 Oct 2023 14:00:18 +0200 Subject: [PATCH 0804/1312] Add test case for UAF due to bad memcpy --- .../74-use_after_free/16-uaf-packed-struct.c | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 tests/regression/74-use_after_free/16-uaf-packed-struct.c diff --git a/tests/regression/74-use_after_free/16-uaf-packed-struct.c b/tests/regression/74-use_after_free/16-uaf-packed-struct.c new file mode 100644 index 0000000000..e10aa28486 --- /dev/null +++ b/tests/regression/74-use_after_free/16-uaf-packed-struct.c @@ -0,0 +1,40 @@ +// PARAM: --set ana.activated[+] useAfterFree +#include +#include + +struct A { + unsigned char a; + unsigned char b:2; + unsigned char c:2; + unsigned char pad1[2]; + unsigned int d; + unsigned char e; + unsigned char pad2[3]; +} __attribute__((packed)); + +struct A d; +int main(void) +{ + struct A *p; + p = malloc(12); + d.a = 1; + d.b = 2; + d.c = 3; + d.d = 4; + d.e = 5; + memcpy(p, &d, 4); + if (p->a != 1) { + free(p); + } + if (p->b != 2) {//WARN + free(p);//WARN + } + if (p->c != 3) {//WARN + free(p);//WARN + } + if (p->d != 4) { //WARN + free(p);//WARN + } + free(p);//WARN +} + From 753b5c1661009bbfa6542fd64ca3d6de62231a34 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 5 Oct 2023 14:02:33 +0200 Subject: [PATCH 0805/1312] Check offsets of dereferenced lvalues as well --- src/analyses/memOutOfBounds.ml | 65 +++++++++++++++++++++++++++++++--- 1 file changed, 61 insertions(+), 4 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 8a2ca12467..d555db968d 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -122,9 +122,9 @@ struct | x::xs -> List.fold_left VDQ.ID.join x xs end | _ -> - set_mem_safety_flag InvalidDeref; - M.warn "Pointer %a has a points-to-set of top. An invalid memory access might occur" d_exp ptr; - `Top + (set_mem_safety_flag InvalidDeref; + M.warn "Pointer %a has a points-to-set of top. An invalid memory access might occur" d_exp ptr; + `Top) let get_ptr_deref_type ptr_typ = match ptr_typ with @@ -165,6 +165,32 @@ struct with IntDomain.ArithmeticOnIntegerBot _ -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () end + let rec cil_offs_to_idx ctx typ offs = + match offs with + | NoOffset -> intdom_of_int 0 + | Field (field, o) -> + let field_as_offset = Field (field, NoOffset) in + let bits_offset, _size = GoblintCil.bitsOffset (TComp (field.fcomp, [])) field_as_offset in + let bytes_offset = intdom_of_int (bits_offset / 8) in + let remaining_offset = cil_offs_to_idx ctx field.ftype o in + begin + try ID.add bytes_offset remaining_offset + with IntDomain.ArithmeticOnIntegerBot _ -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () + end + | Index (x, o) -> + begin try + begin match ctx.ask (Queries.EvalInt x) with + | `Top -> ID.top_of @@ Cilfacade.ptrdiff_ikind () + | `Bot -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () + | `Lifted eval_x -> + let typ_size_in_bytes = size_of_type_in_bytes typ in + let bytes_offset = ID.mul typ_size_in_bytes eval_x in + let remaining_offset = cil_offs_to_idx ctx typ o in + ID.add bytes_offset remaining_offset + end + with IntDomain.ArithmeticOnIntegerBot _ -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () + end + let check_unknown_addr_deref ctx ptr = let may_contain_unknown_addr = match ctx.ask (Queries.EvalValue ptr) with @@ -245,7 +271,38 @@ struct match lval, is_implicitly_derefed with | (Var _, _), false -> () | (Var v, _), true -> check_no_binop_deref ctx (Lval lval) - | (Mem e, _), _ -> + | (Mem e, o), _ -> + let ptr_deref_type = get_ptr_deref_type @@ typeOf e in + let offs_intdom = begin match ptr_deref_type with + | Some t -> cil_offs_to_idx ctx t o + | None -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () + end in + let e_size = get_size_of_ptr_target ctx e in + let () = begin match e_size with + | `Top -> + (set_mem_safety_flag InvalidDeref; + M.warn "Size of lval dereference expression %a is top. Out-of-bounds memory access may occur" d_exp e) + | `Bot -> + (set_mem_safety_flag InvalidDeref; + M.warn "Size of lval dereference expression %a is bot. Out-of-bounds memory access may occur" d_exp e) + | `Lifted es -> + let casted_es = ID.cast_to (Cilfacade.ptrdiff_ikind ()) es in + let one = intdom_of_int 1 in + let casted_es = ID.sub casted_es one in + let casted_offs = ID.cast_to (Cilfacade.ptrdiff_ikind ()) offs_intdom in + let ptr_size_lt_offs = ID.lt casted_es casted_offs in + let behavior = Undefined MemoryOutOfBoundsAccess in + let cwe_number = 823 in + begin match ID.to_bool ptr_size_lt_offs with + | Some true -> + (set_mem_safety_flag InvalidDeref; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of lval dereference expression is %a (in bytes). It is offset by %a (in bytes). Memory out-of-bounds access must occur" ID.pretty casted_es ID.pretty casted_offs) + | Some false -> () + | None -> + (set_mem_safety_flag InvalidDeref; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Could not compare size of lval dereference expression (%a) (in bytes) with offset by (%a) (in bytes). Memory out-of-bounds access might occur" ID.pretty casted_es ID.pretty casted_offs) + end + end in begin match e with | Lval (Var v, _) as lval_exp -> check_no_binop_deref ctx lval_exp | BinOp (binop, e1, e2, t) when binop = PlusPI || binop = MinusPI || binop = IndexPI -> From d68328163e07f228f44f50c0b9b27200ea631284 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 5 Oct 2023 14:02:51 +0200 Subject: [PATCH 0806/1312] Add regr. test case for OOB due to too large lval offset --- .../77-mem-oob/13-mem-oob-packed-struct.c | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 tests/regression/77-mem-oob/13-mem-oob-packed-struct.c diff --git a/tests/regression/77-mem-oob/13-mem-oob-packed-struct.c b/tests/regression/77-mem-oob/13-mem-oob-packed-struct.c new file mode 100644 index 0000000000..552cd1bb0b --- /dev/null +++ b/tests/regression/77-mem-oob/13-mem-oob-packed-struct.c @@ -0,0 +1,33 @@ +// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval +#include + +struct A { + unsigned char a; + unsigned char b:2; + unsigned char c:2; + unsigned char d; +} __attribute__((packed)); + +int main(void) +{ + struct A *p; + p = malloc(2); + p->a = 1; + if (p->a != 1) { + free(p); + } + p->b = 2; + if (p->b != 2) { + free(p); + } + p->c = 3; + if (p->c != 3) { + free(p); + } + p->d = 4; //WARN + if (p->d != 4) {//WARN + free(p); + } + free(p); +} + From d723fdef0bf9e17625c141ed44a0e9d271e0609d Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 5 Oct 2023 15:48:41 +0300 Subject: [PATCH 0807/1312] Extract widely used modules to common dune library --- src/{util => common}/afterConfig.ml | 0 src/{framework => common}/analysisState.ml | 0 src/{cdomains => common}/basetype.ml | 0 src/{util => common}/cilType.ml | 0 src/{util => common}/cilfacade.ml | 0 src/{util => common}/cilfacade0.ml | 0 src/{framework => common}/controlSpecC.ml | 0 src/{framework => common}/controlSpecC.mli | 0 src/common/dune | 28 ++++++++++++++++++++++ src/{framework => common}/edge.ml | 0 src/{util => common}/gobConfig.ml | 0 src/{util => common}/gobFormat.ml | 0 src/{util => common}/jsonSchema.ml | 0 src/{domains => common}/lattice.ml | 0 src/{util => common}/lazyEval.ml | 0 src/{util => common}/messageCategory.ml | 0 src/{util => common}/messageUtil.ml | 0 src/{util => common}/messages.ml | 0 src/{framework => common}/myCFG.ml | 0 src/{domains => common}/myCheck.ml | 0 src/{framework => common}/node.ml | 0 src/{framework => common}/node0.ml | 0 src/{util => common}/options.ml | 0 src/{util => common}/options.schema.json | 0 src/{domains => common}/printable.ml | 0 src/{util => common}/resettableLazy.ml | 0 src/{util => common}/resettableLazy.mli | 0 src/{util => common}/richVarinfo.ml | 0 src/{util => common}/richVarinfo.mli | 0 src/{util => common}/timing.ml | 0 src/{util => common}/tracing.ml | 0 src/{incremental => common}/updateCil0.ml | 0 src/{util => common}/xmlUtil.ml | 0 src/dune | 3 +-- 34 files changed, 29 insertions(+), 2 deletions(-) rename src/{util => common}/afterConfig.ml (100%) rename src/{framework => common}/analysisState.ml (100%) rename src/{cdomains => common}/basetype.ml (100%) rename src/{util => common}/cilType.ml (100%) rename src/{util => common}/cilfacade.ml (100%) rename src/{util => common}/cilfacade0.ml (100%) rename src/{framework => common}/controlSpecC.ml (100%) rename src/{framework => common}/controlSpecC.mli (100%) create mode 100644 src/common/dune rename src/{framework => common}/edge.ml (100%) rename src/{util => common}/gobConfig.ml (100%) rename src/{util => common}/gobFormat.ml (100%) rename src/{util => common}/jsonSchema.ml (100%) rename src/{domains => common}/lattice.ml (100%) rename src/{util => common}/lazyEval.ml (100%) rename src/{util => common}/messageCategory.ml (100%) rename src/{util => common}/messageUtil.ml (100%) rename src/{util => common}/messages.ml (100%) rename src/{framework => common}/myCFG.ml (100%) rename src/{domains => common}/myCheck.ml (100%) rename src/{framework => common}/node.ml (100%) rename src/{framework => common}/node0.ml (100%) rename src/{util => common}/options.ml (100%) rename src/{util => common}/options.schema.json (100%) rename src/{domains => common}/printable.ml (100%) rename src/{util => common}/resettableLazy.ml (100%) rename src/{util => common}/resettableLazy.mli (100%) rename src/{util => common}/richVarinfo.ml (100%) rename src/{util => common}/richVarinfo.mli (100%) rename src/{util => common}/timing.ml (100%) rename src/{util => common}/tracing.ml (100%) rename src/{incremental => common}/updateCil0.ml (100%) rename src/{util => common}/xmlUtil.ml (100%) diff --git a/src/util/afterConfig.ml b/src/common/afterConfig.ml similarity index 100% rename from src/util/afterConfig.ml rename to src/common/afterConfig.ml diff --git a/src/framework/analysisState.ml b/src/common/analysisState.ml similarity index 100% rename from src/framework/analysisState.ml rename to src/common/analysisState.ml diff --git a/src/cdomains/basetype.ml b/src/common/basetype.ml similarity index 100% rename from src/cdomains/basetype.ml rename to src/common/basetype.ml diff --git a/src/util/cilType.ml b/src/common/cilType.ml similarity index 100% rename from src/util/cilType.ml rename to src/common/cilType.ml diff --git a/src/util/cilfacade.ml b/src/common/cilfacade.ml similarity index 100% rename from src/util/cilfacade.ml rename to src/common/cilfacade.ml diff --git a/src/util/cilfacade0.ml b/src/common/cilfacade0.ml similarity index 100% rename from src/util/cilfacade0.ml rename to src/common/cilfacade0.ml diff --git a/src/framework/controlSpecC.ml b/src/common/controlSpecC.ml similarity index 100% rename from src/framework/controlSpecC.ml rename to src/common/controlSpecC.ml diff --git a/src/framework/controlSpecC.mli b/src/common/controlSpecC.mli similarity index 100% rename from src/framework/controlSpecC.mli rename to src/common/controlSpecC.mli diff --git a/src/common/dune b/src/common/dune new file mode 100644 index 0000000000..03a93a3030 --- /dev/null +++ b/src/common/dune @@ -0,0 +1,28 @@ +(include_subdirs no) + +(library + (name goblint_common) + (public_name goblint.common) + (wrapped false) ; TODO: wrap + (libraries + batteries + zarith + goblint_std + goblint-cil + fpath + yojson + json-data-encoding + cpu + goblint_timing + goblint_build_info + goblint.sites + qcheck-core.runner) + (flags :standard -open Goblint_std) + (preprocess + (pps + ppx_deriving.std + ppx_deriving_hash + ppx_deriving_yojson + ppx_blob)) + (preprocessor_deps (file options.schema.json))) + diff --git a/src/framework/edge.ml b/src/common/edge.ml similarity index 100% rename from src/framework/edge.ml rename to src/common/edge.ml diff --git a/src/util/gobConfig.ml b/src/common/gobConfig.ml similarity index 100% rename from src/util/gobConfig.ml rename to src/common/gobConfig.ml diff --git a/src/util/gobFormat.ml b/src/common/gobFormat.ml similarity index 100% rename from src/util/gobFormat.ml rename to src/common/gobFormat.ml diff --git a/src/util/jsonSchema.ml b/src/common/jsonSchema.ml similarity index 100% rename from src/util/jsonSchema.ml rename to src/common/jsonSchema.ml diff --git a/src/domains/lattice.ml b/src/common/lattice.ml similarity index 100% rename from src/domains/lattice.ml rename to src/common/lattice.ml diff --git a/src/util/lazyEval.ml b/src/common/lazyEval.ml similarity index 100% rename from src/util/lazyEval.ml rename to src/common/lazyEval.ml diff --git a/src/util/messageCategory.ml b/src/common/messageCategory.ml similarity index 100% rename from src/util/messageCategory.ml rename to src/common/messageCategory.ml diff --git a/src/util/messageUtil.ml b/src/common/messageUtil.ml similarity index 100% rename from src/util/messageUtil.ml rename to src/common/messageUtil.ml diff --git a/src/util/messages.ml b/src/common/messages.ml similarity index 100% rename from src/util/messages.ml rename to src/common/messages.ml diff --git a/src/framework/myCFG.ml b/src/common/myCFG.ml similarity index 100% rename from src/framework/myCFG.ml rename to src/common/myCFG.ml diff --git a/src/domains/myCheck.ml b/src/common/myCheck.ml similarity index 100% rename from src/domains/myCheck.ml rename to src/common/myCheck.ml diff --git a/src/framework/node.ml b/src/common/node.ml similarity index 100% rename from src/framework/node.ml rename to src/common/node.ml diff --git a/src/framework/node0.ml b/src/common/node0.ml similarity index 100% rename from src/framework/node0.ml rename to src/common/node0.ml diff --git a/src/util/options.ml b/src/common/options.ml similarity index 100% rename from src/util/options.ml rename to src/common/options.ml diff --git a/src/util/options.schema.json b/src/common/options.schema.json similarity index 100% rename from src/util/options.schema.json rename to src/common/options.schema.json diff --git a/src/domains/printable.ml b/src/common/printable.ml similarity index 100% rename from src/domains/printable.ml rename to src/common/printable.ml diff --git a/src/util/resettableLazy.ml b/src/common/resettableLazy.ml similarity index 100% rename from src/util/resettableLazy.ml rename to src/common/resettableLazy.ml diff --git a/src/util/resettableLazy.mli b/src/common/resettableLazy.mli similarity index 100% rename from src/util/resettableLazy.mli rename to src/common/resettableLazy.mli diff --git a/src/util/richVarinfo.ml b/src/common/richVarinfo.ml similarity index 100% rename from src/util/richVarinfo.ml rename to src/common/richVarinfo.ml diff --git a/src/util/richVarinfo.mli b/src/common/richVarinfo.mli similarity index 100% rename from src/util/richVarinfo.mli rename to src/common/richVarinfo.mli diff --git a/src/util/timing.ml b/src/common/timing.ml similarity index 100% rename from src/util/timing.ml rename to src/common/timing.ml diff --git a/src/util/tracing.ml b/src/common/tracing.ml similarity index 100% rename from src/util/tracing.ml rename to src/common/tracing.ml diff --git a/src/incremental/updateCil0.ml b/src/common/updateCil0.ml similarity index 100% rename from src/incremental/updateCil0.ml rename to src/common/updateCil0.ml diff --git a/src/util/xmlUtil.ml b/src/common/xmlUtil.ml similarity index 100% rename from src/util/xmlUtil.ml rename to src/common/xmlUtil.ml diff --git a/src/dune b/src/dune index 5fdf58a5b2..df19f85340 100644 --- a/src/dune +++ b/src/dune @@ -7,7 +7,7 @@ (name goblint_lib) (public_name goblint.lib) (modules :standard \ goblint mainspec privPrecCompare apronPrecCompare messagesCompare) - (libraries goblint.sites goblint.build-info goblint-cil.all-features batteries.unthreaded qcheck-core.runner sha json-data-encoding jsonrpc cpu arg-complete fpath yaml yaml.unix uuidm goblint_timing catapult goblint_backtrace fileutils goblint_std + (libraries goblint.sites goblint.build-info goblint-cil.all-features batteries.unthreaded qcheck-core.runner sha json-data-encoding jsonrpc cpu arg-complete fpath yaml yaml.unix uuidm goblint_timing catapult goblint_backtrace fileutils goblint_std goblint_common ; Conditionally compile based on whether apron optional dependency is installed or not. ; Alternative dependencies seem like the only way to optionally depend on optional dependencies. ; See: https://dune.readthedocs.io/en/stable/concepts.html#alternative-dependencies. @@ -61,7 +61,6 @@ (ocamlopt_flags :standard -no-float-const-prop) (preprocess (pps ppx_deriving.std ppx_deriving_hash ppx_deriving_yojson ppx_blob)) - (preprocessor_deps (file util/options.schema.json)) (instrumentation (backend bisect_ppx)) ) From 22e4df53b11aa16dc676a690f131321f2112523b Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 5 Oct 2023 15:24:47 +0200 Subject: [PATCH 0808/1312] Fix exceptions due to ID ops --- src/analyses/base.ml | 6 +++++- src/analyses/memOutOfBounds.ml | 9 +++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 2d779b8d85..b6cc5c29cf 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2144,7 +2144,11 @@ struct | `Lifted ds, `Lifted n -> let casted_ds = ID.cast_to (Cilfacade.ptrdiff_ikind ()) ds in let casted_n = ID.cast_to (Cilfacade.ptrdiff_ikind ()) n in - let ds_eq_n = ID.eq casted_ds casted_n in + let ds_eq_n = + begin try ID.eq casted_ds casted_n + with IntDomain.ArithmeticOnIntegerBot _ -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () + end + in begin match ID.to_bool ds_eq_n with | Some b -> b | None -> false diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index d555db968d..68dae1d89a 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -184,7 +184,8 @@ struct | `Bot -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () | `Lifted eval_x -> let typ_size_in_bytes = size_of_type_in_bytes typ in - let bytes_offset = ID.mul typ_size_in_bytes eval_x in + let casted_eval_x = ID.cast_to (Cilfacade.ptrdiff_ikind ()) eval_x in + let bytes_offset = ID.mul typ_size_in_bytes casted_eval_x in let remaining_offset = cil_offs_to_idx ctx typ o in ID.add bytes_offset remaining_offset end @@ -290,7 +291,11 @@ struct let one = intdom_of_int 1 in let casted_es = ID.sub casted_es one in let casted_offs = ID.cast_to (Cilfacade.ptrdiff_ikind ()) offs_intdom in - let ptr_size_lt_offs = ID.lt casted_es casted_offs in + let ptr_size_lt_offs = + begin try ID.lt casted_es casted_offs + with IntDomain.ArithmeticOnIntegerBot _ -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () + end + in let behavior = Undefined MemoryOutOfBoundsAccess in let cwe_number = 823 in begin match ID.to_bool ptr_size_lt_offs with From 94307d03c47d63621ade9b833c0be35bb23bee89 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 5 Oct 2023 17:27:55 +0300 Subject: [PATCH 0809/1312] Add option ana.race.call --- conf/svcomp.json | 3 ++- src/domains/access.ml | 2 ++ src/util/options.schema.json | 6 ++++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/conf/svcomp.json b/conf/svcomp.json index f51c7a52ee..df624e4b83 100644 --- a/conf/svcomp.json +++ b/conf/svcomp.json @@ -70,7 +70,8 @@ } }, "race": { - "free": false + "free": false, + "call": false }, "autotune": { "enabled": true, diff --git a/src/domains/access.ml b/src/domains/access.ml index 8907ccbc32..f243b85bda 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -444,6 +444,8 @@ let may_race A.{kind; acc; _} A.{kind=kind2; acc=acc2; _} = false (* two read/read accesses do not race *) else if not (get_bool "ana.race.free") && (kind = Free || kind2 = Free) then false + else if not (get_bool "ana.race.call") && (kind = Call || kind2 = Call) then + false else if not (MCPAccess.A.may_race acc acc2) then false (* analysis-specific information excludes race *) else diff --git a/src/util/options.schema.json b/src/util/options.schema.json index 1b9c7d3fd5..33de069b38 100644 --- a/src/util/options.schema.json +++ b/src/util/options.schema.json @@ -1002,6 +1002,12 @@ "type": "boolean", "default": true }, + "call": { + "title": "ana.race.call", + "description": "Report races for thread-unsafe function calls.", + "type": "boolean", + "default": true + }, "direct-arithmetic": { "title": "ana.race.direct-arithmetic", "description": "Collect and distribute direct (i.e. not in a field) accesses to arithmetic types.", From 1d94b5a5f596f194b87cf64a4c58605c35287cf7 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 6 Oct 2023 17:21:17 +0300 Subject: [PATCH 0810/1312] Add 73-strings/05-string-unit-domain test --- .../regression/73-strings/05-string-unit-domain.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 tests/regression/73-strings/05-string-unit-domain.c diff --git a/tests/regression/73-strings/05-string-unit-domain.c b/tests/regression/73-strings/05-string-unit-domain.c new file mode 100644 index 0000000000..521e2f3ec5 --- /dev/null +++ b/tests/regression/73-strings/05-string-unit-domain.c @@ -0,0 +1,15 @@ +// PARAM: --enable ana.base.limit-string-addresses +#include +#include + +void foo(char *s) { + int l = strlen(s); + __goblint_check(l == 3 || l == 6); // UNKNOWN +} + +int main() { + foo("foo"); + foo("bar"); + foo("foobar"); + return 0; +} From 12a22b64c461fb7d80ff5be8de196f5f33536eb3 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 6 Oct 2023 17:39:41 +0300 Subject: [PATCH 0811/1312] Extract StringDomain from AddressDomain --- src/cdomains/addressDomain.ml | 85 ++++++++------------------------- src/cdomains/stringDomain.ml | 89 +++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 66 deletions(-) create mode 100644 src/cdomains/stringDomain.ml diff --git a/src/cdomains/addressDomain.ml b/src/cdomains/addressDomain.ml index 5981caf9ea..55b1aceefc 100644 --- a/src/cdomains/addressDomain.ml +++ b/src/cdomains/addressDomain.ml @@ -5,6 +5,7 @@ open IntOps module M = Messages module Mval_outer = Mval +module SD = StringDomain module AddressBase (Mval: Printable.S) = @@ -14,23 +15,14 @@ struct | Addr of Mval.t | NullPtr | UnknownPtr - | StrPtr of string option + | StrPtr of SD.t [@@deriving eq, ord, hash] (* TODO: StrPtr equal problematic if the same literal appears more than once *) let name () = Format.sprintf "address (%s)" (Mval.name ()) - let hash x = match x with - | StrPtr _ -> - if GobConfig.get_bool "ana.base.limit-string-addresses" then - 13859 - else - hash x - | _ -> hash x - let show = function | Addr m -> Mval.show m - | StrPtr (Some x) -> "\"" ^ x ^ "\"" - | StrPtr None -> "(unknown string)" + | StrPtr s -> StringDomain.show s | UnknownPtr -> "?" | NullPtr -> "NULL" @@ -42,31 +34,18 @@ struct ) (* strings *) - let of_string x = StrPtr (Some x) + let of_string x = StrPtr (SD.of_string x) let to_string = function - | StrPtr (Some x) -> Some x + | StrPtr s -> SD.to_string s | _ -> None - (* only keep part before first null byte *) let to_c_string = function - | StrPtr (Some x) -> - begin match String.split_on_char '\x00' x with - | s::_ -> Some s - | [] -> None - end + | StrPtr s -> SD.to_c_string s | _ -> None - let to_n_c_string n x = - match to_c_string x with - | Some x -> - if n > String.length x then - Some x - else if n < 0 then - None - else - Some (String.sub x 0 n) + let to_n_c_string n = function + | StrPtr s -> SD.to_n_c_string n s | _ -> None - let to_string_length x = - match to_c_string x with - | Some x -> Some (String.length x) + let to_string_length = function + | StrPtr s -> SD.to_string_length s | _ -> None let arbitrary () = QCheck.always UnknownPtr (* S TODO: non-unknown *) @@ -101,8 +80,7 @@ struct (* TODO: seems to be unused *) let to_exp = function | Addr m -> AddrOf (Mval.to_cil m) - | StrPtr (Some x) -> mkString x - | StrPtr None -> raise (Lattice.Unsupported "Cannot express unknown string pointer as expression.") + | StrPtr s -> SD.to_exp s | NullPtr -> integer 0 | UnknownPtr -> raise Lattice.TopValue (* TODO: unused *) @@ -123,9 +101,7 @@ struct let semantic_equal x y = match x, y with | Addr x, Addr y -> Mval.semantic_equal x y - | StrPtr None, StrPtr _ - | StrPtr _, StrPtr None -> Some true - | StrPtr (Some a), StrPtr (Some b) -> if a = b then None else Some false + | StrPtr s1, StrPtr s2 -> SD.semantic_equal s1 s2 | NullPtr, NullPtr -> Some true | UnknownPtr, UnknownPtr | UnknownPtr, Addr _ @@ -135,8 +111,7 @@ struct | _, _ -> Some false let leq x y = match x, y with - | StrPtr _, StrPtr None -> true - | StrPtr a, StrPtr b -> a = b + | StrPtr s1, StrPtr s2 -> SD.leq s1 s2 | Addr x, Addr y -> Mval.leq x y | _ -> x = y @@ -144,26 +119,6 @@ struct | Addr x -> Addr (Mval.top_indices x) | x -> x - let join_string_ptr x y = match x, y with - | None, _ - | _, None -> None - | Some a, Some b when a = b -> Some a - | Some a, Some b (* when a <> b *) -> - if GobConfig.get_bool "ana.base.limit-string-addresses" then - None - else - raise Lattice.Uncomparable - - let meet_string_ptr x y = match x, y with - | None, a - | a, None -> a - | Some a, Some b when a = b -> Some a - | Some a, Some b (* when a <> b *) -> - if GobConfig.get_bool "ana.base.limit-string-addresses" then - raise Lattice.BotValue - else - raise Lattice.Uncomparable - let merge mop sop x y = match x, y with | UnknownPtr, UnknownPtr -> UnknownPtr @@ -172,10 +127,10 @@ struct | Addr x, Addr y -> Addr (mop x y) | _ -> raise Lattice.Uncomparable - let join = merge Mval.join join_string_ptr - let widen = merge Mval.widen join_string_ptr - let meet = merge Mval.meet meet_string_ptr - let narrow = merge Mval.narrow meet_string_ptr + let join = merge Mval.join SD.join + let widen = merge Mval.widen SD.join + let meet = merge Mval.meet SD.meet + let narrow = merge Mval.narrow SD.meet include Lattice.NoBotTop @@ -194,8 +149,7 @@ struct let of_elt (x: elt): t = match x with | Addr (v, o) -> Addr v - | StrPtr _ when GobConfig.get_bool "ana.base.limit-string-addresses" -> StrPtr None (* all strings together if limited *) - | StrPtr x -> StrPtr x (* everything else is kept separate, including strings if not limited *) + | StrPtr s -> StrPtr (SD.repr s) | NullPtr -> NullPtr | UnknownPtr -> UnknownPtr end @@ -211,8 +165,7 @@ struct let of_elt (x: elt): t = match x with | Addr (v, o) -> Addr (v, Offset.Unit.of_offs o) (* addrs grouped by var and part of offset *) - | StrPtr _ when GobConfig.get_bool "ana.base.limit-string-addresses" -> StrPtr None (* all strings together if limited *) - | StrPtr x -> StrPtr x (* everything else is kept separate, including strings if not limited *) + | StrPtr s -> StrPtr (SD.repr s) | NullPtr -> NullPtr | UnknownPtr -> UnknownPtr end diff --git a/src/cdomains/stringDomain.ml b/src/cdomains/stringDomain.ml new file mode 100644 index 0000000000..c888663c7c --- /dev/null +++ b/src/cdomains/stringDomain.ml @@ -0,0 +1,89 @@ +type t = string option [@@deriving eq, ord, hash] + +let hash x = + if GobConfig.get_bool "ana.base.limit-string-addresses" then + 13859 + else + hash x + +let show = function + | Some x -> "\"" ^ x ^ "\"" + | None -> "(unknown string)" + +include Printable.SimpleShow ( + struct + type nonrec t = t + let show = show + end + ) + +let of_string x = Some x +let to_string x = x + +(* only keep part before first null byte *) +let to_c_string = function + | Some x -> + begin match String.split_on_char '\x00' x with + | s::_ -> Some s + | [] -> None + end + | None -> None + +let to_n_c_string n x = + match to_c_string x with + | Some x -> + if n > String.length x then + Some x + else if n < 0 then + None + else + Some (String.sub x 0 n) + | None -> None + +let to_string_length x = + match to_c_string x with + | Some x -> Some (String.length x) + | None -> None + +let to_exp = function + | Some x -> GoblintCil.mkString x + | None -> raise (Lattice.Unsupported "Cannot express unknown string pointer as expression.") + +let semantic_equal x y = + match x, y with + | None, _ + | _, None -> Some true + | Some a, Some b -> if a = b then None else Some false + +let leq x y = + match x, y with + | _, None -> true + | a, b -> a = b + +let join x y = + match x, y with + | None, _ + | _, None -> None + | Some a, Some b when a = b -> Some a + | Some a, Some b (* when a <> b *) -> + if GobConfig.get_bool "ana.base.limit-string-addresses" then + None + else + raise Lattice.Uncomparable + +let meet x y = + match x, y with + | None, a + | a, None -> a + | Some a, Some b when a = b -> Some a + | Some a, Some b (* when a <> b *) -> + if GobConfig.get_bool "ana.base.limit-string-addresses" then + raise Lattice.BotValue + else + raise Lattice.Uncomparable + +let repr x = + if GobConfig.get_bool "ana.base.limit-string-addresses" then + None (* all strings together if limited *) + else + x (* everything else is kept separate, including strings if not limited *) From 26b9cad1951bc574848fe6de993c2d69a21fa324 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 6 Oct 2023 17:48:28 +0300 Subject: [PATCH 0812/1312] Change ana.base.limit-string-addresses to ana.base.strings.domain --- conf/examples/very-precise.json | 4 ++- src/cdomains/addressDomain_intf.ml | 2 +- src/cdomains/stringDomain.ml | 30 +++++++++++-------- src/util/options.schema.json | 18 +++++++---- .../02-base/88-string-ptrs-limited.c | 2 +- .../02-base/89-string-ptrs-not-limited.c | 2 +- .../73-strings/01-string_literals.c | 8 ++--- .../73-strings/02-string_literals_with_null.c | 2 +- .../regression/73-strings/03-string_basics.c | 2 +- .../73-strings/05-string-unit-domain.c | 2 +- 10 files changed, 43 insertions(+), 29 deletions(-) diff --git a/conf/examples/very-precise.json b/conf/examples/very-precise.json index 84cbf53585..2197335eaf 100644 --- a/conf/examples/very-precise.json +++ b/conf/examples/very-precise.json @@ -61,7 +61,9 @@ "structs" : { "domain" : "combined-sk" }, - "limit-string-addresses": false + "strings": { + "domain": "disjoint" + } } }, "exp": { diff --git a/src/cdomains/addressDomain_intf.ml b/src/cdomains/addressDomain_intf.ml index 0ef3d6dd8d..f86dee29c4 100644 --- a/src/cdomains/addressDomain_intf.ml +++ b/src/cdomains/addressDomain_intf.ml @@ -71,7 +71,7 @@ sig - Each {!Addr}, modulo precise index expressions in the offset, is a sublattice with ordering induced by {!Mval}. - {!NullPtr} is a singleton sublattice. - {!UnknownPtr} is a singleton sublattice. - - If [ana.base.limit-string-addresses] is enabled, then all {!StrPtr} are together in one sublattice with flat ordering. If [ana.base.limit-string-addresses] is disabled, then each {!StrPtr} is a singleton sublattice. *) + - If [ana.base.strings.domain] is disjoint, then each {!StrPtr} is a singleton sublattice. Otherwise, all {!StrPtr} are together in one sublattice with flat ordering. *) module AddressLattice (Mval: Mval.Lattice): sig include module type of AddressPrintable (Mval) diff --git a/src/cdomains/stringDomain.ml b/src/cdomains/stringDomain.ml index c888663c7c..6c398cf9fd 100644 --- a/src/cdomains/stringDomain.ml +++ b/src/cdomains/stringDomain.ml @@ -1,10 +1,10 @@ type t = string option [@@deriving eq, ord, hash] let hash x = - if GobConfig.get_bool "ana.base.limit-string-addresses" then - 13859 - else + if GobConfig.get_string "ana.base.strings.domain" = "disjoint" then hash x + else + 13859 let show = function | Some x -> "\"" ^ x ^ "\"" @@ -17,7 +17,11 @@ include Printable.SimpleShow ( end ) -let of_string x = Some x +let of_string x = + if GobConfig.get_string "ana.base.strings.domain" = "unit" then + None + else + Some x let to_string x = x (* only keep part before first null byte *) @@ -66,10 +70,10 @@ let join x y = | _, None -> None | Some a, Some b when a = b -> Some a | Some a, Some b (* when a <> b *) -> - if GobConfig.get_bool "ana.base.limit-string-addresses" then - None - else + if GobConfig.get_string "ana.base.strings.domain" = "disjoint" then raise Lattice.Uncomparable + else + None let meet x y = match x, y with @@ -77,13 +81,13 @@ let meet x y = | a, None -> a | Some a, Some b when a = b -> Some a | Some a, Some b (* when a <> b *) -> - if GobConfig.get_bool "ana.base.limit-string-addresses" then - raise Lattice.BotValue - else + if GobConfig.get_string "ana.base.strings.domain" = "disjoint" then raise Lattice.Uncomparable + else + raise Lattice.BotValue let repr x = - if GobConfig.get_bool "ana.base.limit-string-addresses" then - None (* all strings together if limited *) - else + if GobConfig.get_string "ana.base.strings.domain" = "disjoint" then x (* everything else is kept separate, including strings if not limited *) + else + None (* all strings together if limited *) diff --git a/src/util/options.schema.json b/src/util/options.schema.json index 1b9c7d3fd5..330506958a 100644 --- a/src/util/options.schema.json +++ b/src/util/options.schema.json @@ -619,11 +619,19 @@ }, "additionalProperties": false }, - "limit-string-addresses": { - "title": "ana.base.limit-string-addresses", - "description": "Limit abstract address sets to keep at most one distinct string pointer.", - "type": "boolean", - "default": true + "strings": { + "title": "ana.base.strings", + "type": "object", + "properties": { + "domain": { + "title": "ana.base.strings.domain", + "description": "Domain for string literals.", + "type": "string", + "enum": ["unit", "flat", "disjoint"], + "default": "flat" + } + }, + "additionalProperties": false }, "partition-arrays": { "title": "ana.base.partition-arrays", diff --git a/tests/regression/02-base/88-string-ptrs-limited.c b/tests/regression/02-base/88-string-ptrs-limited.c index ab8b2fefe8..c4f39dc711 100644 --- a/tests/regression/02-base/88-string-ptrs-limited.c +++ b/tests/regression/02-base/88-string-ptrs-limited.c @@ -1,4 +1,4 @@ -//PARAM: --enable ana.base.limit-string-addresses +//PARAM: --set ana.base.strings.domain flat #include #include diff --git a/tests/regression/02-base/89-string-ptrs-not-limited.c b/tests/regression/02-base/89-string-ptrs-not-limited.c index 96100d230d..ab30e21fd8 100644 --- a/tests/regression/02-base/89-string-ptrs-not-limited.c +++ b/tests/regression/02-base/89-string-ptrs-not-limited.c @@ -1,4 +1,4 @@ -//PARAM: --disable ana.base.limit-string-addresses +//PARAM: --set ana.base.strings.domain disjoint #include #include diff --git a/tests/regression/73-strings/01-string_literals.c b/tests/regression/73-strings/01-string_literals.c index 36e4ed121c..42086e07b6 100644 --- a/tests/regression/73-strings/01-string_literals.c +++ b/tests/regression/73-strings/01-string_literals.c @@ -1,4 +1,4 @@ -// PARAM: --disable ana.base.limit-string-addresses --enable ana.int.interval +// PARAM: --set ana.base.strings.domain disjoint --enable ana.int.interval #include #include @@ -21,7 +21,7 @@ int main() { char* s1 = "abcde"; char* s2 = "abcdfg"; char* s3 = hello_world(); - + int i = strlen(s1); __goblint_check(i == 5); @@ -96,10 +96,10 @@ int main() { #define STRNCAT strncat(s1, "hi", 1) #endif STRNCAT; // WARN - + #ifdef __APPLE__ // do nothing => no warning - #else + #else char s4[] = "hello"; strcpy(s4, s2); // NOWARN strncpy(s4, s3, 2); // NOWARN diff --git a/tests/regression/73-strings/02-string_literals_with_null.c b/tests/regression/73-strings/02-string_literals_with_null.c index 75d000bbb8..cc41e9e287 100644 --- a/tests/regression/73-strings/02-string_literals_with_null.c +++ b/tests/regression/73-strings/02-string_literals_with_null.c @@ -1,4 +1,4 @@ -// PARAM: --disable ana.base.limit-string-addresses --enable ana.int.interval +// PARAM: --set ana.base.strings.domain disjoint --enable ana.int.interval #include #include diff --git a/tests/regression/73-strings/03-string_basics.c b/tests/regression/73-strings/03-string_basics.c index db196c64b4..a9d02d5e8b 100644 --- a/tests/regression/73-strings/03-string_basics.c +++ b/tests/regression/73-strings/03-string_basics.c @@ -1,4 +1,4 @@ -// PARAM: --disable ana.base.limit-string-addresses --enable ana.int.interval +// PARAM: --set ana.base.strings.domain disjoint --enable ana.int.interval #include #include diff --git a/tests/regression/73-strings/05-string-unit-domain.c b/tests/regression/73-strings/05-string-unit-domain.c index 521e2f3ec5..70e6bed5bf 100644 --- a/tests/regression/73-strings/05-string-unit-domain.c +++ b/tests/regression/73-strings/05-string-unit-domain.c @@ -1,4 +1,4 @@ -// PARAM: --enable ana.base.limit-string-addresses +// PARAM: --set ana.base.strings.domain unit #include #include From 3cb651f0ac8258523d82356bca5ce1d2bf5498df Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 6 Oct 2023 17:57:11 +0300 Subject: [PATCH 0813/1312] Add StringDomain interface --- src/cdomains/addressDomain_intf.ml | 4 +--- src/cdomains/stringDomain.ml | 4 ++++ src/cdomains/stringDomain.mli | 37 ++++++++++++++++++++++++++++++ src/goblint_lib.ml | 1 + 4 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 src/cdomains/stringDomain.mli diff --git a/src/cdomains/addressDomain_intf.ml b/src/cdomains/addressDomain_intf.ml index f86dee29c4..f65b2977c4 100644 --- a/src/cdomains/addressDomain_intf.ml +++ b/src/cdomains/addressDomain_intf.ml @@ -7,7 +7,7 @@ sig | Addr of Mval.t (** Pointer to mvalue. *) | NullPtr (** NULL pointer. *) | UnknownPtr (** Unknown pointer. Could point to globals, heap and escaped variables. *) - | StrPtr of string option (** String literal pointer. [StrPtr None] abstracts any string pointer *) + | StrPtr of StringDomain.t (** String literal pointer. [StrPtr None] abstracts any string pointer *) include Printable.S with type t := t (** @closed *) val of_string: string -> t @@ -16,8 +16,6 @@ sig val to_string: t -> string option (** Convert {!StrPtr} to string if possible. *) - (** C strings are different from OCaml strings as they are not processed after the first [NUL] byte, even though the OCaml string (and a C string literal) may be longer. *) - val to_c_string: t -> string option (** Convert {!StrPtr} to C string if possible. *) diff --git a/src/cdomains/stringDomain.ml b/src/cdomains/stringDomain.ml index 6c398cf9fd..925a7fec62 100644 --- a/src/cdomains/stringDomain.ml +++ b/src/cdomains/stringDomain.ml @@ -1,3 +1,7 @@ +include Printable.StdLeaf + +let name () = "string" + type t = string option [@@deriving eq, ord, hash] let hash x = diff --git a/src/cdomains/stringDomain.mli b/src/cdomains/stringDomain.mli new file mode 100644 index 0000000000..3541dac6e7 --- /dev/null +++ b/src/cdomains/stringDomain.mli @@ -0,0 +1,37 @@ +(** String literals domain. *) + +include Printable.S + +val of_string: string -> t +(** Convert from string. *) + +val to_string: t -> string option +(** Convert to string if possible. *) + +(** C strings are different from OCaml strings as they are not processed after the first [NUL] byte, even though the OCaml string (and a C string literal) may be longer. *) + +val to_c_string: t -> string option +(** Convert to C string if possible. *) + +val to_n_c_string: int -> t -> string option +(** Convert to C string of given maximum length if possible. *) + +val to_string_length: t -> int option +(** Find length of C string if possible. *) + +val to_exp: t -> GoblintCil.exp +(** Convert to CIL expression. *) + +val semantic_equal: t -> t -> bool option +(** Check semantic equality of two strings. + + @return [Some true] if definitely equal, [Some false] if definitely not equal, [None] if unknown. *) + +(** Some {!Lattice.S} operations. *) + +val leq: t -> t -> bool +val join: t -> t -> t +val meet: t -> t -> t + +val repr : t -> t +(** Representative for address lattice. *) diff --git a/src/goblint_lib.ml b/src/goblint_lib.ml index 6e700485dd..3f0123c372 100644 --- a/src/goblint_lib.ml +++ b/src/goblint_lib.ml @@ -211,6 +211,7 @@ module FloatDomain = FloatDomain module Mval = Mval module Offset = Offset +module StringDomain = StringDomain module AddressDomain = AddressDomain (** {5 Complex} *) From b9c213441f2ef03ce02173fa323650ebe234c080 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 6 Oct 2023 23:54:55 +0300 Subject: [PATCH 0814/1312] Fix size check in `memory_copying` Co-authored-by: Michael Schwarz --- src/analyses/base.ml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index b6cc5c29cf..6aaf25944e 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2139,21 +2139,16 @@ struct in let dest_size_equal_n = match dest_size, n_intdom with - | `Top, `Top -> true - | `Bot, `Bot -> true | `Lifted ds, `Lifted n -> let casted_ds = ID.cast_to (Cilfacade.ptrdiff_ikind ()) ds in let casted_n = ID.cast_to (Cilfacade.ptrdiff_ikind ()) n in let ds_eq_n = begin try ID.eq casted_ds casted_n - with IntDomain.ArithmeticOnIntegerBot _ -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () + with IntDomain.ArithmeticOnIntegerBot _ -> ID.top_of @@ Cilfacade.ptrdiff_ikind () end in - begin match ID.to_bool ds_eq_n with - | Some b -> b - | None -> false - end - | _, _ -> false + Option.value ~default:false ID.to_bool ds_eq_n + | _ -> false in let dest_a, dest_typ = addr_type_of_exp dst in let src_lval = mkMem ~addr:(Cil.stripCasts src) ~off:NoOffset in From 625e90b308159da7407f0fef01cd863bfc662d36 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 6 Oct 2023 23:56:11 +0300 Subject: [PATCH 0815/1312] Use `Option.map_default` instead of `match` Co-authored-by: Michael Schwarz --- src/analyses/base.ml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 6aaf25944e..cc5ddc9c9d 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2133,10 +2133,7 @@ struct let desc = LF.find f in let memory_copying dst src n = let dest_size = get_size_of_ptr_target ctx dst in - let n_intdom = match n with - | Some exp -> ctx.ask (Queries.EvalInt exp) - | None -> `Bot - in + let n_intdom = Option.map_default (fun exp -> ctx.ask (Queries.EvalInt exp)) `Bot n in let dest_size_equal_n = match dest_size, n_intdom with | `Lifted ds, `Lifted n -> From fbab25ec49ecee2727f9675e36e5b3116b4c1d91 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 6 Oct 2023 23:56:59 +0300 Subject: [PATCH 0816/1312] Use `_` in place of unused offset in lambda Co-authored-by: Michael Schwarz --- src/analyses/base.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index cc5ddc9c9d..df67d1afe4 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2058,7 +2058,7 @@ struct match ctx.ask (Queries.MayPointTo ptr) with | a when not (Queries.AD.is_top a)-> Queries.AD.for_all (function - | Addr (v, o) -> ctx.ask (Queries.IsHeapVar v) + | Addr (v, _) -> ctx.ask (Queries.IsHeapVar v) | _ -> false ) a | _ -> false From 91aeee732f1f97afdb406e189176f22c46049ff1 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 6 Oct 2023 23:57:46 +0300 Subject: [PATCH 0817/1312] Set `Cabs2cil.addNestedScopeAttr` based on the Goblint config option Co-authored-by: Michael Schwarz --- src/util/cilfacade.ml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/util/cilfacade.ml b/src/util/cilfacade.ml index 432b623464..3b00365abf 100644 --- a/src/util/cilfacade.ml +++ b/src/util/cilfacade.ml @@ -41,8 +41,7 @@ let init_options () = Mergecil.merge_inlines := get_bool "cil.merge.inlines"; Cil.cstd := Cil.cstd_of_string (get_string "cil.cstd"); Cil.gnu89inline := get_bool "cil.gnu89inline"; - if get_bool "cil.addNestedScopeAttr" then - Cabs2cil.addNestedScopeAttr := true + Cabs2cil.addNestedScopeAttr := get_bool "cil.addNestedScopeAttr"; let init () = initCIL (); From 992e5c0b183326d8a3acc102fd40c99e110c140d Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 6 Oct 2023 23:04:44 +0200 Subject: [PATCH 0818/1312] Remove extra semicolon --- src/util/cilfacade.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/cilfacade.ml b/src/util/cilfacade.ml index 3b00365abf..ba57074e5a 100644 --- a/src/util/cilfacade.ml +++ b/src/util/cilfacade.ml @@ -41,7 +41,7 @@ let init_options () = Mergecil.merge_inlines := get_bool "cil.merge.inlines"; Cil.cstd := Cil.cstd_of_string (get_string "cil.cstd"); Cil.gnu89inline := get_bool "cil.gnu89inline"; - Cabs2cil.addNestedScopeAttr := get_bool "cil.addNestedScopeAttr"; + Cabs2cil.addNestedScopeAttr := get_bool "cil.addNestedScopeAttr" let init () = initCIL (); From cc351e04e745b11aaab409a60b7d2dc7a7c32eab Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 6 Oct 2023 23:05:00 +0200 Subject: [PATCH 0819/1312] Use `Option.default` in place of `Option.value` --- src/analyses/base.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index df67d1afe4..f5da725226 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2144,7 +2144,7 @@ struct with IntDomain.ArithmeticOnIntegerBot _ -> ID.top_of @@ Cilfacade.ptrdiff_ikind () end in - Option.value ~default:false ID.to_bool ds_eq_n + Option.default false (ID.to_bool ds_eq_n) | _ -> false in let dest_a, dest_typ = addr_type_of_exp dst in From de0220baf762f69ba6f0da19299ce568b243bed4 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 6 Oct 2023 23:09:46 +0200 Subject: [PATCH 0820/1312] Use `AD.exists` to warn about non-local vars in address set instead of using `AD.iter` --- src/analyses/base.ml | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index f5da725226..908dc88401 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1052,17 +1052,14 @@ struct AnalysisStateUtil.set_mem_safety_flag InvalidDeref; M.warn ~category:M.Category.Behavior.Undefined.nullpointer_dereference ~tags:[CWE 476] "May dereference NULL pointer" ); - (* Warn if any of the addresses contains a non-local variable *) - AD.iter (function - | AD.Addr.Addr (v,o) -> - if not (CPA.mem v st.cpa) && not (is_global a v) then ( - (* TODO: Not the smartest move to set the global flag within an iter *) - (* TODO: We can resort to using AD.exists instead *) - AnalysisStateUtil.set_mem_safety_flag InvalidDeref; - M.warn "lval %a points to non-local variable %a. Invalid pointer dereference may occur" d_lval lval CilType.Varinfo.pretty v - ) - | _ -> () - ) adr + (* Warn if any of the addresses contains a non-local and non-global variable *) + if AD.exists (function + | AD.Addr.Addr (v, _) -> not (CPA.mem v st.cpa) && not (is_global a v) + | _ -> false + ) adr then ( + AnalysisStateUtil.set_mem_safety_flag InvalidDeref; + M.warn "lval %a points to a non-local variable. Invalid pointer dereference may occur" d_lval lval + ) ); AD.map (add_offset_varinfo (convert_offset a gs st ofs)) adr | _ -> From fd5237a6dfdd68f1cd6791030dd43227e9ed77fc Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 9 Oct 2023 11:40:54 +0300 Subject: [PATCH 0821/1312] Add 68-longjmp/56-longjmp-top extracted from concrat --- tests/regression/68-longjmp/56-longjmp-top.c | 21 ++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 tests/regression/68-longjmp/56-longjmp-top.c diff --git a/tests/regression/68-longjmp/56-longjmp-top.c b/tests/regression/68-longjmp/56-longjmp-top.c new file mode 100644 index 0000000000..4a12a43792 --- /dev/null +++ b/tests/regression/68-longjmp/56-longjmp-top.c @@ -0,0 +1,21 @@ +// Extracted from concrat/pigz. +#include +#include +#include + +pthread_key_t buf_key; + +int main() { + jmp_buf buf; + pthread_setspecific(buf_key, &buf); + + if (!setjmp(buf)) { + jmp_buf *buf_ptr; + buf_ptr = pthread_getspecific(buf_key); + longjmp(*buf_ptr, 1); // TODO NO CRASH: problem?! + } + else { + __goblint_check(1); // reachable + } + return 0; +} From 2224e86ce5e770cedd4558cfff6379471424e743 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 9 Oct 2023 11:46:24 +0300 Subject: [PATCH 0822/1312] Fix longjmp crash on Uninitialized --- src/analyses/base.ml | 11 +++++++++-- tests/regression/68-longjmp/56-longjmp-top.c | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 7b87d3ff51..2fda2540e8 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1217,9 +1217,16 @@ struct if copied then M.warn ~category:(Behavior (Undefined Other)) "The jump buffer %a contains values that were copied here instead of being set by setjmp. This is Undefined Behavior." d_exp e; x - | y -> failwith (GobPretty.sprintf "problem?! is %a %a:\n state is %a" CilType.Exp.pretty e VD.pretty y D.pretty ctx.local) + | Top + | Bot -> + JmpBufDomain.JmpBufSet.top () + | y -> + M.debug ~category:Imprecise "EvalJmpBuf %a is %a, not JmpBuf." CilType.Exp.pretty e VD.pretty y; + JmpBufDomain.JmpBufSet.top () end - | _ -> failwith "problem?!" + | _ -> + M.debug ~category:Imprecise "EvalJmpBuf is not Address"; + JmpBufDomain.JmpBufSet.top () end | Q.EvalInt e -> query_evalint (Analyses.ask_of_ctx ctx) ctx.global ctx.local e diff --git a/tests/regression/68-longjmp/56-longjmp-top.c b/tests/regression/68-longjmp/56-longjmp-top.c index 4a12a43792..4d57b42fd3 100644 --- a/tests/regression/68-longjmp/56-longjmp-top.c +++ b/tests/regression/68-longjmp/56-longjmp-top.c @@ -12,7 +12,7 @@ int main() { if (!setjmp(buf)) { jmp_buf *buf_ptr; buf_ptr = pthread_getspecific(buf_key); - longjmp(*buf_ptr, 1); // TODO NO CRASH: problem?! + longjmp(*buf_ptr, 1); // NO CRASH: problem?! } else { __goblint_check(1); // reachable From 8468a5a676fae48a82e0284ea13170d0cefa935c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 9 Oct 2023 12:12:58 +0300 Subject: [PATCH 0823/1312] Fix too broad try block in BaseInvariant Caused Invalid_argument("Cilfacade.get_fkind: non-float type int ") before, even though integer case is checked first, but something else in it raises. --- src/analyses/baseInvariant.ml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index 70c6ed9101..72e00efbb1 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -805,15 +805,15 @@ struct | BinOp ((Lt | Gt | Le | Ge | Eq | Ne | LAnd | LOr), _, _, _) -> true | _ -> false in - try - let ik = Cilfacade.get_ikind_exp exp in + match Cilfacade.get_ikind_exp exp with + | ik -> let itv = if not tv || is_cmp exp then (* false is 0, but true can be anything that is not 0, except for comparisons which yield 1 *) ID.of_bool ik tv (* this will give 1 for true which is only ok for comparisons *) else ID.of_excl_list ik [BI.zero] (* Lvals, Casts, arithmetic operations etc. should work with true = non_zero *) in inv_exp (Int itv) exp st - with Invalid_argument _ -> + | exception Invalid_argument _ -> let fk = Cilfacade.get_fkind_exp exp in let ftv = if not tv then (* false is 0, but true can be anything that is not 0, except for comparisons which yield 1 *) FD.of_const fk 0. From 56cf37e347039f9a2f12cfd360f0dece5e583c5a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 9 Oct 2023 12:50:22 +0300 Subject: [PATCH 0824/1312] Add missing library functions for concrat/dnspod-sr --- src/analyses/libraryFunctions.ml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 1c509e7660..8566cd6b0c 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -265,6 +265,9 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("access", unknown [drop "pathname" [r]; drop "mode" []]); ("ttyname", unknown ~attrs:[ThreadUnsafe] [drop "fd" []]); ("shm_open", unknown [drop "name" [r]; drop "oflag" []; drop "mode" []]); + ("shmget", unknown [drop "key" []; drop "size" []; drop "shmflag" []]); + ("shmat", unknown [drop "shmid" []; drop "shmaddr" []; drop "shmflag" []]) (* TODO: shmaddr? *); + ("shmdt", unknown [drop "shmaddr" []]) (* TODO: shmaddr? *); ("sched_get_priority_max", unknown [drop "policy" []]); ("mprotect", unknown [drop "addr" []; drop "len" []; drop "prot" []]); ("ftime", unknown [drop "tp" [w]]); @@ -364,6 +367,9 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("sigdelset", unknown [drop "set" [r; w]; drop "signum" []]); ("sigismember", unknown [drop "set" [r]; drop "signum" []]); ("sigprocmask", unknown [drop "how" []; drop "set" [r]; drop "oldset" [w]]); + ("sigwait", unknown [drop "set" [r]; drop "sig" [w]]); + ("sigwaitinfo", unknown [drop "set" [r]; drop "info" [w]]); + ("sigtimedwait", unknown [drop "set" [r]; drop "info" [w]; drop "timeout" [r]]); ("fork", unknown []); ("dlopen", unknown [drop "filename" [r]; drop "flag" []]); ("dlerror", unknown ~attrs:[ThreadUnsafe] []); @@ -566,6 +572,7 @@ let glibc_desc_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("atoq", unknown [drop "nptr" [r]]); ("strchrnul", unknown [drop "s" [r]; drop "c" []]); ("getdtablesize", unknown []); + ("daemon", unknown [drop "nochdir" []; drop "noclose" []]); ] let linux_userspace_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ @@ -1135,7 +1142,6 @@ let invalidate_actions = [ "umount", readsAll;(*safe*) "scandir", writes [1;3;4];(*keep [1;3;4]*) "unlink", readsAll;(*safe*) - "sigwait", writesAllButFirst 1 readsAll;(*drop 1*) "bindtextdomain", readsAll;(*safe*) "textdomain", readsAll;(*safe*) "dcgettext", readsAll;(*safe*) From a13e2b921268d7b4f894494a32424ed952c4b145 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 9 Oct 2023 12:54:12 +0300 Subject: [PATCH 0825/1312] Add mremap library function for concrat/kona --- src/analyses/libraryFunctions.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 8566cd6b0c..87534e97bb 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -589,6 +589,7 @@ let linux_userspace_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__xpg_basename", unknown [drop "path" [r]]); ("ptrace", unknown (drop "request" [] :: VarArgs (drop' [r_deep; w_deep]))); (* man page has 4 arguments, but header has varargs and real-world programs may call with <4 *) ("madvise", unknown [drop "addr" []; drop "length" []; drop "advice" []]); + ("mremap", unknown (drop "old_address" [] :: drop "old_size" [] :: drop "new_size" [] :: drop "flags" [] :: VarArgs (drop "new_address" []))); ("inotify_init1", unknown [drop "flags" []]); ("inotify_add_watch", unknown [drop "fd" []; drop "pathname" [r]; drop "mask" []]); ("inotify_rm_watch", unknown [drop "fd" []; drop "wd" []]); From 530781376bfc10281fd0a3451e7bb683d3e28141 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 9 Oct 2023 13:03:11 +0300 Subject: [PATCH 0826/1312] Add missing library functions for concrat/lmdb --- src/analyses/libraryFunctions.ml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 87534e97bb..a1a79f5f5c 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -246,6 +246,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("mkfifo", unknown [drop "pathname" [r]; drop "mode" []]); ("ntohs", unknown [drop "netshort" []]); ("alarm", unknown [drop "seconds" []]); + ("pread", unknown [drop "fd" []; drop "buf" [w]; drop "count" []; drop "offset" []]); ("pwrite", unknown [drop "fd" []; drop "buf" [r]; drop "count" []; drop "offset" []]); ("hstrerror", unknown [drop "err" []]); ("inet_ntoa", unknown ~attrs:[ThreadUnsafe] [drop "in" []]); @@ -379,6 +380,8 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("uname", unknown [drop "buf" [w_deep]]); ("strcasecmp", unknown [drop "s1" [r]; drop "s2" [r]]); ("strncasecmp", unknown [drop "s1" [r]; drop "s2" [r]; drop "n" []]); + ("fsync", unknown [drop "fd" []]); + ("fdatasync", unknown [drop "fd" []]); ] (** Pthread functions. *) @@ -443,6 +446,10 @@ let pthread_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("pthread_attr_setschedpolicy", unknown [drop "attr" [r; w]; drop "policy" []]); ("pthread_condattr_init", unknown [drop "attr" [w]]); ("pthread_condattr_setclock", unknown [drop "attr" [w]; drop "clock_id" []]); + ("pthread_mutexattr_getpshared", unknown [drop "attr" [r]; drop "pshared" [w]]); + ("pthread_mutexattr_setpshared", unknown [drop "attr" [w]; drop "pshared" []]); + ("pthread_mutexattr_getrobust", unknown [drop "attr" [r]; drop "pshared" [w]]); + ("pthread_mutexattr_setrobust", unknown [drop "attr" [w]; drop "pshared" []]); ("pthread_mutexattr_destroy", unknown [drop "attr" [f]]); ("pthread_attr_setschedparam", unknown [drop "attr" [r; w]; drop "param" [r]]); ("pthread_setaffinity_np", unknown [drop "thread" []; drop "cpusetsize" []; drop "cpuset" [r]]); @@ -590,12 +597,15 @@ let linux_userspace_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("ptrace", unknown (drop "request" [] :: VarArgs (drop' [r_deep; w_deep]))); (* man page has 4 arguments, but header has varargs and real-world programs may call with <4 *) ("madvise", unknown [drop "addr" []; drop "length" []; drop "advice" []]); ("mremap", unknown (drop "old_address" [] :: drop "old_size" [] :: drop "new_size" [] :: drop "flags" [] :: VarArgs (drop "new_address" []))); + ("msync", unknown [drop "addr" []; drop "len" []; drop "flags" []]); ("inotify_init1", unknown [drop "flags" []]); ("inotify_add_watch", unknown [drop "fd" []; drop "pathname" [r]; drop "mask" []]); ("inotify_rm_watch", unknown [drop "fd" []; drop "wd" []]); ("fts_open", unknown [drop "path_argv" [r_deep]; drop "options" []; drop "compar" [s]]); (* TODO: use Call instead of Spawn *) ("fts_read", unknown [drop "ftsp" [r_deep; w_deep]]); ("fts_close", unknown [drop "ftsp" [f_deep]]); + ("statfs", unknown [drop "path" [r]; drop "buf" [w]]); + ("fstatfs", unknown [drop "fd" []; drop "buf" [w]]); ] let big_kernel_lock = AddrOf (Cil.var (Cilfacade.create_var (makeGlobalVar "[big kernel lock]" intType))) @@ -1134,7 +1144,6 @@ let invalidate_actions = [ "lstat__extinline", writesAllButFirst 1 readsAll;(*drop 1*) "umount2", readsAll;(*safe*) "waitpid", readsAll;(*safe*) - "statfs", writes [1;3;4];(*keep [1;3;4]*) "mount", readsAll;(*safe*) "__open_alias", readsAll;(*safe*) "__open_2", readsAll;(*safe*) From 25ef4ce6f33be1406b9af3e0c8ca1ed3a20e0474 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 9 Oct 2023 13:12:07 +0300 Subject: [PATCH 0827/1312] Add missing library functions for concrat/minimap2 --- src/analyses/libraryFunctions.ml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index a1a79f5f5c..af96e10c06 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -42,6 +42,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("getc", unknown [drop "stream" [r_deep; w_deep]]); ("fgets", unknown [drop "str" [w]; drop "count" []; drop "stream" [r_deep; w_deep]]); ("fopen", unknown [drop "pathname" [r]; drop "mode" [r]]); + ("freopen", unknown [drop "pathname" [r]; drop "mode" [r]; drop "stream" [r_deep; w_deep]]); ("printf", unknown (drop "format" [r] :: VarArgs (drop' [r]))); ("fprintf", unknown (drop "stream" [r_deep; w_deep] :: drop "format" [r] :: VarArgs (drop' [r]))); ("sprintf", unknown (drop "buffer" [w] :: drop "format" [r] :: VarArgs (drop' [r]))); @@ -382,6 +383,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("strncasecmp", unknown [drop "s1" [r]; drop "s2" [r]; drop "n" []]); ("fsync", unknown [drop "fd" []]); ("fdatasync", unknown [drop "fd" []]); + ("getrusage", unknown [drop "who" []; drop "usage" [w]]); ] (** Pthread functions. *) @@ -987,6 +989,10 @@ let zlib_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("inflateInit2", unknown [drop "strm" [r_deep; w_deep]; drop "windowBits" []]); ("inflateInit2_", unknown [drop "strm" [r_deep; w_deep]; drop "windowBits" []; drop "version" [r]; drop "stream_size" []]); ("inflateEnd", unknown [drop "strm" [f_deep]]); + ("gzopen", unknown [drop "path" [r]; drop "mode" [r]]); + ("gzdopen", unknown [drop "fd" []; drop "mode" [r]]); + ("gzread", unknown [drop "file" [r_deep; w_deep]; drop "buf" [w]; drop "len" []]); + ("gzclose", unknown [drop "file" [f_deep]]); ] let liblzma_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ From 9f614ebbd474a0c8b9696a39cbd63620e24e3ae4 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 9 Oct 2023 13:21:03 +0300 Subject: [PATCH 0828/1312] Add missing library functions for concrat/phpspy --- src/analyses/libraryFunctions.ml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index af96e10c06..c795c04084 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -111,6 +111,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("vprintf", unknown [drop "format" [r]; drop "vlist" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) ("vfprintf", unknown [drop "stream" [r_deep; w_deep]; drop "format" [r]; drop "vlist" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) ("vsprintf", unknown [drop "buffer" [w]; drop "format" [r]; drop "vlist" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) + ("asprintf", unknown (drop "strp" [w] :: drop "format" [r] :: VarArgs (drop' [r_deep]))); (* TODO: glibc section? *) ("vasprintf", unknown [drop "strp" [w]; drop "format" [r]; drop "ap" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) ("vsnprintf", unknown [drop "str" [w]; drop "size" []; drop "format" [r]; drop "ap" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) ("mktime", unknown [drop "tm" [r;w]]); @@ -339,6 +340,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("execl", unknown (drop "path" [r] :: drop "arg" [r] :: VarArgs (drop' [r]))); ("statvfs", unknown [drop "path" [r]; drop "buf" [w]]); ("readlink", unknown [drop "path" [r]; drop "buf" [w]; drop "bufsz" []]); + ("wcwidth", unknown [drop "c" []]); ("wcswidth", unknown [drop "s" [r]; drop "n" []]); ("link", unknown [drop "oldpath" [r]; drop "newpath" [r]]); ("renameat", unknown [drop "olddirfd" []; drop "oldpath" [r]; drop "newdirfd" []; drop "newpath" [r]]); @@ -518,6 +520,12 @@ let gcc_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__atomic_clear", unknown [drop "ptr" [w]; drop "memorder" []]); ("__atomic_compare_exchange_n", unknown [drop "ptr" [r; w]; drop "expected" [r; w]; drop "desired" []; drop "weak" []; drop "success_memorder" []; drop "failure_memorder" []]); ("__atomic_compare_exchange", unknown [drop "ptr" [r; w]; drop "expected" [r; w]; drop "desired" [r]; drop "weak" []; drop "success_memorder" []; drop "failure_memorder" []]); + ("__atomic_add_fetch", unknown [drop "ptr" [r; w]; drop "val" []; drop "memorder" []]); + ("__atomic_sub_fetch", unknown [drop "ptr" [r; w]; drop "val" []; drop "memorder" []]); + ("__atomic_and_fetch", unknown [drop "ptr" [r; w]; drop "val" []; drop "memorder" []]); + ("__atomic_xor_fetch", unknown [drop "ptr" [r; w]; drop "val" []; drop "memorder" []]); + ("__atomic_or_fetch", unknown [drop "ptr" [r; w]; drop "val" []; drop "memorder" []]); + ("__atomic_nand_fetch", unknown [drop "ptr" [r; w]; drop "val" []; drop "memorder" []]); ("__atomic_fetch_add", unknown [drop "ptr" [r; w]; drop "val" []; drop "memorder" []]); ("__atomic_fetch_sub", unknown [drop "ptr" [r; w]; drop "val" []; drop "memorder" []]); ("__atomic_fetch_and", unknown [drop "ptr" [r; w]; drop "val" []; drop "memorder" []]); @@ -608,6 +616,8 @@ let linux_userspace_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("fts_close", unknown [drop "ftsp" [f_deep]]); ("statfs", unknown [drop "path" [r]; drop "buf" [w]]); ("fstatfs", unknown [drop "fd" []; drop "buf" [w]]); + ("cfmakeraw", unknown [drop "termios" [r; w]]); + ("process_vm_readv", unknown [drop "pid" []; drop "local_iov" [w_deep]; drop "liovcnt" []; drop "remote_iov" []; drop "riovcnt" []; drop "flags" []]); ] let big_kernel_lock = AddrOf (Cil.var (Cilfacade.create_var (makeGlobalVar "[big kernel lock]" intType))) From c22250889672d27f11aa99a33593218a70502bad Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 9 Oct 2023 13:22:49 +0300 Subject: [PATCH 0829/1312] Add alphasort library function for concrat/ProcDump-for-Linux --- src/analyses/libraryFunctions.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index c795c04084..2ea99cdcaa 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -386,6 +386,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("fsync", unknown [drop "fd" []]); ("fdatasync", unknown [drop "fd" []]); ("getrusage", unknown [drop "who" []; drop "usage" [w]]); + ("alphasort", unknown [drop "a" [r]; drop "b" [r]]); ] (** Pthread functions. *) From 969d3156b879ba6a22f3d752945d320364e917e6 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 9 Oct 2023 13:43:02 +0300 Subject: [PATCH 0830/1312] Add missing library functions for concrat/Remotery --- src/analyses/libraryFunctions.ml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 2ea99cdcaa..1247a66497 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -395,6 +395,7 @@ let pthread_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("pthread_exit", special [__ "retval" []] @@ fun retval -> ThreadExit { ret_val = retval }); (* Doesn't dereference the void* itself, but just passes to pthread_join. *) ("pthread_join", special [__ "thread" []; __ "retval" [w]] @@ fun thread retval -> ThreadJoin {thread; ret_var = retval}); ("pthread_kill", unknown [drop "thread" []; drop "sig" []]); + ("pthread_equal", unknown [drop "t1" []; drop "t2" []]); ("pthread_cond_init", unknown [drop "cond" [w]; drop "attr" [r]]); ("__pthread_cond_init", unknown [drop "cond" [w]; drop "attr" [r]]); ("pthread_cond_signal", special [__ "cond" []] @@ fun cond -> Signal cond); @@ -535,6 +536,7 @@ let gcc_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__atomic_fetch_nand", unknown [drop "ptr" [r; w]; drop "val" []; drop "memorder" []]); ("__atomic_test_and_set", unknown [drop "ptr" [r; w]; drop "memorder" []]); ("__atomic_thread_fence", unknown [drop "memorder" []]); + ("__sync_bool_compare_and_swap", unknown [drop "ptr" [r; w]; drop "oldval" []; drop "newval" []]); ("__sync_fetch_and_add", unknown (drop "ptr" [r; w] :: drop "value" [] :: VarArgs (drop' []))); ("__sync_fetch_and_sub", unknown (drop "ptr" [r; w] :: drop "value" [] :: VarArgs (drop' []))); ("__builtin_va_copy", unknown [drop "dest" [w]; drop "src" [r]]); From 012282132c4fc362ee111e2fe91e90da4752610a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 9 Oct 2023 13:56:58 +0300 Subject: [PATCH 0831/1312] Add missing library functions for concrat/siege --- src/analyses/libraryFunctions.ml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 1247a66497..7bc373e3cc 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -387,6 +387,8 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("fdatasync", unknown [drop "fd" []]); ("getrusage", unknown [drop "who" []; drop "usage" [w]]); ("alphasort", unknown [drop "a" [r]; drop "b" [r]]); + ("gmtime_r", unknown [drop "timer" [r]; drop "result" [w]]); + ("rand_r", special [drop "seedp" [r; w]] Rand); ] (** Pthread functions. *) @@ -447,6 +449,8 @@ let pthread_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("pthread_key_create", unknown [drop "key" [w]; drop "destructor" [s]]); ("pthread_key_delete", unknown [drop "key" [f]]); ("pthread_cancel", unknown [drop "thread" []]); + ("pthread_testcancel", unknown []); + ("pthread_setcancelstate", unknown [drop "state" []; drop "oldstate" [w]]); ("pthread_setcanceltype", unknown [drop "type" []; drop "oldtype" [w]]); ("pthread_detach", unknown [drop "thread" []]); ("pthread_attr_setschedpolicy", unknown [drop "attr" [r; w]; drop "policy" []]); From c630a3926f71b740e002ae4ff6e6bdf515142cf4 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 9 Oct 2023 13:57:09 +0300 Subject: [PATCH 0832/1312] Add warn library function for concrat/the_silver_searcher --- src/analyses/libraryFunctions.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 7bc373e3cc..62dbe2aa7c 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -551,7 +551,8 @@ let gcc_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ let glibc_desc_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("fputs_unlocked", unknown [drop "s" [r]; drop "stream" [w]]); ("futimesat", unknown [drop "dirfd" []; drop "pathname" [r]; drop "times" [r]]); - ("error", unknown ((drop "status" []):: (drop "errnum" []) :: (drop "format" [r]) :: (VarArgs (drop' [r])))); + ("error", unknown ((drop "status" []) :: (drop "errnum" []) :: (drop "format" [r]) :: (VarArgs (drop' [r])))); + ("warn", unknown (drop "format" [r] :: VarArgs (drop' [r]))); ("gettext", unknown [drop "msgid" [r]]); ("euidaccess", unknown [drop "pathname" [r]; drop "mode" []]); ("rpmatch", unknown [drop "response" [r]]); From 0af8ba71bac405e1e7ade51607cee49f91dd2f3a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 9 Oct 2023 14:39:47 +0300 Subject: [PATCH 0833/1312] Add zError library function for concrat/the_silver_searcher --- src/analyses/libraryFunctions.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 62dbe2aa7c..03a984fede 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -1007,6 +1007,7 @@ let zlib_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("inflateInit2", unknown [drop "strm" [r_deep; w_deep]; drop "windowBits" []]); ("inflateInit2_", unknown [drop "strm" [r_deep; w_deep]; drop "windowBits" []; drop "version" [r]; drop "stream_size" []]); ("inflateEnd", unknown [drop "strm" [f_deep]]); + ("zError", unknown [drop "err" []]); ("gzopen", unknown [drop "path" [r]; drop "mode" [r]]); ("gzdopen", unknown [drop "fd" []; drop "mode" [r]]); ("gzread", unknown [drop "file" [r_deep; w_deep]; drop "buf" [w]; drop "len" []]); From c6f7180617d67f5e00845cfc80b2a6f7e78e9dda Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 9 Oct 2023 15:37:37 +0300 Subject: [PATCH 0834/1312] Add more duplicate library function checks --- src/analyses/libraryFunctions.ml | 40 ++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 1c509e7660..f30f40cbdf 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -994,11 +994,43 @@ let libraries = Hashtbl.of_list [ ("liblzma", liblzma_descs_list); ] +let libraries = + Hashtbl.map (fun library descs_list -> + let descs_tbl = Hashtbl.create 113 in + List.iter (fun (name, desc) -> + Hashtbl.modify_opt name (function + | None -> Some desc + | Some _ -> failwith (Format.sprintf "Library function %s specified multiple times in library %s" name library) + ) descs_tbl + ) descs_list; + descs_tbl + ) libraries + +let all_library_descs: (string, LibraryDesc.t) Hashtbl.t = + Hashtbl.fold (fun _ descs_tbl acc -> + Hashtbl.merge (fun name desc1 desc2 -> + match desc1, desc2 with + | Some _, Some _ -> failwith (Format.sprintf "Library function %s specified in multiple libraries" name) + | (Some _ as desc), None + | None, (Some _ as desc) -> desc + | None, None -> assert false + ) acc descs_tbl + ) libraries (Hashtbl.create 0) + let activated_library_descs: (string, LibraryDesc.t) Hashtbl.t ResettableLazy.t = + let union = + Hashtbl.merge (fun _ desc1 desc2 -> + match desc1, desc2 with + | (Some _ as desc), None + | None, (Some _ as desc) -> desc + | _, _ -> assert false + ) + in ResettableLazy.from_fun (fun () -> - let activated = List.unique (GobConfig.get_string_list "lib.activated") in - let desc_list = List.concat_map (Hashtbl.find libraries) activated in - Hashtbl.of_list desc_list + GobConfig.get_string_list "lib.activated" + |> List.unique + |> List.map (Hashtbl.find libraries) + |> List.fold_left union (Hashtbl.create 0) ) let reset_lazy () = @@ -1201,7 +1233,7 @@ let invalidate_actions = [ ] let () = List.iter (fun (x, _) -> - if Hashtbl.exists (fun _ b -> List.mem_assoc x b) libraries then + if Hashtbl.mem all_library_descs x then failwith ("You have added a function to invalidate_actions that already exists in libraries. Please undo this for function: " ^ x); ) invalidate_actions From 44e01f563bc6cf74399af4e4251456c54325a34e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 9 Oct 2023 15:43:04 +0300 Subject: [PATCH 0835/1312] Remove duplicate library functions --- src/analyses/libraryFunctions.ml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index f30f40cbdf..0360617171 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -244,7 +244,6 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("symlink" , unknown [drop "oldpath" [r]; drop "newpath" [r];]); ("ftruncate", unknown [drop "fd" []; drop "length" []]); ("mkfifo", unknown [drop "pathname" [r]; drop "mode" []]); - ("ntohs", unknown [drop "netshort" []]); ("alarm", unknown [drop "seconds" []]); ("pwrite", unknown [drop "fd" []; drop "buf" [r]; drop "count" []; drop "offset" []]); ("hstrerror", unknown [drop "err" []]); @@ -275,7 +274,6 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("lstat", unknown [drop "pathname" [r]; drop "statbuf" [w]]); ("fstat", unknown [drop "fd" []; drop "buf" [w]]); ("fstatat", unknown [drop "dirfd" []; drop "pathname" [r]; drop "buf" [w]; drop "flags" []]); - ("getpwnam", unknown [drop "name" [r]]); ("chdir", unknown [drop "path" [r]]); ("closedir", unknown [drop "dirp" [r]]); ("mkdir", unknown [drop "pathname" [r]; drop "mode" []]); @@ -295,7 +293,6 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("freeaddrinfo", unknown [drop "res" [f_deep]]); ("getgid", unknown []); ("pselect", unknown [drop "nfds" []; drop "readdfs" [r]; drop "writedfs" [r]; drop "exceptfds" [r]; drop "timeout" [r]; drop "sigmask" [r]]); - ("strncasecmp", unknown [drop "s1" [r]; drop "s2" [r]; drop "n" []]); ("getnameinfo", unknown [drop "addr" [r_deep]; drop "addrlen" []; drop "host" [w]; drop "hostlen" []; drop "serv" [w]; drop "servlen" []; drop "flags" []]); ("strtok_r", unknown [drop "str" [r; w]; drop "delim" [r]; drop "saveptr" [r_deep; w_deep]]); (* deep accesses through saveptr if str is NULL: https://github.com/lattera/glibc/blob/895ef79e04a953cac1493863bcae29ad85657ee1/string/strtok_r.c#L31-L40 *) ("kill", unknown [drop "pid" []; drop "sig" []]); @@ -437,7 +434,6 @@ let pthread_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("pthread_attr_setschedpolicy", unknown [drop "attr" [r; w]; drop "policy" []]); ("pthread_condattr_init", unknown [drop "attr" [w]]); ("pthread_condattr_setclock", unknown [drop "attr" [w]; drop "clock_id" []]); - ("pthread_mutexattr_destroy", unknown [drop "attr" [f]]); ("pthread_attr_setschedparam", unknown [drop "attr" [r; w]; drop "param" [r]]); ("pthread_setaffinity_np", unknown [drop "thread" []; drop "cpusetsize" []; drop "cpuset" [r]]); ("pthread_getaffinity_np", unknown [drop "thread" []; drop "cpusetsize" []; drop "cpuset" [w]]); From 599bbb5ed55a7a8ab509271193e4c2df05dadbbe Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 9 Oct 2023 15:53:34 +0300 Subject: [PATCH 0836/1312] Refactor invalidate actions table --- src/analyses/libraryFunctions.ml | 38 +++++++++----------------------- 1 file changed, 11 insertions(+), 27 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 0360617171..aa279ff324 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -1228,32 +1228,16 @@ let invalidate_actions = [ "__goblint_assume_join", readsAll; ] -let () = List.iter (fun (x, _) -> - if Hashtbl.mem all_library_descs x then - failwith ("You have added a function to invalidate_actions that already exists in libraries. Please undo this for function: " ^ x); - ) invalidate_actions - -(* used by get_invalidate_action to make sure - * that hash of invalidates is built only once - * - * Hashtable from strings to functions of type (exp list -> exp list) -*) -let processed_table = ref None - -let get_invalidate_action name = - let tbl = match !processed_table with - | None -> begin - let hash = Hashtbl.create 113 in - let f (k, v) = Hashtbl.add hash k v in - List.iter f invalidate_actions; - processed_table := (Some hash); - hash - end - | Some x -> x - in - if Hashtbl.mem tbl name - then Some (Hashtbl.find tbl name) - else None +let invalidate_actions = + let tbl = Hashtbl.create 113 in + List.iter (fun (name, old_accesses) -> + Hashtbl.modify_opt name (function + | None when Hashtbl.mem all_library_descs name -> failwith (Format.sprintf "Library function %s specified both in libraries and invalidate actions" name) + | None -> Some old_accesses + | Some _ -> failwith (Format.sprintf "Library function %s specified multiple times in invalidate actions" name) + ) tbl + ) invalidate_actions; + tbl let lib_funs = ref (Set.String.of_list ["__raw_read_unlock"; "__raw_write_unlock"; "spin_trylock"]) @@ -1297,7 +1281,7 @@ let find f = match Hashtbl.find_option (ResettableLazy.force activated_library_descs) name with | Some desc -> desc | None -> - match get_invalidate_action name with + match Hashtbl.find_option invalidate_actions name with | Some old_accesses -> LibraryDesc.of_old old_accesses | None -> From 6fd299852648e1dde28eeb0b70e5684b9f471dab Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 9 Oct 2023 16:09:39 +0300 Subject: [PATCH 0837/1312] Fix references to options.schema.json --- .github/workflows/options.yml | 6 +++--- .readthedocs.yaml | 2 +- docs/user-guide/configuring.md | 2 +- src/common/options.ml | 2 +- src/goblint_lib.ml | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/options.yml b/.github/workflows/options.yml index b5f690a700..84906d4949 100644 --- a/.github/workflows/options.yml +++ b/.github/workflows/options.yml @@ -26,10 +26,10 @@ jobs: run: npm install -g ajv-cli - name: Migrate schema # https://github.com/ajv-validator/ajv-cli/issues/199 - run: ajv migrate -s src/util/options.schema.json + run: ajv migrate -s src/common/options.schema.json - name: Validate conf - run: ajv validate -s src/util/options.schema.json -d "conf/**/*.json" + run: ajv validate -s src/common/options.schema.json -d "conf/**/*.json" - name: Validate incremental tests - run: ajv validate -s src/util/options.schema.json -d "tests/incremental/*/*.json" + run: ajv validate -s src/common/options.schema.json -d "tests/incremental/*/*.json" diff --git a/.readthedocs.yaml b/.readthedocs.yaml index c9b41df49d..4827b825ef 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -20,4 +20,4 @@ build: - pip install json-schema-for-humans post_build: - mkdir _readthedocs/html/jsfh/ - - generate-schema-doc --config-file jsfh.yml src/util/options.schema.json _readthedocs/html/jsfh/ + - generate-schema-doc --config-file jsfh.yml src/common/options.schema.json _readthedocs/html/jsfh/ diff --git a/docs/user-guide/configuring.md b/docs/user-guide/configuring.md index 82e92f6fe7..348e15dac4 100644 --- a/docs/user-guide/configuring.md +++ b/docs/user-guide/configuring.md @@ -24,7 +24,7 @@ In `.vscode/settings.json` add the following: "/conf/*.json", "/tests/incremental/*/*.json" ], - "url": "/src/util/options.schema.json" + "url": "/src/common/options.schema.json" } ] } diff --git a/src/common/options.ml b/src/common/options.ml index d352c86465..c9bd41038f 100644 --- a/src/common/options.ml +++ b/src/common/options.ml @@ -1,4 +1,4 @@ -(** [src/util/options.schema.json] low-level access. *) +(** [src/common/options.schema.json] low-level access. *) open Json_schema diff --git a/src/goblint_lib.ml b/src/goblint_lib.ml index e009ecf86b..a108058291 100644 --- a/src/goblint_lib.ml +++ b/src/goblint_lib.ml @@ -49,7 +49,7 @@ module VarQuery = VarQuery (** {2 Configuration} Runtime configuration is represented as JSON. - Options are specified and documented by the JSON schema [src/util/options.schema.json]. *) + Options are specified and documented by the JSON schema [src/common/options.schema.json]. *) module GobConfig = GobConfig module AfterConfig = AfterConfig From 809f84b905a4b85f6e56d7fc74a5d252dfe90a5e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 9 Oct 2023 16:12:52 +0300 Subject: [PATCH 0838/1312] Update Gobview for goblint.common --- gobview | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gobview b/gobview index b373d06174..41be36b548 160000 --- a/gobview +++ b/gobview @@ -1 +1 @@ -Subproject commit b373d06174667537b671f3122daf4ebd4b195aea +Subproject commit 41be36b54837b24e6de83740c34e810d3d1afdfb From 5aa420441c248a582b4484df170666b75fee5377 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 9 Oct 2023 16:20:50 +0200 Subject: [PATCH 0839/1312] Some ~15 more library functions (#1203) * More socket * `recvfrom` * `writev` / `readv` * `popen` * `stat` / `fstat` * `statfs` * `mount` / `umount` * Fix `select` Co-authored-by: Simmo Saan * Rm duplicate `fstat` Co-authored-by: Simmo Saan * Rm duplicates --------- Co-authored-by: Simmo Saan --- src/analyses/libraryFunctions.ml | 33 ++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 1c509e7660..c4d1acf76a 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -285,7 +285,9 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("read", unknown [drop "fd" []; drop "buf" [w]; drop "count" []]); ("write", unknown [drop "fd" []; drop "buf" [r]; drop "count" []]); ("recv", unknown [drop "sockfd" []; drop "buf" [w]; drop "len" []; drop "flags" []]); + ("recvfrom", unknown [drop "sockfd" []; drop "buf" [w]; drop "len" []; drop "flags" []; drop "src_addr" [w_deep]; drop "addrlen" [r; w]]); ("send", unknown [drop "sockfd" []; drop "buf" [r]; drop "len" []; drop "flags" []]); + ("sendto", unknown [drop "sockfd" []; drop "buf" [r]; drop "len" []; drop "flags" []; drop "dest_addr" [r_deep]; drop "addrlen" []]); ("strdup", unknown [drop "s" [r]]); ("strndup", unknown [drop "s" [r]; drop "n" []]); ("syscall", unknown (drop "number" [] :: VarArgs (drop' [r; w]))); @@ -373,6 +375,18 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("uname", unknown [drop "buf" [w_deep]]); ("strcasecmp", unknown [drop "s1" [r]; drop "s2" [r]]); ("strncasecmp", unknown [drop "s1" [r]; drop "s2" [r]; drop "n" []]); + ("connect", unknown [drop "sockfd" []; drop "sockaddr" [r_deep]; drop "addrlen" []]); + ("bind", unknown [drop "sockfd" []; drop "sockaddr" [r_deep]; drop "addrlen" []]); + ("listen", unknown [drop "sockfd" []; drop "backlog" []]); + ("select", unknown [drop "nfds" []; drop "readfds" [r; w]; drop "writefds" [r; w]; drop "exceptfds" [r; w]; drop "timeout" [r; w]]); + ("accept", unknown [drop "sockfd" []; drop "addr" [w_deep]; drop "addrlen" [r; w]]); + ("close", unknown [drop "fd" []]); + ("writev", unknown [drop "fd" []; drop "iov" [r_deep]; drop "iovcnt" []]); + ("readv", unknown [drop "fd" []; drop "iov" [w_deep]; drop "iovcnt" []]); + ("unlink", unknown [drop "pathname" [r]]); + ("popen", unknown [drop "command" [r]; drop "type" [r]]); + ("stat", unknown [drop "pathname" [r]; drop "statbuf" [w]]); + ("statfs", unknown [drop "path" [r]; drop "buf" [w]]); ] (** Pthread functions. *) @@ -588,6 +602,9 @@ let linux_userspace_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("fts_open", unknown [drop "path_argv" [r_deep]; drop "options" []; drop "compar" [s]]); (* TODO: use Call instead of Spawn *) ("fts_read", unknown [drop "ftsp" [r_deep; w_deep]]); ("fts_close", unknown [drop "ftsp" [f_deep]]); + ("mount", unknown [drop "source" [r]; drop "target" [r]; drop "filesystemtype" [r]; drop "mountflags" []; drop "data" [r]]); + ("umount", unknown [drop "target" [r]]); + ("umount2", unknown [drop "target" [r]; drop "flags" []]); ] let big_kernel_lock = AddrOf (Cil.var (Cilfacade.create_var (makeGlobalVar "[big kernel lock]" intType))) @@ -1100,7 +1117,6 @@ open Invalidate * We assume that no known functions that are reachable are executed/spawned. For that we use ThreadCreate above. *) (* WTF: why are argument numbers 1-indexed (in partition)? *) let invalidate_actions = [ - "connect", readsAll; (*safe*) "__printf_chk", readsAll;(*safe*) "printk", readsAll;(*safe*) "__mutex_init", readsAll;(*safe*) @@ -1118,23 +1134,17 @@ let invalidate_actions = [ "atoi__extinline", readsAll;(*safe*) "_IO_getc", writesAll;(*unsafe*) "pipe", writesAll;(*unsafe*) - "close", writesAll;(*unsafe*) "strerror_r", writesAll;(*unsafe*) "raise", writesAll;(*unsafe*) "_strlen", readsAll;(*safe*) "stat__extinline", writesAllButFirst 1 readsAll;(*drop 1*) "lstat__extinline", writesAllButFirst 1 readsAll;(*drop 1*) - "umount2", readsAll;(*safe*) "waitpid", readsAll;(*safe*) - "statfs", writes [1;3;4];(*keep [1;3;4]*) - "mount", readsAll;(*safe*) "__open_alias", readsAll;(*safe*) "__open_2", readsAll;(*safe*) "ioctl", writesAll;(*unsafe*) "fstat__extinline", writesAll;(*unsafe*) - "umount", readsAll;(*safe*) "scandir", writes [1;3;4];(*keep [1;3;4]*) - "unlink", readsAll;(*safe*) "sigwait", writesAllButFirst 1 readsAll;(*drop 1*) "bindtextdomain", readsAll;(*safe*) "textdomain", readsAll;(*safe*) @@ -1149,11 +1159,9 @@ let invalidate_actions = [ "svctcp_create", readsAll;(*safe*) "clntudp_bufcreate", writesAll;(*unsafe*) "authunix_create_default", readsAll;(*safe*) - "writev", readsAll;(*safe*) "clnt_broadcast", writesAll;(*unsafe*) "clnt_sperrno", readsAll;(*safe*) "pmap_unset", writesAll;(*unsafe*) - "bind", readsAll;(*safe*) "svcudp_create", readsAll;(*safe*) "svc_register", writesAll;(*unsafe*) "svc_run", writesAll;(*unsafe*) @@ -1162,18 +1170,13 @@ let invalidate_actions = [ "__builtin___vsnprintf_chk", writesAllButFirst 3 readsAll; (*drop 3*) "__error", readsAll; (*safe*) "__maskrune", writesAll; (*unsafe*) - "listen", readsAll; (*safe*) - "select", writes [1;5]; (*keep [1;5]*) - "accept", writesAll; (*keep [1]*) "times", writesAll; (*unsafe*) "timespec_get", writes [1]; "__tolower", readsAll; (*safe*) "signal", writesAll; (*unsafe*) - "popen", readsAll; (*safe*) "BF_cfb64_encrypt", writes [1;3;4;5]; (*keep [1;3;4,5]*) "BZ2_bzBuffToBuffDecompress", writes [3;4]; (*keep [3;4]*) "uncompress", writes [3;4]; (*keep [3;4]*) - "stat", writes [2]; (*keep [1]*) "__xstat", writes [3]; (*keep [1]*) "__lxstat", writes [3]; (*keep [1]*) "remove", readsAll; @@ -1181,8 +1184,6 @@ let invalidate_actions = [ "compress2", writes [3]; (*keep [3]*) "__toupper", readsAll; (*safe*) "BF_set_key", writes [3]; (*keep [3]*) - "sendto", writes [2;4]; (*keep [2;4]*) - "recvfrom", writes [4;5]; (*keep [4;5]*) "PL_NewHashTable", readsAll; (*safe*) "assert_failed", readsAll; (*safe*) "munmap", readsAll;(*safe*) From 5ac2f23a2029290940b65b85554f69242b42d830 Mon Sep 17 00:00:00 2001 From: Nathan Schmidt Date: Mon, 9 Oct 2023 17:18:08 +0200 Subject: [PATCH 0840/1312] Integrate review suggestions --- src/analyses/base.ml | 8 +- src/cdomains/arrayDomain.ml | 518 +++++++++++++++++------------------ src/cdomains/arrayDomain.mli | 21 +- src/cdomains/valueDomain.ml | 45 +-- 4 files changed, 274 insertions(+), 318 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index d0f9dcc03e..c8c13fe3ef 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2047,7 +2047,7 @@ struct in let address_from_value (v:value) = match v with | Address a -> - let rec lo:'a Offset_intf.t -> 'a Offset_intf.t = function + let rec lo = function | `Index (i, `NoOffset) -> `NoOffset | `NoOffset -> `NoOffset | `Field (f, o) -> `Field (f, lo o) @@ -2191,9 +2191,9 @@ struct if it surely isn't, assign a null_ptr *) string_manipulation haystack needle lv true (Some (fun h_a n_a -> Address (AD.substring_extraction h_a n_a))) (fun h_ar n_ar -> match CArrays.substring_extraction h_ar n_ar with - | true, false -> Address (AD.null_ptr) - | false, true -> Address (eval_lv (Analyses.ask_of_ctx ctx) gs st (mkMem ~addr:(Cil.stripCasts haystack) ~off:NoOffset)) - | _ -> Address (AD.join (eval_lv (Analyses.ask_of_ctx ctx) gs st + | CArrays.IsNotSubstr -> Address (AD.null_ptr) + | CArrays.IsSubstrAtIndex0 -> Address (eval_lv (Analyses.ask_of_ctx ctx) gs st (mkMem ~addr:(Cil.stripCasts haystack) ~off:NoOffset)) + | CArrays.IsMaybeSubstr -> Address (AD.join (eval_lv (Analyses.ask_of_ctx ctx) gs st (mkMem ~addr:(Cil.stripCasts haystack) ~off:(Index (Offset.Index.Exp.any, NoOffset)))) (AD.null_ptr))) | None -> st end diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 4503d3c7fb..a09d15bd23 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -53,7 +53,6 @@ sig val get_vars_in_e: t -> Cil.varinfo list val map: (value -> value) -> t -> t val fold_left: ('a -> value -> 'a) -> 'a -> t -> 'a - val content_to_top: t -> t val smart_join: (exp -> BI.t option) -> (exp -> BI.t option) -> t -> t -> t val smart_widen: (exp -> BI.t option) -> (exp -> BI.t option) -> t -> t -> t val smart_leq: (exp -> BI.t option) -> (exp -> BI.t option) -> t -> t -> bool @@ -76,14 +75,15 @@ sig include S0 type ret = Null | NotNull | Top + type substr = IsNotSubstr | IsSubstrAtIndex0 | IsMaybeSubstr - val get: ?checkBounds:bool -> VDQ.t -> t -> Basetype.CilExp.t option * idx -> ret + val get: VDQ.t -> t -> Basetype.CilExp.t option * idx -> ret val to_null_byte_domain: string -> t val to_string_length: t -> idx val string_copy: t -> t -> int option -> t val string_concat: t -> t -> int option -> t - val substring_extraction: t -> t -> bool * bool + val substring_extraction: t -> t -> substr val string_comparison: t -> t -> int option -> idx end @@ -117,7 +117,7 @@ sig val is_null: t -> bool val is_not_null: t -> bool - val is_int_ikind: t -> Cil.ikind option + val get_ikind: t -> Cil.ikind option val zero_of_ikind: Cil.ikind -> t val not_zero_of_ikind: Cil.ikind -> t end @@ -149,8 +149,6 @@ struct let map f x = f x let fold_left f a x = f a x - let content_to_top x = Val.invalidate_abstract_value x - let printXml f x = BatPrintf.fprintf f "\n\nAny\n%a\n\n\n" Val.printXml x let smart_join _ _ = join let smart_widen _ _ = widen @@ -259,9 +257,6 @@ struct let get_vars_in_e _ = [] let map f (xl, xr) = ((List.map f xl), f xr) let fold_left f a x = f a (join_of_all_parts x) - let content_to_top (xl, xr) = - let invalidated_val _ = Val.invalidate_abstract_value xr in - (List.map invalidated_val xl, invalidated_val xr) let printXml f (xl,xr) = BatPrintf.fprintf f "\n\n unrolled array\n xl\n%a\n\n @@ -354,7 +349,6 @@ struct let is_top = function | Joint x -> Val.is_top x | _-> false - let content_to_top x = Joint (Val.invalidate_abstract_value (join_of_all_parts x)) let join (x:t) (y:t) = normalize @@ match x, y with @@ -875,8 +869,6 @@ struct let fold_left f a (x, l) = Base.fold_left f a x let get_vars_in_e _ = [] - let content_to_top (x, l) = (Base.content_to_top x, l) - let smart_join _ _ = join let smart_widen _ _ = widen let smart_leq _ _ = leq @@ -924,8 +916,6 @@ struct let fold_left f a (x, l) = Base.fold_left f a x let get_vars_in_e (x, _) = Base.get_vars_in_e x - let content_to_top (x, l) = (Base.content_to_top x, l) - let smart_join x_eval_int y_eval_int (x,xl) (y,yl) = let l = Idx.join xl yl in (Base.smart_join_with_length (Some l) x_eval_int y_eval_int x y , l) @@ -978,8 +968,6 @@ struct let fold_left f a (x, l) = Base.fold_left f a x let get_vars_in_e _ = [] - let content_to_top (x, l) = (Base.content_to_top x, l) - let smart_join _ _ = join let smart_widen _ _ = widen let smart_leq _ _ = leq @@ -1003,87 +991,87 @@ struct let to_yojson (x, y) = `Assoc [ (Base.name (), Base.to_yojson x); ("length", Idx.to_yojson y) ] end -module HelperFunctionsIndexMustMaySets = +module NullByte (Val: LatticeWithNull) (Idx: IntDomain.Z): Str with type value = Val.t and type idx = Idx.t = struct - module MustSet = SetDomain.Reverse (SetDomain.ToppedSet (IntDomain.BigInt) (struct let topname = "All indexes" end)) - module MaySet = SetDomain.ToppedSet (IntDomain.BigInt) (struct let topname = "All indexes" end) + module MustSet = struct + module M = SetDomain.Reverse (SetDomain.ToppedSet (IntDomain.BigInt) (struct let topname = "All Null" end)) + include M - let compute_set len = - List.init (Z.to_int len) (Fun.id) - |> List.map Z.of_int - |> MustSet.of_list + let compute_set len = + List.init (Z.to_int len) Z.of_int + |> of_list - let must_nulls_remove i must_nulls_set min_size = - if MustSet.is_bot must_nulls_set then - MustSet.remove i (compute_set min_size) - else - MustSet.remove i must_nulls_set + let remove i must_nulls_set min_size = + if M.is_bot must_nulls_set then + M.remove i (compute_set min_size) + else + M.remove i must_nulls_set - let must_nulls_filter cond must_nulls_set min_size = - if MustSet.is_bot must_nulls_set then - MustSet.filter cond (compute_set min_size) - else - MustSet.filter cond must_nulls_set + let filter cond must_nulls_set min_size = + if M.is_bot must_nulls_set then + M.filter cond (compute_set min_size) + else + M.filter cond must_nulls_set - let must_nulls_min_elt must_nulls_set = - if MustSet.is_bot must_nulls_set then - Z.zero - else - MustSet.min_elt must_nulls_set + let min_elt must_nulls_set = + if M.is_bot must_nulls_set then + Z.zero + else + M.min_elt must_nulls_set + end - let may_nulls_remove i may_nulls_set max_size = - if MaySet.is_top may_nulls_set then - MaySet.remove i (compute_set max_size) - else - MaySet.remove i may_nulls_set + module MaySet = struct + module M = SetDomain.ToppedSet (IntDomain.BigInt) (struct let topname = "All Null" end) + include M - let may_nulls_filter cond may_nulls_set max_size = - if MaySet.is_top may_nulls_set then - MaySet.filter cond (compute_set max_size) - else - MaySet.filter cond may_nulls_set + let remove i may_nulls_set max_size = + if M.is_top may_nulls_set then + M.remove i (MustSet.compute_set max_size) + else + M.remove i may_nulls_set - let may_nulls_min_elt may_nulls_set = - if MaySet.is_top may_nulls_set then - Z.zero - else - MaySet.min_elt may_nulls_set -end + let filter cond may_nulls_set max_size = + if M.is_top may_nulls_set then + M.filter cond (MustSet.compute_set max_size) + else + M.filter cond may_nulls_set -module NullByte (Val: LatticeWithNull) (Idx: IntDomain.Z): Str with type value = Val.t and type idx = Idx.t = -struct - module MustNulls = SetDomain.Reverse (SetDomain.ToppedSet (IntDomain.BigInt) (struct let topname = "All Null" end)) - module MayNulls = SetDomain.ToppedSet (IntDomain.BigInt) (struct let topname = "All Null" end) - (* (Must Null Set, May Null Set, Array Size) *) - include Lattice.Prod3 (MustNulls) (MayNulls) (Idx) + let min_elt may_nulls_set = + if M.is_top may_nulls_set then + Z.zero + else + M.min_elt may_nulls_set + end - include HelperFunctionsIndexMustMaySets + (* (Must Null Set, May Null Set, Array Size) *) + include Lattice.Prod3 (MustSet) (MaySet) (Idx) let name () = "arrays containing null bytes" type idx = Idx.t type value = Val.t type ret = Null | NotNull | Top + type substr = IsNotSubstr | IsSubstrAtIndex0 | IsMaybeSubstr (* helper: returns Idx.maximal except for Overflows that are mapped to None *) let idx_maximal i = match Idx.maximal i with - | Some i -> (try Some (Z.of_int (Z.to_int i)) with Z.Overflow -> None) - | None -> None + | Some i when Z.fits_int i -> Some i + | _ -> None - let get ?(checkBounds=true) (ask: VDQ.t) (must_nulls_set, may_nulls_set, size) (e, i) = + let get (ask: VDQ.t) (must_nulls_set, may_nulls_set, size) (e, i) = let all_indexes_must_null i max = - let rec check_all_indexes i = - if Z.gt i max then - true - else if MustNulls.mem i must_nulls_set then - check_all_indexes (Z.succ i) - else - false in - if MustNulls.is_bot must_nulls_set then + if MustSet.is_bot must_nulls_set then true - else if Z.lt (Z.of_int (MustNulls.cardinal must_nulls_set)) (Z.sub max i) then + else if Z.lt (Z.of_int (MustSet.cardinal must_nulls_set)) (Z.sub max i) then false else + let rec check_all_indexes i = + if Z.gt i max then + true + else if MustSet.mem i must_nulls_set then + check_all_indexes (Z.succ i) + else + false in check_all_indexes i in let min interval = match Idx.minimal interval with | Some min_num when Z.geq min_num Z.zero -> min_num @@ -1097,7 +1085,7 @@ struct (* if there is no maximum value in index interval *) | None, _ -> (* ... return NotNull if no i >= min_i in may_nulls_set *) - if not (MayNulls.exists (Z.leq min_i) may_nulls_set) then + if not (MaySet.exists (Z.leq min_i) may_nulls_set) then NotNull (* ... else return Top *) else @@ -1108,7 +1096,7 @@ struct if Z.lt max_i min_size && all_indexes_must_null min_i max_i then Null (* ... return NotNull if no number in index interval is in may_nulls_set *) - else if not (MayNulls.exists (fun x -> Z.geq x min_i && Z.leq x max_i) may_nulls_set) then + else if not (MaySet.exists (fun x -> Z.geq x min_i && Z.leq x max_i) may_nulls_set) then NotNull else Top @@ -1117,7 +1105,7 @@ struct if Z.lt max_i min_size && all_indexes_must_null min_i max_i then Null (* if maximum value in index interval < maximal size, return NotNull if no number in index interval is in may_nulls_set *) - else if Z.lt max_i max_size && not (MayNulls.exists (fun x -> Z.geq x min_i && Z.leq x max_i) may_nulls_set) then + else if Z.lt max_i max_size && not (MaySet.exists (fun x -> Z.geq x min_i && Z.leq x max_i) may_nulls_set) then NotNull else Top @@ -1129,7 +1117,7 @@ struct if Z.gt i max then may_nulls_set else - add_indexes (Z.succ i) max (MayNulls.add i may_nulls_set) in + add_indexes (Z.succ i) max (MaySet.add i may_nulls_set) in let min interval = match Idx.minimal interval with | Some min_num when Z.geq min_num Z.zero -> min_num | _ -> Z.zero in (* assume worst case minimal natural number *) @@ -1143,32 +1131,32 @@ struct (* if size has no upper limit *) | None -> (* ... and value <> null, remove i from must_nulls_set and also from may_nulls_set if not top *) - if Val.is_not_null v && not (MayNulls.is_top may_nulls_set) then - (must_nulls_remove i must_nulls_set min_size, MayNulls.remove i may_nulls_set, size) + if Val.is_not_null v && not (MaySet.is_top may_nulls_set) then + (MustSet.remove i must_nulls_set min_size, MaySet.M.remove i may_nulls_set, size) else if Val.is_not_null v then - (must_nulls_remove i must_nulls_set min_size, may_nulls_set, size) + (MustSet.remove i must_nulls_set min_size, may_nulls_set, size) (* ..., i < minimal size and value = null, add i to must_nulls_set and may_nulls_set *) else if Z.lt i min_size && Val.is_null v then - (MustNulls.add i must_nulls_set, MayNulls.add i may_nulls_set, size) + (MustSet.add i must_nulls_set, MaySet.add i may_nulls_set, size) (* ..., i >= minimal size and value = null, add i only to may_nulls_set *) else if Val.is_null v then - (must_nulls_set, MayNulls.add i may_nulls_set, size) + (must_nulls_set, MaySet.add i may_nulls_set, size) (* ... and value unknown, remove i from must_nulls_set and add it to may_nulls_set *) else - (must_nulls_remove i must_nulls_set min_size, MayNulls.add i may_nulls_set, size) + (MustSet.remove i must_nulls_set min_size, MaySet.add i may_nulls_set, size) | Some max_size -> (* if value <> null, remove i from must_nulls_set and may_nulls_set *) if Val.is_not_null v then - (must_nulls_remove i must_nulls_set min_size, may_nulls_remove i may_nulls_set max_size, size) + (MustSet.remove i must_nulls_set min_size, MaySet.remove i may_nulls_set max_size, size) (* if i < minimal size and value = null, add i to must_nulls_set and may_nulls_set *) else if Z.lt i min_size && Val.is_null v then - (MustNulls.add i must_nulls_set, MayNulls.add i may_nulls_set, size) + (MustSet.add i must_nulls_set, MaySet.add i may_nulls_set, size) (* if minimal size <= i < maximal size and value = null, add i only to may_nulls_set *) else if Z.lt i max_size && Val.is_null v then - (must_nulls_set, MayNulls.add i may_nulls_set, size) + (must_nulls_set, MaySet.add i may_nulls_set, size) (* if i < maximal size and value unknown, remove i from must_nulls_set and add it to may_nulls_set *) else if Z.lt i max_size then - (must_nulls_remove i must_nulls_set min_size, MayNulls.add i may_nulls_set, size) + (MustSet.remove i must_nulls_set min_size, MaySet.add i may_nulls_set, size) (* if i >= maximal size, return tuple unmodified *) else (must_nulls_set, may_nulls_set, size) in @@ -1179,9 +1167,9 @@ struct must_nulls_set (* if value <> null or unknown, only keep indexes must_i < minimal index and must_i > maximal index *) else if Z.equal min_i Z.zero && Z.geq max_i min_size then - MustNulls.top () + MustSet.top () else - must_nulls_filter (fun x -> (Z.lt x min_i || Z.gt x max_i) && Z.lt x min_size) must_nulls_set min_size in + MustSet.filter (fun x -> (Z.lt x min_i || Z.gt x max_i) && Z.lt x min_size) must_nulls_set min_size in let set_interval_may min_i max_i = (* if value <> null, return may_nulls_set unmodified as not clear which index is set to value *) @@ -1195,7 +1183,7 @@ struct | Some max_size -> (* ... add all indexes < maximal size to may_nulls_set *) if Z.equal min_i Z.zero && Z.geq max_i max_size then - MayNulls.top () + MaySet.top () else if Z.geq max_i max_size then add_indexes min_i (Z.pred max_size) may_nulls_set else @@ -1210,23 +1198,23 @@ struct (if Val.is_null v && idx_maximal size = None then match idx_maximal size with (* ... and there is no maximal size, modify may_nulls_set to top *) - | None -> (must_nulls_set, MayNulls.top (), size) + | None -> (must_nulls_set, MaySet.top (), size) (* ... and there is a maximal size, add all i from minimal index to maximal size to may_nulls_set *) | Some max_size -> (must_nulls_set, add_indexes min_i (Z.pred max_size) may_nulls_set, size) (* ... and value <> null, only keep indexes < minimal index in must_nulls_set *) else if Val.is_not_null v then - (must_nulls_filter (Z.gt min_i) must_nulls_set min_size, may_nulls_set, size) + (MustSet.filter (Z.gt min_i) must_nulls_set min_size, may_nulls_set, size) (*..., value unknown *) else match Idx.minimal size, idx_maximal size with (* ... and size unknown, modify both sets to top *) - | None, None -> (MustNulls.top (), MayNulls.top (), size) + | None, None -> (MustSet.top (), MaySet.top (), size) (* ... and only minimal size known, remove all indexes < minimal size from must_nulls_set and modify may_nulls_set to top *) - | Some min_size, None -> (must_nulls_filter (Z.gt min_size) must_nulls_set min_size, MayNulls.top (), size) + | Some min_size, None -> (MustSet.filter (Z.gt min_size) must_nulls_set min_size, MaySet.top (), size) (* ... and only maximal size known, modify must_nulls_set to top and add all i from minimal index to maximal size to may_nulls_set *) - | None, Some max_size -> (MustNulls.top (), add_indexes min_i (Z.pred max_size) may_nulls_set, size) + | None, Some max_size -> (MustSet.top (), add_indexes min_i (Z.pred max_size) may_nulls_set, size) (* ... and size is known, remove all indexes < minimal size from must_nulls_set and add all i from minimal index to maximal size to may_nulls_set *) - | Some min_size, Some max_size -> (must_nulls_filter (Z.gt min_size) must_nulls_set min_size, add_indexes min_i (Z.pred max_size) may_nulls_set, size)) + | Some min_size, Some max_size -> (MustSet.filter (Z.gt min_size) must_nulls_set min_size, add_indexes min_i (Z.pred max_size) may_nulls_set, size)) | Some max_i when Z.geq max_i Z.zero -> if Z.equal min_i max_i then set_exact min_i @@ -1261,14 +1249,14 @@ struct | None, None -> Z.zero, None in match max_i, Val.is_null v, Val.is_not_null v with (* if value = null, return (bot = all indexes up to minimal size - 1, top = all indexes up to maximal size - 1, size) *) - | Some max_i, true, _ -> (MustNulls.bot (), MayNulls.top (), Idx.of_interval ILong (min_i, max_i)) - | None, true, _ -> (MustNulls.bot (), MayNulls.top (), Idx.starting ILong min_i) + | Some max_i, true, _ -> (MustSet.bot (), MaySet.top (), Idx.of_interval ILong (min_i, max_i)) + | None, true, _ -> (MustSet.bot (), MaySet.top (), Idx.starting ILong min_i) (* if value <> null, return (top = no indexes, bot = no indexes, size) *) - | Some max_i, false, true -> (MustNulls.top (), MayNulls.bot (), Idx.of_interval ILong (min_i, max_i)) - | None, false, true -> (MustNulls.top (), MayNulls.bot (), Idx.starting ILong min_i) + | Some max_i, false, true -> (MustSet.top (), MaySet.bot (), Idx.of_interval ILong (min_i, max_i)) + | None, false, true -> (MustSet.top (), MaySet.bot (), Idx.starting ILong min_i) (* if value unknown, return (top = no indexes, top = all indexes up to maximal size - 1, size) *) - | Some max_i, false, false -> (MustNulls.top (), MayNulls.top (), Idx.of_interval ILong (min_i, max_i)) - | None, false, false -> (MustNulls.top (), MayNulls.top (), Idx.starting ILong min_i) + | Some max_i, false, false -> (MustSet.top (), MaySet.top (), Idx.of_interval ILong (min_i, max_i)) + | None, false, false -> (MustSet.top (), MaySet.top (), Idx.starting ILong min_i) let length (_, _, size) = Some size @@ -1280,15 +1268,13 @@ struct (* if f(null) = null, all values in must_nulls_set still are surely null; * assume top for may_nulls_set as checking effect of f for every possible value is unfeasbile *) if Val.is_null (f (Val.null ())) then - (must_nulls_set, MayNulls.top (), size) + (must_nulls_set, MaySet.top (), size) (* else also return top for must_nulls_set *) else - (MustNulls.top (), MayNulls.top (), size) + (MustSet.top (), MaySet.top (), size) let fold_left f acc _ = f acc (Val.top ()) - let content_to_top (_, _, size) = (MustNulls.top (), MayNulls.top (), size) - let smart_join _ _ = join let smart_widen _ _ = widen let smart_leq _ _ = leq @@ -1299,43 +1285,43 @@ struct let last_null = Z.of_int (String.length s) in let rec build_set i set = if Z.geq (Z.of_int i) last_null then - MayNulls.add last_null set + MaySet.add last_null set else match String.index_from_opt s i '\x00' with - | Some i -> build_set (i + 1) (MayNulls.add (Z.of_int i) set) - | None -> MayNulls.add last_null set in - let set = build_set 0 (MayNulls.empty ()) in + | Some i -> build_set (i + 1) (MaySet.add (Z.of_int i) set) + | None -> MaySet.add last_null set in + let set = build_set 0 (MaySet.empty ()) in (set, set, Idx.of_int ILong (Z.succ last_null)) (** Returns an abstract value with at most one null byte marking the end of the string *) let to_string (must_nulls_set, may_nulls_set, size) = (* if must_nulls_set and min_nulls_set empty, definitely no null byte in array => warn about certain buffer overflow and return tuple unchanged *) - if MustNulls.is_empty must_nulls_set && MayNulls.is_empty may_nulls_set then + if MustSet.is_empty must_nulls_set && MaySet.is_empty may_nulls_set then (M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array access past end: buffer overflow"; (must_nulls_set, may_nulls_set, size)) (* if only must_nulls_set empty, no certainty about array containing null byte => warn about potential buffer overflow and return tuple unchanged *) - else if MustNulls.is_empty must_nulls_set then + else if MustSet.is_empty must_nulls_set then (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "May access array past end: potential buffer overflow"; (must_nulls_set, may_nulls_set, size)) else - let min_must_null = must_nulls_min_elt must_nulls_set in + let min_must_null = MustSet.min_elt must_nulls_set in (* if smallest index in sets coincides, only this null byte is kept in both sets *) - if Z.equal min_must_null (may_nulls_min_elt may_nulls_set) then - (MustNulls.singleton min_must_null, MayNulls.singleton min_must_null, Idx.of_int ILong (Z.succ min_must_null)) + if Z.equal min_must_null (MaySet.min_elt may_nulls_set) then + (MustSet.singleton min_must_null, MaySet.singleton min_must_null, Idx.of_int ILong (Z.succ min_must_null)) (* else return empty must_nulls_set and keep every index up to smallest index of must_nulls_set included in may_nulls_set *) else match idx_maximal size with - | Some max_size -> (MustNulls.empty (), may_nulls_filter (Z.geq min_must_null) may_nulls_set max_size, Idx.of_int ILong (Z.succ min_must_null)) + | Some max_size -> (MustSet.empty (), MaySet.filter (Z.geq min_must_null) may_nulls_set max_size, Idx.of_int ILong (Z.succ min_must_null)) | None -> - if MayNulls.is_top may_nulls_set then + if MaySet.is_top may_nulls_set then let rec add_indexes acc i = if Z.gt i min_must_null then acc else - add_indexes (MayNulls.add i acc) (Z.succ i) in - (MustNulls.empty (), add_indexes (MayNulls.empty ()) Z.zero, Idx.of_int ILong (Z.succ min_must_null)) + add_indexes (MaySet.add i acc) (Z.succ i) in + (MustSet.empty (), add_indexes (MaySet.empty ()) Z.zero, Idx.of_int ILong (Z.succ min_must_null)) else - (MustNulls.empty (), MayNulls.filter (Z.geq min_must_null) may_nulls_set, Idx.of_int ILong (Z.succ min_must_null)) + (MustSet.empty (), MaySet.M.filter (Z.geq min_must_null) may_nulls_set, Idx.of_int ILong (Z.succ min_must_null)) (** [to_n_string index_set n] returns an abstract value with a potential null byte * marking the end of the string and if needed followed by further null bytes to obtain @@ -1345,21 +1331,21 @@ struct if Z.geq i max then set else - add_indexes (Z.succ i) max (MayNulls.add i set) in + add_indexes (Z.succ i) max (MaySet.add i set) in let update_must_indexes min_must_null must_nulls_set = if Z.equal min_must_null Z.zero then - MustNulls.bot () + MustSet.bot () else (* if strlen < n, every byte starting from min_must_null is surely also transformed to null *) add_indexes min_must_null (Z.of_int n) must_nulls_set - |> MustNulls.filter (Z.gt (Z.of_int n)) in + |> MustSet.M.filter (Z.gt (Z.of_int n)) in let update_may_indexes min_may_null may_nulls_set = if Z.equal min_may_null Z.zero then - MayNulls.top () + MaySet.top () else (* if minimal strlen < n, every byte starting from minimal may null index may be transformed to null *) add_indexes min_may_null (Z.of_int n) may_nulls_set - |> MayNulls.filter (Z.gt (Z.of_int n)) in + |> MaySet.M.filter (Z.gt (Z.of_int n)) in let warn_no_null min_must_null exists_min_must_null min_may_null = if Z.geq min_may_null (Z.of_int n) then M.warn "Resulting string might not be null-terminated because src doesn't contain a null byte in the first n bytes" @@ -1367,7 +1353,7 @@ struct M.warn "Resulting string might not be null-terminated because src might not contain a null byte in the first n bytes" in if n < 0 then - (MustNulls.top (), MayNulls.top (), Idx.top_of ILong) + (MustSet.top (), MaySet.top (), Idx.top_of ILong) else ((match Idx.minimal size, idx_maximal size with | Some min_size, Some max_size -> @@ -1384,7 +1370,7 @@ struct | None, None -> ()); (* if definitely no null byte in array, i.e. must_nulls_set = may_nulls_set = empty set *) - if MustNulls.is_empty must_nulls_set && MayNulls.is_empty may_nulls_set then + if MustSet.is_empty must_nulls_set && MaySet.is_empty may_nulls_set then (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Resulting string might not be null-terminated because src doesn't contain a null byte"; match idx_maximal size with @@ -1393,35 +1379,35 @@ struct | _ -> (must_nulls_set, may_nulls_set, Idx.of_int ILong (Z.of_int n))) (* if only must_nulls_set empty, remove indexes >= n from may_nulls_set and add all indexes from minimal may null index to n - 1; * warn as in any case, resulting array not guaranteed to contain null byte *) - else if MustNulls.is_empty must_nulls_set then - let min_may_null = may_nulls_min_elt may_nulls_set in + else if MustSet.is_empty must_nulls_set then + let min_may_null = MaySet.min_elt may_nulls_set in warn_no_null Z.zero false min_may_null; (must_nulls_set, update_may_indexes min_may_null may_nulls_set, Idx.of_int ILong (Z.of_int n)) else - let min_must_null = must_nulls_min_elt must_nulls_set in - let min_may_null = may_nulls_min_elt may_nulls_set in + let min_must_null = MustSet.min_elt must_nulls_set in + let min_may_null = MaySet.min_elt may_nulls_set in (* warn if resulting array may not contain null byte *) warn_no_null min_must_null true min_may_null; (* if min_must_null = min_may_null, remove indexes >= n and add all indexes from minimal must/may null to n - 1 in the sets *) if Z.equal min_must_null min_may_null then (update_must_indexes min_must_null must_nulls_set, update_may_indexes min_may_null may_nulls_set, Idx.of_int ILong (Z.of_int n)) else - (MustNulls.top (), update_may_indexes min_may_null may_nulls_set, Idx.of_int ILong (Z.of_int n))) + (MustSet.top (), update_may_indexes min_may_null may_nulls_set, Idx.of_int ILong (Z.of_int n))) let to_string_length (must_nulls_set, may_nulls_set, size) = (* if must_nulls_set and min_nulls_set empty, definitely no null byte in array => return interval [size, inf) and warn *) - if MustNulls.is_empty must_nulls_set && MayNulls.is_empty may_nulls_set then + if MustSet.is_empty must_nulls_set && MaySet.is_empty may_nulls_set then (M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array doesn't contain a null byte: buffer overflow"; match Idx.minimal size with | Some min_size -> Idx.starting !Cil.kindOfSizeOf min_size | None -> Idx.starting !Cil.kindOfSizeOf Z.zero) (* if only must_nulls_set empty, no guarantee that null ever encountered in array => return interval [minimal may null, inf) and *) - else if MustNulls.is_empty must_nulls_set then + else if MustSet.is_empty must_nulls_set then (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array might not contain a null byte: potential buffer overflow"; - Idx.starting !Cil.kindOfSizeOf (may_nulls_min_elt may_nulls_set)) + Idx.starting !Cil.kindOfSizeOf (MaySet.min_elt may_nulls_set)) (* else return interval [minimal may null, minimal must null] *) else - Idx.of_interval !Cil.kindOfSizeOf (may_nulls_min_elt may_nulls_set, must_nulls_min_elt must_nulls_set) + Idx.of_interval !Cil.kindOfSizeOf (MaySet.min_elt may_nulls_set, MustSet.min_elt must_nulls_set) let string_copy (must_nulls_set1, may_nulls_set1, size1) (must_nulls_set2, may_nulls_set2, size2) n = (* filter out indexes before strlen(src) from dest sets and after strlen(src) from src sets and build union, keep size of dest *) @@ -1437,17 +1423,17 @@ struct | Some min_size2 -> min_size2 | None -> Z.zero in (* get must nulls from src string < minimal size of dest *) - must_nulls_filter (Z.gt min_size1) must_nulls_set2' min_size2 + MustSet.filter (Z.gt min_size1) must_nulls_set2' min_size2 (* and keep indexes of dest >= maximal strlen of src *) - |> MustNulls.union (must_nulls_filter (Z.leq max_len2) must_nulls_set1 min_size1) in + |> MustSet.union (MustSet.filter (Z.leq max_len2) must_nulls_set1 min_size1) in let may_nulls_set_result = let max_size2 = match idx_maximal size2' with | Some max_size2 -> max_size2 | None -> max_size1 in (* get may nulls from src string < maximal size of dest *) - may_nulls_filter (Z.gt max_size1) may_nulls_set2' max_size2 + MaySet.filter (Z.gt max_size1) may_nulls_set2' max_size2 (* and keep indexes of dest >= minimal strlen of src *) - |> MayNulls.union (may_nulls_filter (Z.leq min_len2) may_nulls_set1 max_size1) in + |> MaySet.union (MaySet.filter (Z.leq min_len2) may_nulls_set1 max_size1) in (must_nulls_set_result, may_nulls_set_result, size1) | Some min_size1, None, Some min_len2, Some max_len2 -> (if Z.lt min_size1 max_len2 then @@ -1456,12 +1442,12 @@ struct let min_size2 = match Idx.minimal size2' with | Some min_size2 -> min_size2 | None -> Z.zero in - must_nulls_filter (Z.gt min_size1) must_nulls_set2' min_size2 - |> MustNulls.union (must_nulls_filter (Z.leq max_len2) must_nulls_set1 min_size1) in + MustSet.filter (Z.gt min_size1) must_nulls_set2' min_size2 + |> MustSet.union (MustSet.filter (Z.leq max_len2) must_nulls_set1 min_size1) in let may_nulls_set_result = (* get all may nulls from src string as no maximal size of dest *) may_nulls_set2' - |> MayNulls.union (may_nulls_filter (Z.leq min_len2) may_nulls_set1 (Z.succ min_len2)) in + |> MaySet.union (MaySet.filter (Z.leq min_len2) may_nulls_set1 (Z.succ min_len2)) in (must_nulls_set_result, may_nulls_set_result, size1) | Some min_size1, Some max_size1, Some min_len2, None -> (if Z.lt max_size1 min_len2 then @@ -1473,13 +1459,13 @@ struct let min_size2 = match Idx.minimal size2' with | Some min_size2 -> min_size2 | None -> Z.zero in - must_nulls_filter (Z.gt min_size1) must_nulls_set2' min_size2 in + MustSet.filter (Z.gt min_size1) must_nulls_set2' min_size2 in let may_nulls_set_result = let max_size2 = match idx_maximal size2' with | Some max_size2 -> max_size2 | None -> max_size1 in - may_nulls_filter (Z.gt max_size1) may_nulls_set2' max_size2 - |> MayNulls.union (may_nulls_filter (Z.leq min_len2) may_nulls_set1 max_size1) in + MaySet.filter (Z.gt max_size1) may_nulls_set2' max_size2 + |> MaySet.union (MaySet.filter (Z.leq min_len2) may_nulls_set1 max_size1) in (must_nulls_set_result, may_nulls_set_result, size1) | Some min_size1, None, Some min_len2, None -> (if Z.lt min_size1 min_len2 then @@ -1489,36 +1475,36 @@ struct let min_size2 = match Idx.minimal size2' with | Some min_size2 -> min_size2 | None -> Z.zero in - must_nulls_filter (Z.gt min_size1) must_nulls_set2' min_size2 in + MustSet.filter (Z.gt min_size1) must_nulls_set2' min_size2 in let may_nulls_set_result = (* get all may nulls from src string as no maximal size of dest *) may_nulls_set2' - |> MayNulls.union (may_nulls_filter (Z.leq min_len2) may_nulls_set1 (Z.succ min_len2)) in + |> MaySet.union (MaySet.filter (Z.leq min_len2) may_nulls_set1 (Z.succ min_len2)) in (must_nulls_set_result, may_nulls_set_result, size1) (* any other case shouldn't happen as minimal index is always >= 0 *) - | _ -> (MustNulls.top (), MayNulls.top (), size1) in + | _ -> (MustSet.top (), MaySet.top (), size1) in (* warn if size of dest is (potentially) smaller than size of src and the latter (potentially) has no null byte at index < size of dest *) let sizes_warning size2 = (match Idx.minimal size1, idx_maximal size1, Idx.minimal size2, idx_maximal size2 with | Some min_size1, _, Some min_size2, _ when Z.lt min_size1 min_size2 -> - if not (MayNulls.exists (Z.gt min_size1) may_nulls_set2) then + if not (MaySet.exists (Z.gt min_size1) may_nulls_set2) then M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "src doesn't contain a null byte at an index smaller than the size of dest" - else if not (MustNulls.exists (Z.gt min_size1) must_nulls_set2) then + else if not (MustSet.exists (Z.gt min_size1) must_nulls_set2) then M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "src may not contain a null byte at an index smaller than the size of dest" | Some min_size1, _, _, Some max_size2 when Z.lt min_size1 max_size2 -> - if not (MayNulls.exists (Z.gt min_size1) may_nulls_set2) then + if not (MaySet.exists (Z.gt min_size1) may_nulls_set2) then M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "src doesn't contain a null byte at an index smaller than the size of dest" - else if not (MustNulls.exists (Z.gt min_size1) must_nulls_set2) then + else if not (MustSet.exists (Z.gt min_size1) must_nulls_set2) then M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "src may not contain a null byte at an index smaller than the size of dest" | Some min_size1, _, _, None -> - if not (MustNulls.exists (Z.gt min_size1) must_nulls_set2) then + if not (MustSet.exists (Z.gt min_size1) must_nulls_set2) then M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "src may not contain a null byte at an index smaller than the size of dest" | _, Some max_size1, _, Some max_size2 when Z.lt max_size1 max_size2 -> - if not (MustNulls.exists (Z.gt max_size1) must_nulls_set2) then + if not (MustSet.exists (Z.gt max_size1) must_nulls_set2) then M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "src may not contain a null byte at an index smaller than the size of dest" |_, Some max_size1, _, None -> - if not (MustNulls.exists (Z.gt max_size1) must_nulls_set2) then + if not (MustSet.exists (Z.gt max_size1) must_nulls_set2) then M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "src may not contain a null byte at an index smaller than the size of dest" | _ -> ()) in @@ -1534,7 +1520,7 @@ struct sizes_warning (Idx.of_int ILong (Z.of_int n)); let must_nulls_set2', may_nulls_set2', size2' = to_n_string (must_nulls_set2, may_nulls_set2, size2) n in update_sets must_nulls_set2' may_nulls_set2' size2' (Idx.of_int !Cil.kindOfSizeOf (Z.of_int n)) - | _ -> (MustNulls.top (), MayNulls.top (), size1) + | _ -> (MustSet.top (), MaySet.top (), size1) let string_concat (must_nulls_set1, may_nulls_set1, size1) (must_nulls_set2, may_nulls_set2, size2) n = let update_sets min_size1 max_size1 max_size1_exists minlen1 maxlen1 maxlen1_exists minlen2 maxlen2 maxlen2_exists must_nulls_set2' may_nulls_set2' = @@ -1548,70 +1534,70 @@ struct (* if any must_nulls_set empty, result must_nulls_set also empty; * for all i1, i2 in may_nulls_set1, may_nulls_set2: add i1 + i2 if it is <= strlen(dest) + strlen(src) to new may_nulls_set * and keep indexes > minimal strlen(dest) + strlen(src) of may_nulls_set *) - if MustNulls.is_empty must_nulls_set1 || MustNulls.is_empty must_nulls_set2' then + if MustSet.is_empty must_nulls_set1 || MustSet.is_empty must_nulls_set2' then let may_nulls_set_result = if max_size1_exists then - may_nulls_filter (fun x -> if maxlen1_exists && maxlen2_exists then Z.leq x (Z.add maxlen1 maxlen2) else true) may_nulls_set1 max_size1 - |> MayNulls.elements + MaySet.filter (fun x -> if maxlen1_exists && maxlen2_exists then Z.leq x (Z.add maxlen1 maxlen2) else true) may_nulls_set1 max_size1 + |> MaySet.elements (* if may_nulls_set2' is top, limit it to max_size1 *) - |> BatList.cartesian_product (MayNulls.elements (may_nulls_filter (fun x -> true) may_nulls_set2' max_size1)) + |> BatList.cartesian_product (MaySet.elements (MaySet.filter (fun x -> true) may_nulls_set2' max_size1)) |> List.map (fun (i1, i2) -> Z.add i1 i2) - |> MayNulls.of_list - |> MayNulls.union (may_nulls_filter (Z.lt (Z.add minlen1 minlen2)) may_nulls_set1 max_size1) - |> MayNulls.filter (Z.gt max_size1) - else if not (MayNulls.is_top may_nulls_set1) && not (MayNulls.is_top may_nulls_set2') && maxlen1_exists && maxlen2_exists then - MayNulls.filter (fun x -> if maxlen1_exists && maxlen2_exists then Z.leq x (Z.add maxlen1 maxlen2) else true) may_nulls_set1 - |> MayNulls.elements - |> BatList.cartesian_product (MayNulls.elements may_nulls_set2') + |> MaySet.of_list + |> MaySet.union (MaySet.filter (Z.lt (Z.add minlen1 minlen2)) may_nulls_set1 max_size1) + |> MaySet.M.filter (Z.gt max_size1) + else if not (MaySet.is_top may_nulls_set1) && not (MaySet.is_top may_nulls_set2') && maxlen1_exists && maxlen2_exists then + MaySet.M.filter (fun x -> if maxlen1_exists && maxlen2_exists then Z.leq x (Z.add maxlen1 maxlen2) else true) may_nulls_set1 + |> MaySet.elements + |> BatList.cartesian_product (MaySet.elements may_nulls_set2') |> List.map (fun (i1, i2) -> Z.add i1 i2) - |> MayNulls.of_list - |> MayNulls.union (MayNulls.filter (Z.lt (Z.add minlen1 minlen2)) may_nulls_set1) + |> MaySet.of_list + |> MaySet.union (MaySet.M.filter (Z.lt (Z.add minlen1 minlen2)) may_nulls_set1) else - MayNulls.top () in - (MustNulls.top (), may_nulls_set_result, size1) + MaySet.top () in + (MustSet.top (), may_nulls_set_result, size1) (* if minimal must null = minimal may null in ar1 and ar2, add them together and keep indexes > strlen(dest) + strlen(src) of ar1 *) - else if Z.equal (must_nulls_min_elt must_nulls_set1) (may_nulls_min_elt may_nulls_set1) && Z.equal (must_nulls_min_elt must_nulls_set2') (may_nulls_min_elt may_nulls_set2') then - let min_i1 = must_nulls_min_elt must_nulls_set1 in - let min_i2 = must_nulls_min_elt must_nulls_set2' in + else if Z.equal (MustSet.min_elt must_nulls_set1) (MaySet.min_elt may_nulls_set1) && Z.equal (MustSet.min_elt must_nulls_set2') (MaySet.min_elt may_nulls_set2') then + let min_i1 = MustSet.min_elt must_nulls_set1 in + let min_i2 = MustSet.min_elt must_nulls_set2' in let min_i = Z.add min_i1 min_i2 in let must_nulls_set_result = - must_nulls_filter (Z.lt min_i) must_nulls_set1 min_size1 - |> MustNulls.add min_i - |> MustNulls.filter (Z.gt min_size1) in + MustSet.filter (Z.lt min_i) must_nulls_set1 min_size1 + |> MustSet.add min_i + |> MustSet.M.filter (Z.gt min_size1) in let may_nulls_set_result = if max_size1_exists then - may_nulls_filter (Z.lt min_i) may_nulls_set1 max_size1 - |> MayNulls.add min_i - |> MayNulls.filter (fun x -> if max_size1_exists then Z.gt max_size1 x else true) + MaySet.filter (Z.lt min_i) may_nulls_set1 max_size1 + |> MaySet.add min_i + |> MaySet.M.filter (fun x -> if max_size1_exists then Z.gt max_size1 x else true) else - MayNulls.top () in + MaySet.top () in (must_nulls_set_result, may_nulls_set_result, size1) (* else only add all may nulls together <= strlen(dest) + strlen(src) *) else - let min_i2 = must_nulls_min_elt must_nulls_set2' in + let min_i2 = MustSet.min_elt must_nulls_set2' in let may_nulls_set2'_until_min_i2 = match idx_maximal size2 with - | Some max_size2 -> may_nulls_filter (Z.geq min_i2) may_nulls_set2' max_size2 - | None -> may_nulls_filter (Z.geq min_i2) may_nulls_set2' (Z.succ min_i2) in - let must_nulls_set_result = must_nulls_filter (fun x -> if maxlen1_exists && maxlen2_exists then Z.lt (Z.add maxlen1 maxlen2) x else false) must_nulls_set1 min_size1 in + | Some max_size2 -> MaySet.filter (Z.geq min_i2) may_nulls_set2' max_size2 + | None -> MaySet.filter (Z.geq min_i2) may_nulls_set2' (Z.succ min_i2) in + let must_nulls_set_result = MustSet.filter (fun x -> if maxlen1_exists && maxlen2_exists then Z.lt (Z.add maxlen1 maxlen2) x else false) must_nulls_set1 min_size1 in let may_nulls_set_result = if max_size1_exists then - may_nulls_filter (fun x -> if maxlen1_exists && maxlen2_exists then Z.leq x (Z.add maxlen1 maxlen2) else true) may_nulls_set1 max_size1 - |> MayNulls.elements - |> BatList.cartesian_product (MayNulls.elements may_nulls_set2'_until_min_i2) + MaySet.filter (fun x -> if maxlen1_exists && maxlen2_exists then Z.leq x (Z.add maxlen1 maxlen2) else true) may_nulls_set1 max_size1 + |> MaySet.elements + |> BatList.cartesian_product (MaySet.elements may_nulls_set2'_until_min_i2) |> List.map (fun (i1, i2) -> Z.add i1 i2) - |> MayNulls.of_list - |> MayNulls.union (may_nulls_filter (Z.lt (Z.add minlen1 minlen2)) may_nulls_set1 max_size1) - |> MayNulls.filter (fun x -> if max_size1_exists then Z.gt max_size1 x else true) - else if not (MayNulls.is_top may_nulls_set1) then - MayNulls.filter (fun x -> if maxlen1_exists && maxlen2_exists then Z.leq x (Z.add maxlen1 maxlen2) else true) may_nulls_set1 - |> MayNulls.elements - |> BatList.cartesian_product (MayNulls.elements may_nulls_set2'_until_min_i2) + |> MaySet.of_list + |> MaySet.union (MaySet.filter (Z.lt (Z.add minlen1 minlen2)) may_nulls_set1 max_size1) + |> MaySet.M.filter (fun x -> if max_size1_exists then Z.gt max_size1 x else true) + else if not (MaySet.is_top may_nulls_set1) then + MaySet.M.filter (fun x -> if maxlen1_exists && maxlen2_exists then Z.leq x (Z.add maxlen1 maxlen2) else true) may_nulls_set1 + |> MaySet.elements + |> BatList.cartesian_product (MaySet.elements may_nulls_set2'_until_min_i2) |> List.map (fun (i1, i2) -> Z.add i1 i2) - |> MayNulls.of_list - |> MayNulls.union (MayNulls.filter (Z.lt (Z.add minlen1 minlen2)) may_nulls_set1) + |> MaySet.of_list + |> MaySet.union (MaySet.M.filter (Z.lt (Z.add minlen1 minlen2)) may_nulls_set1) else - MayNulls.top () in + MaySet.top () in (must_nulls_set_result, may_nulls_set_result, size1) in let compute_concat must_nulls_set2' may_nulls_set2' = @@ -1637,7 +1623,7 @@ struct update_sets min_size1 Z.zero false minlen1 Z.zero false minlen2 Z.zero false must_nulls_set2' may_nulls_set2' end (* any other case shouldn't happen as minimal index is always >= 0 *) - | _ -> (MustNulls.top (), MayNulls.top (), size1) in + | _ -> (MustSet.top (), MaySet.top (), size1) in match n with (* strcat *) @@ -1649,13 +1635,13 @@ struct (* take at most n bytes from src; if no null byte among them, add null byte at index n *) let must_nulls_set2', may_nulls_set2' = let must_nulls_set2, may_nulls_set2, size2 = to_string (must_nulls_set2, may_nulls_set2, size2) in - if not (MayNulls.exists (Z.gt (Z.of_int n)) may_nulls_set2) then - (MustNulls.singleton (Z.of_int n), MayNulls.singleton (Z.of_int n)) - else if not (MustNulls.exists (Z.gt (Z.of_int n)) must_nulls_set2) then + if not (MaySet.exists (Z.gt (Z.of_int n)) may_nulls_set2) then + (MustSet.singleton (Z.of_int n), MaySet.singleton (Z.of_int n)) + else if not (MustSet.exists (Z.gt (Z.of_int n)) must_nulls_set2) then let max_size2 = match idx_maximal size2 with | Some max_size2 -> max_size2 | None -> Z.succ (Z.of_int n) in - (MustNulls.empty (), MayNulls.add (Z.of_int n) (may_nulls_filter (Z.geq (Z.of_int n)) may_nulls_set2 max_size2)) + (MustSet.empty (), MaySet.add (Z.of_int n) (MaySet.filter (Z.geq (Z.of_int n)) may_nulls_set2 max_size2)) else let min_size2 = match Idx.minimal size2 with | Some min_size2 -> min_size2 @@ -1663,14 +1649,14 @@ struct let max_size2 = match idx_maximal size2 with | Some max_size2 -> max_size2 | None -> Z.of_int n in - (must_nulls_filter (Z.gt (Z.of_int n)) must_nulls_set2 min_size2, may_nulls_filter (Z.gt (Z.of_int n)) may_nulls_set2 max_size2) in + (MustSet.filter (Z.gt (Z.of_int n)) must_nulls_set2 min_size2, MaySet.filter (Z.gt (Z.of_int n)) may_nulls_set2 max_size2) in compute_concat must_nulls_set2' may_nulls_set2' - | _ -> (MustNulls.top (), MayNulls.top (), size1) + | _ -> (MustSet.top (), MaySet.top (), size1) let substring_extraction haystack (must_nulls_set_needle, may_nulls_set_needle, size_needle) = (* if needle is empty string, i.e. certain null byte at index 0, return value of strstr is pointer to haystack *) - if MustNulls.mem Z.zero must_nulls_set_needle then - false, true + if MustSet.mem Z.zero must_nulls_set_needle then + IsSubstrAtIndex0 else let haystack_len = to_string_length haystack in let needle_len = to_string_length (must_nulls_set_needle, may_nulls_set_needle, size_needle) in @@ -1678,29 +1664,29 @@ struct | Some haystack_max, Some needle_min -> (* if strlen(haystack) < strlen(needle), needle can never be substring of haystack => return None *) if Z.lt haystack_max needle_min then - true, false + IsNotSubstr else - false, false - | _ -> false, false + IsMaybeSubstr + | _ -> IsMaybeSubstr let string_comparison (must_nulls_set1, may_nulls_set1, size1) (must_nulls_set2, may_nulls_set2, size2) n = let compare n n_exists = (* if s1 = s2 = empty string, i.e. certain null byte at index 0, or n = 0, return 0 *) - if (MustNulls.mem Z.zero must_nulls_set1 && (MustNulls.mem Z.zero must_nulls_set2)) + if (MustSet.mem Z.zero must_nulls_set1 && (MustSet.mem Z.zero must_nulls_set2)) || (n_exists && Z.equal Z.zero n) then Idx.of_int IInt Z.zero (* if only s1 = empty string, return negative integer *) - else if MustNulls.mem Z.zero must_nulls_set1 && not (MayNulls.mem Z.zero may_nulls_set2) then + else if MustSet.mem Z.zero must_nulls_set1 && not (MaySet.mem Z.zero may_nulls_set2) then Idx.ending IInt Z.minus_one (* if only s2 = empty string, return positive integer *) - else if MustNulls.mem Z.zero must_nulls_set2 then + else if MustSet.mem Z.zero must_nulls_set2 then Idx.starting IInt Z.one else (* if first null bytes are certain, have different indexes and are before index n if n present, return integer <> 0 *) - (try if Z.equal (must_nulls_min_elt must_nulls_set1) (may_nulls_min_elt may_nulls_set1) - && Z.equal (must_nulls_min_elt must_nulls_set2) (may_nulls_min_elt may_nulls_set2) - && (not n_exists || Z.lt (must_nulls_min_elt must_nulls_set1) n || Z.lt (must_nulls_min_elt must_nulls_set2) n ) - && not (Z.equal (must_nulls_min_elt must_nulls_set1) (must_nulls_min_elt must_nulls_set2)) then + (try if Z.equal (MustSet.min_elt must_nulls_set1) (MaySet.min_elt may_nulls_set1) + && Z.equal (MustSet.min_elt must_nulls_set2) (MaySet.min_elt may_nulls_set2) + && (not n_exists || Z.lt (MustSet.min_elt must_nulls_set1) n || Z.lt (MustSet.min_elt must_nulls_set2) n ) + && not (Z.equal (MustSet.min_elt must_nulls_set1) (MustSet.min_elt must_nulls_set2)) then Idx.of_excl_list IInt [Z.zero] else Idx.top_of IInt @@ -1710,13 +1696,13 @@ struct (* strcmp *) | None -> (* track any potential buffer overflow and issue warning if needed *) - (if MustNulls.is_empty must_nulls_set1 && MayNulls.is_empty may_nulls_set1 then + (if MustSet.is_empty must_nulls_set1 && MaySet.is_empty may_nulls_set1 then M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array of string 1 doesn't contain a null byte: buffer overflow" - else if MustNulls.is_empty must_nulls_set1 then + else if MustSet.is_empty must_nulls_set1 then M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array of string 1 might not contain a null byte: potential buffer overflow"); - (if MustNulls.is_empty must_nulls_set2 && MayNulls.is_empty may_nulls_set2 then + (if MustSet.is_empty must_nulls_set2 && MaySet.is_empty may_nulls_set2 then M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array of string 2 doesn't contain a null byte: buffer overflow" - else if MustNulls.is_empty must_nulls_set2 then + else if MustSet.is_empty must_nulls_set2 then M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array of string 2 might not contain a null byte: potential buffer overflow"); (* compute abstract value for result of strcmp *) compare Z.zero false @@ -1758,7 +1744,7 @@ struct let invariant ~value_invariant ~offset ~lval x = Invariant.none end -module FlagHelperAttributeConfiguredArrayDomain(Val: LatticeWithSmartOps) (Idx:IntDomain.Z):S with type value = Val.t and type idx = Idx.t = +module AttributeConfiguredArrayDomain(Val: LatticeWithSmartOps) (Idx:IntDomain.Z):S with type value = Val.t and type idx = Idx.t = struct module P = PartitionedWithLength(Val)(Idx) module T = TrivialWithLength(Val)(Idx) @@ -1823,8 +1809,6 @@ struct | TrivialDomain -> (None, Some (T.top ()), None) | UnrolledDomain -> (None, None, Some (U.top ())) - let content_to_top x = unop_to_t' P.content_to_top T.content_to_top U.content_to_top x - let make ?(varAttr=[]) ?(typAttr=[]) i v = to_t @@ match get_domain ~varAttr ~typAttr with | PartitionedDomain -> (Some (P.make i v), None, None) | TrivialDomain -> (None, Some (T.make i v), None) @@ -1882,26 +1866,27 @@ struct (U.invariant ~value_invariant ~offset ~lval) end -module AttributeConfiguredArrayDomain (Val: LatticeWithNull) (Idx: IntDomain.Z): StrWithDomain with type value = Val.t and type idx = Idx.t = +module AttributeConfiguredAndNullByteArrayDomain (Val: LatticeWithNull) (Idx: IntDomain.Z): StrWithDomain with type value = Val.t and type idx = Idx.t = struct - module F = FlagHelperAttributeConfiguredArrayDomain (Val) (Idx) + module A = AttributeConfiguredArrayDomain (Val) (Idx) module N = NullByte (Val) (Idx) - include Lattice.Prod (F) (N) + include Lattice.Prod (A) (N) - let name () = "AttributeConfiguredArrayDomain" + let name () = "AttributeConfiguredAndNullByteArrayDomain" type idx = Idx.t type value = Val.t type ret = Null | NotNull | Top + type substr = IsNotSubstr | IsSubstrAtIndex0 | IsMaybeSubstr - let domain_of_t (t_f, _) = F.domain_of_t t_f + let domain_of_t (t_f, _) = A.domain_of_t t_f let get ?(checkBounds=true) (ask: VDQ.t) (t_f, t_n) i = - let f_get = F.get ~checkBounds ask t_f i in + let f_get = A.get ~checkBounds ask t_f i in if get_bool "ana.base.arrays.nullbytes" then - let n_get = N.get ~checkBounds ask t_n i in - match Val.is_int_ikind f_get, n_get with + let n_get = N.get ask t_n i in + match Val.get_ikind f_get, n_get with | Some ik, Null -> Val.meet f_get (Val.zero_of_ikind ik) | Some ik, NotNull -> Val.meet f_get (Val.not_zero_of_ikind ik) | _ -> f_get @@ -1909,55 +1894,49 @@ struct f_get let set (ask:VDQ.t) (t_f, t_n) i v = if get_bool "ana.base.arrays.nullbytes" then - (F.set ask t_f i v, N.set ask t_n i v) + (A.set ask t_f i v, N.set ask t_n i v) else - (F.set ask t_f i v, N.top ()) + (A.set ask t_f i v, N.top ()) let make ?(varAttr=[]) ?(typAttr=[]) i v = if get_bool "ana.base.arrays.nullbytes" then - (F.make ~varAttr ~typAttr i v, N.make i v) + (A.make ~varAttr ~typAttr i v, N.make i v) else - (F.make ~varAttr ~typAttr i v, N.top ()) + (A.make ~varAttr ~typAttr i v, N.top ()) let length (t_f, t_n) = if get_bool "ana.base.arrays.nullbytes" then N.length t_n else - F.length t_f - let move_if_affected ?(replace_with_const=false) (ask:VDQ.t) (t_f, t_n) v f = (F.move_if_affected ~replace_with_const ask t_f v f, N.move_if_affected ~replace_with_const ask t_n v f) - let get_vars_in_e (t_f, _) = F.get_vars_in_e t_f + A.length t_f + let move_if_affected ?(replace_with_const=false) (ask:VDQ.t) (t_f, t_n) v f = (A.move_if_affected ~replace_with_const ask t_f v f, N.move_if_affected ~replace_with_const ask t_n v f) + let get_vars_in_e (t_f, _) = A.get_vars_in_e t_f let map f (t_f, t_n) = if get_bool "ana.base.arrays.nullbytes" then - (F.map f t_f, N.map f t_n) - else - (F.map f t_f, N.top ()) - let fold_left f acc (t_f, _) = F.fold_left f acc t_f - - let content_to_top (t_f, t_n) = - if get_bool "ana.base.arrays.nullbytes" then - (F.content_to_top t_f, N.content_to_top t_n) + (A.map f t_f, N.map f t_n) else - (F.content_to_top t_f, N.top ()) + (A.map f t_f, N.top ()) + let fold_left f acc (t_f, _) = A.fold_left f acc t_f let smart_join x y (t_f1, t_n1) (t_f2, t_n2) = if get_bool "ana.base.arrays.nullbytes" then - (F.smart_join x y t_f1 t_f2, N.smart_join x y t_n1 t_n2) + (A.smart_join x y t_f1 t_f2, N.smart_join x y t_n1 t_n2) else - (F.smart_join x y t_f1 t_f2, N.top ()) + (A.smart_join x y t_f1 t_f2, N.top ()) let smart_widen x y (t_f1, t_n1) (t_f2, t_n2) = if get_bool "ana.base.arrays.nullbytes" then - (F.smart_widen x y t_f1 t_f2, N.smart_widen x y t_n1 t_n2) + (A.smart_widen x y t_f1 t_f2, N.smart_widen x y t_n1 t_n2) else - (F.smart_widen x y t_f1 t_f2, N.top ()) + (A.smart_widen x y t_f1 t_f2, N.top ()) let smart_leq x y (t_f1, t_n1) (t_f2, t_n2) = if get_bool "ana.base.arrays.nullbytes" then - F.smart_leq x y t_f1 t_f2 && N.smart_leq x y t_n1 t_n2 + A.smart_leq x y t_f1 t_f2 && N.smart_leq x y t_n1 t_n2 else - F.smart_leq x y t_f1 t_f2 + A.smart_leq x y t_f1 t_f2 let to_null_byte_domain s = if get_bool "ana.base.arrays.nullbytes" then - (F.make (Idx.top_of ILong) (Val.meet (Val.not_zero_of_ikind IChar) (Val.zero_of_ikind IChar)), N.to_null_byte_domain s) + (A.make (Idx.top_of ILong) (Val.meet (Val.not_zero_of_ikind IChar) (Val.zero_of_ikind IChar)), N.to_null_byte_domain s) else - (F.top (), N.top ()) + (A.top (), N.top ()) let to_string_length (_, t_n) = if get_bool "ana.base.arrays.nullbytes" then N.to_string_length t_n @@ -1965,19 +1944,18 @@ struct Idx.top_of !Cil.kindOfSizeOf let string_copy (t_f1, t_n1) (_, t_n2) n = if get_bool "ana.base.arrays.nullbytes" then - (F.content_to_top t_f1, N.string_copy t_n1 t_n2 n) + (A.map Val.invalidate_abstract_value t_f1, N.string_copy t_n1 t_n2 n) else - (F.content_to_top t_f1, N.top ()) + (A.map Val.invalidate_abstract_value t_f1, N.top ()) let string_concat (t_f1, t_n1) (_, t_n2) n = if get_bool "ana.base.arrays.nullbytes" then - (F.content_to_top t_f1, N.string_concat t_n1 t_n2 n) - else - (F.content_to_top t_f1, N.top ()) - let substring_extraction (_, t_n1) (_, t_n2) = - if get_bool "ana.base.arrays.nullbytes" then - N.substring_extraction t_n1 t_n2 + (A.map Val.invalidate_abstract_value t_f1, N.string_concat t_n1 t_n2 n) else - false, false + (A.map Val.invalidate_abstract_value t_f1, N.top ()) + let substring_extraction (_, t_n1) (_, t_n2) = match N.substring_extraction t_n1 t_n2 with + | IsNotSubstr when get_bool "ana.base.arrays.nullbytes" -> IsNotSubstr + | IsSubstrAtIndex0 when get_bool "ana.base.arrays.nullbytes" -> IsSubstrAtIndex0 + | _ -> IsMaybeSubstr let string_comparison (_, t_n1) (_, t_n2) n = if get_bool "ana.base.arrays.nullbytes" then N.string_comparison t_n1 t_n2 n @@ -1986,9 +1964,9 @@ struct let update_length newl (t_f, t_n) = if get_bool "ana.base.arrays.nullbytes" then - (F.update_length newl t_f, N.update_length newl t_n) + (A.update_length newl t_f, N.update_length newl t_n) else - (F.update_length newl t_f, N.top ()) - let project ?(varAttr=[]) ?(typAttr=[]) ask (t_f, t_n) = (F.project ~varAttr ~typAttr ask t_f, N.project ~varAttr ~typAttr ask t_n) - let invariant ~value_invariant ~offset ~lval (t_f, _) = F.invariant ~value_invariant ~offset ~lval t_f + (A.update_length newl t_f, N.top ()) + let project ?(varAttr=[]) ?(typAttr=[]) ask (t_f, t_n) = (A.project ~varAttr ~typAttr ask t_f, N.project ~varAttr ~typAttr ask t_n) + let invariant ~value_invariant ~offset ~lval (t_f, _) = A.invariant ~value_invariant ~offset ~lval t_f end diff --git a/src/cdomains/arrayDomain.mli b/src/cdomains/arrayDomain.mli index 915dfee470..fef063f765 100644 --- a/src/cdomains/arrayDomain.mli +++ b/src/cdomains/arrayDomain.mli @@ -46,9 +46,6 @@ sig val fold_left: ('a -> value -> 'a) -> 'a -> t -> 'a (** Left fold (like List.fold_left) over the arrays elements *) - val content_to_top: t -> t - (** Maps the array's content to top of value, but keeps the type and the size if known *) - val smart_join: (Cil.exp -> BigIntOps.t option) -> (Cil.exp -> BigIntOps.t option) -> t -> t -> t val smart_widen: (Cil.exp -> BigIntOps.t option) -> (Cil.exp -> BigIntOps.t option) -> t -> t -> t val smart_leq: (Cil.exp -> BigIntOps.t option) -> (Cil.exp -> BigIntOps.t option) -> t -> t -> bool @@ -75,8 +72,9 @@ sig include S0 type ret = Null | NotNull | Top + type substr = IsNotSubstr | IsSubstrAtIndex0 | IsMaybeSubstr - val get: ?checkBounds:bool -> VDQ.t -> t -> Basetype.CilExp.t option * idx -> ret + val get: VDQ.t -> t -> Basetype.CilExp.t option * idx -> ret (* overwrites get of module S *) val to_null_byte_domain: string -> t @@ -94,11 +92,10 @@ sig * concatenation of the input abstract values [s1] and [s2], taking at most [n] bytes of * [s2] if present *) - val substring_extraction: t -> t -> bool * bool - (** [substring_extraction haystack needle] returns [is_null_ptr, is_offset_0], i.e. - * [true, false] if the string represented by the abstract value [needle] surely isn't a - * substring of [haystack], [false, true] if [needle] is the empty string, - * else [false, false] *) + val substring_extraction: t -> t -> substr + (** [substring_extraction haystack needle] returns [IsNotSubstr] if the string represented by + * the abstract value [needle] surely isn't a substring of [haystack], [IsSubstrAtIndex0] if + * [needle] is the empty string, else [Unknown] *) val string_comparison: t -> t -> int option -> idx (** [string_comparison s1 s2 n] returns a negative / positive idx element if the string @@ -137,7 +134,7 @@ sig val is_null: t -> bool val is_not_null: t -> bool - val is_int_ikind: t -> Cil.ikind option + val get_ikind: t -> Cil.ikind option val zero_of_ikind: Cil.ikind -> t val not_zero_of_ikind: Cil.ikind -> t end @@ -170,10 +167,10 @@ module NullByte (Val: LatticeWithNull) (Idx: IntDomain.Z): Str with type value = * for this domain. It additionally tracks the array size. *) -module FlagHelperAttributeConfiguredArrayDomain (Val: LatticeWithSmartOps) (Idx: IntDomain.Z): S with type value = Val.t and type idx = Idx.t +module AttributeConfiguredArrayDomain (Val: LatticeWithSmartOps) (Idx: IntDomain.Z): S with type value = Val.t and type idx = Idx.t (** Switches between PartitionedWithLength, TrivialWithLength and Unroll based on variable, type, and flag. *) -module AttributeConfiguredArrayDomain (Val: LatticeWithNull) (Idx: IntDomain.Z): StrWithDomain with type value = Val.t and type idx = Idx.t +module AttributeConfiguredAndNullByteArrayDomain (Val: LatticeWithNull) (Idx: IntDomain.Z): StrWithDomain with type value = Val.t and type idx = Idx.t (** Like FlagHelperAttributeConfiguredArrayDomain but additionally runs NullByte * in parallel if flag "ana.base.arrays.nullbytes" is set. *) diff --git a/src/cdomains/valueDomain.ml b/src/cdomains/valueDomain.ml index b396f3802c..aa52770475 100644 --- a/src/cdomains/valueDomain.ml +++ b/src/cdomains/valueDomain.ml @@ -43,7 +43,7 @@ sig val is_null: t -> bool val is_not_null: t -> bool - val is_int_ikind: t -> Cil.ikind option + val get_ikind: t -> Cil.ikind option val zero_of_ikind: Cil.ikind -> t val not_zero_of_ikind: Cil.ikind -> t @@ -272,38 +272,19 @@ struct let is_top x = x = Top let top_name = "Unknown" - let null () = Int(ID.of_int IChar Z.zero) + let null () = Int (ID.of_int IChar Z.zero) + let is_null = function - | Int n -> - begin match ID.to_int n with - | Some n -> Z.equal n Z.zero - | None -> false - end + | Int n -> GobOption.exists (Z.equal Z.zero) (ID.to_int n) | _ -> false + let is_not_null = function | Int n -> - begin match ID.minimal n, ID.maximal n with - | Some min, Some max -> - if Z.gt min Z.zero || Z.lt max Z.zero then - true - else - false - | Some min, None -> - if Z.gt min Z.zero then - true - else - false - | None, Some max -> - if Z.lt max Z.zero then - true - else - false - | _ -> false - end - | Address a when AD.may_be_null a -> false + let zero_ik = ID.of_int (ID.ikind n) Z.zero in + ID.to_bool (ID.ne n zero_ik) = Some true | _ -> false (* we don't know anything *) - let is_int_ikind = function + let get_ikind = function | Int n -> Some (ID.ikind n) | _ -> None let zero_of_ikind ik = Int(ID.of_int ik Z.zero) @@ -758,14 +739,14 @@ struct | _, Bot -> Bot (* Leave uninitialized value (from malloc) alone in free to avoid trashing everything. TODO: sound? *) | t , _ -> top_value t - let invalidate_abstract_value = function + let rec invalidate_abstract_value = function | Top -> Top | Int i -> Int (ID.top_of (ID.ikind i)) | Float f -> Float (FD.top_of (FD.get_fkind f)) | Address _ -> Address (AD.top_ptr) - | Struct _ -> Struct (Structs.top ()) - | Union _ -> Union (Unions.top ()) - | Array _ -> Array (CArrays.top ()) + | Struct s -> Struct (Structs.map invalidate_abstract_value s) + | Union u -> Union (Unions.top ()) + | Array a -> Array (CArrays.map invalidate_abstract_value a) | Blob _ -> Blob (Blobs.top ()) | Thread _ -> Thread (Threads.top ()) | JmpBuf _ -> JmpBuf (JmpBufs.top ()) @@ -1291,7 +1272,7 @@ and Structs: StructDomain.S with type field = fieldinfo and type value = Compoun and Unions: UnionDomain.S with type t = UnionDomain.Field.t * Compound.t and type value = Compound.t = UnionDomain.Simple (Compound) -and CArrays: ArrayDomain.StrWithDomain with type value = Compound.t and type idx = ArrIdxDomain.t = ArrayDomain.AttributeConfiguredArrayDomain(Compound)(ArrIdxDomain) +and CArrays: ArrayDomain.StrWithDomain with type value = Compound.t and type idx = ArrIdxDomain.t = ArrayDomain.AttributeConfiguredAndNullByteArrayDomain(Compound)(ArrIdxDomain) and Blobs: Blob with type size = ID.t and type value = Compound.t and type origin = ZeroInit.t = Blob (Compound) (ID) From c407d3dd8282f2b6c128038f21919113f19da244 Mon Sep 17 00:00:00 2001 From: Nathan Schmidt Date: Mon, 9 Oct 2023 19:02:55 +0200 Subject: [PATCH 0841/1312] Added test cases to increase coverage --- tests/regression/73-strings/05-char_arrays.c | 53 ++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/tests/regression/73-strings/05-char_arrays.c b/tests/regression/73-strings/05-char_arrays.c index edb5a2ab57..e5c7596063 100644 --- a/tests/regression/73-strings/05-char_arrays.c +++ b/tests/regression/73-strings/05-char_arrays.c @@ -20,6 +20,9 @@ int main() { example13(); example14(); example15(); + example16(); + example17(); + example18(); return 0; } @@ -328,3 +331,53 @@ example15() { char* s3 = strstr(s1, s2); __goblint_check(s3 == NULL); } + +example16() { + size_t i; + if (rand()) + i = 3; + else + i = 1/0; + + char s[5] = "abab"; + __goblint_check(s[i] != '\0'); // UNKNOWN + + s[4] = 'a'; + __goblint_check(s[i] != '\0'); + + s[4] = '\0'; + s[i] = '\0'; + __goblint_check(s[4] == '\0'); + __goblint_check(s[3] == '\0'); // UNKNOWN + + s[i] = 'a'; + __goblint_check(s[4] == '\0'); // UNKNOWN +} + +example17() { + char s1[20]; + char s2[10]; + strcat(s1, s2); // WARN + __goblint_check(s1[0] == '\0'); // UNKNOWN + __goblint_check(s1[5] == '\0'); // UNKNOWN + __goblint_check(s1[12] == '\0'); // UNKNOWN +} + +example18() { + char s1[20] = "hello"; + char s2[10] = "world"; + + size_t i; + if (rand()) + i = 1; + else + i = 2; + s1[i] = '\0'; + + strcat(s1, s2); + __goblint_check(s1[1] != '\0'); + __goblint_check(s1[6] == '\0'); // UNKNOWN + __goblint_check(s1[7] == '\0'); // UNKNOWN + __goblint_check(s1[8] != '\0'); // UNKNOWN because might still be uninitialized + __goblint_check(s1[10] == '\0'); // UNKNOWN +} From 85ce4b252b47aebc19574938f0db01635c93114c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 10 Oct 2023 14:52:23 +0300 Subject: [PATCH 0842/1312] Add some missing library functions for concrat/sysbench --- src/analyses/libraryFunctions.ml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 03a984fede..b84ddc5ac5 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -389,6 +389,9 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("alphasort", unknown [drop "a" [r]; drop "b" [r]]); ("gmtime_r", unknown [drop "timer" [r]; drop "result" [w]]); ("rand_r", special [drop "seedp" [r; w]] Rand); + ("srandom", unknown [drop "seed" []]); + ("random", special [] Rand); + ("posix_memalign", unknown [drop "memptr" [w]; drop "alignment" []; drop "size" []]); (* TODO: Malloc *) ] (** Pthread functions. *) From b96c010fb5c86b5b238b91668feeb0156f2cba8c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 10 Oct 2023 17:36:58 +0300 Subject: [PATCH 0843/1312] Fix memOutOfBounds indentation --- src/analyses/memOutOfBounds.ml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 7015e6f143..c715a1d2e7 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -397,8 +397,7 @@ struct match desc.special arglist with | Memset { dest; ch; count; } -> check_count ctx f.vname dest count; | Memcpy { dest; src; n = count; } -> check_count ctx f.vname dest count; - | _ -> (); - ctx.local + | _ -> ctx.local let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = List.iter (fun arg -> check_exp_for_oob_access ctx arg) args; From 5cc481148d4e079327e6f395c02e28e99cdaa414 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 10 Oct 2023 17:47:36 +0300 Subject: [PATCH 0844/1312] Fix library function duplicate check indentation (PR #1213) --- src/analyses/libraryFunctions.ml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index dd9360d7b7..0f9c34f957 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -1047,11 +1047,11 @@ let all_library_descs: (string, LibraryDesc.t) Hashtbl.t = let activated_library_descs: (string, LibraryDesc.t) Hashtbl.t ResettableLazy.t = let union = Hashtbl.merge (fun _ desc1 desc2 -> - match desc1, desc2 with - | (Some _ as desc), None - | None, (Some _ as desc) -> desc - | _, _ -> assert false - ) + match desc1, desc2 with + | (Some _ as desc), None + | None, (Some _ as desc) -> desc + | _, _ -> assert false + ) in ResettableLazy.from_fun (fun () -> GobConfig.get_string_list "lib.activated" From e98911df182e6725bed56113613e30e7cd9f7fa1 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 11 Oct 2023 11:53:06 +0300 Subject: [PATCH 0845/1312] Remove unnecessary pin Co-authored-by: Simmo Saan --- goblint.opam.locked | 3 --- 1 file changed, 3 deletions(-) diff --git a/goblint.opam.locked b/goblint.opam.locked index d5af6ea348..2744d2fe92 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -122,9 +122,6 @@ build: [ ] dev-repo: "git+https://github.com/goblint/analyzer.git" available: os-distribution != "alpine" & arch != "arm64" -pin-depends: [ - [ "goblint-cil.2.0.2" "git+https://github.com/goblint/cil.git#c7ffc37ad83216a84d90fdbf427cc02a68ea5331" ] -] conflicts: [ "result" {< "1.5"} ] From 072f99d701ddf09a97c9a7ab0b754be4c63ee138 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 11 Oct 2023 10:57:37 +0200 Subject: [PATCH 0846/1312] Remove commented out code from `enter` in UAF analysis Add TODOs for future improvement there --- src/analyses/useAfterFree.ml | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index 521f17d97f..ef63ab3e91 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -182,18 +182,10 @@ struct let enter ctx (lval:lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = let caller_state = ctx.local in List.iter (fun arg -> warn_exp_might_contain_freed "enter" ctx arg) args; + (* TODO: The 2nd component of the callee state needs to contain only the heap vars from the caller state which are reachable from: *) + (* * Global program variables *) + (* * The callee arguments *) [caller_state, (AllocaVars.empty (), snd caller_state)] - (* if AllocaVars.is_empty (fst caller_state) && HeapVars.is_empty (snd caller_state) then - [caller_state, caller_state] - else ( - let reachable_from_args = List.fold_left (fun ad arg -> Queries.AD.join ad (ctx.ask (ReachableFrom arg))) (Queries.AD.empty ()) args in - if Queries.AD.is_top reachable_from_args || D.is_top caller_state then - [caller_state, caller_state] - else - let reachable_vars = Queries.AD.to_var_may reachable_from_args in - let callee_state = (AllocaVars.empty (), HeapVars.filter (fun var -> List.mem var reachable_vars) (snd caller_state)) in (* TODO: use AD.mem directly *) - [caller_state, callee_state] - ) *) let combine_env ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (callee_local:D.t) (f_ask:Queries.ask) : D.t = let (caller_stack_state, caller_heap_state) = ctx.local in From e339ed17c88a154785a0d70ab4ad1c1c0aa31730 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 11 Oct 2023 11:59:50 +0300 Subject: [PATCH 0847/1312] Remove unnecessary stuff from test case `74/15` Co-authored-by: Simmo Saan --- tests/regression/74-use_after_free/15-juliet-uaf-global-var.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/regression/74-use_after_free/15-juliet-uaf-global-var.c b/tests/regression/74-use_after_free/15-juliet-uaf-global-var.c index 9cb3b2b29a..cc9819950f 100644 --- a/tests/regression/74-use_after_free/15-juliet-uaf-global-var.c +++ b/tests/regression/74-use_after_free/15-juliet-uaf-global-var.c @@ -1,7 +1,5 @@ -//PARAM: --set ana.activated[+] useAfterFree --set ana.activated[+] threadJoins +//PARAM: --set ana.activated[+] useAfterFree #include -#include -#include int *global; From f018ea37bc1e2cfb66237283b44a8400fc5cf161 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 11 Oct 2023 11:36:01 +0200 Subject: [PATCH 0848/1312] Reduce duplication --- src/analyses/memOutOfBounds.ml | 45 ++++++++++++++-------------------- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 68dae1d89a..d52bb1109a 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -165,32 +165,23 @@ struct with IntDomain.ArithmeticOnIntegerBot _ -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () end - let rec cil_offs_to_idx ctx typ offs = - match offs with - | NoOffset -> intdom_of_int 0 - | Field (field, o) -> - let field_as_offset = Field (field, NoOffset) in - let bits_offset, _size = GoblintCil.bitsOffset (TComp (field.fcomp, [])) field_as_offset in - let bytes_offset = intdom_of_int (bits_offset / 8) in - let remaining_offset = cil_offs_to_idx ctx field.ftype o in - begin - try ID.add bytes_offset remaining_offset - with IntDomain.ArithmeticOnIntegerBot _ -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () - end - | Index (x, o) -> - begin try - begin match ctx.ask (Queries.EvalInt x) with - | `Top -> ID.top_of @@ Cilfacade.ptrdiff_ikind () - | `Bot -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () - | `Lifted eval_x -> - let typ_size_in_bytes = size_of_type_in_bytes typ in - let casted_eval_x = ID.cast_to (Cilfacade.ptrdiff_ikind ()) eval_x in - let bytes_offset = ID.mul typ_size_in_bytes casted_eval_x in - let remaining_offset = cil_offs_to_idx ctx typ o in - ID.add bytes_offset remaining_offset - end - with IntDomain.ArithmeticOnIntegerBot _ -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () - end + let cil_offs_to_idx ctx typ offs = + (* TODO: Some duplication with convert_offset in base.ml, unclear how to immediately get more reuse *) + let rec convert_offset (ofs: offset) = + match ofs with + | NoOffset -> `NoOffset + | Field (fld, ofs) -> `Field (fld, convert_offset ofs) + | Index (exp, ofs) when CilType.Exp.equal exp Offset.Index.Exp.any -> (* special offset added by convertToQueryLval *) + `Index (ID.top (), convert_offset ofs) + | Index (exp, ofs) -> + let i = match ctx.ask (Queries.EvalInt exp) with + | `Lifted x -> x + | _ -> ID.top_of @@ Cilfacade.ptrdiff_ikind () + in + `Index (i, convert_offset ofs) + in + PreValueDomain.Offs.to_index (convert_offset offs) + let check_unknown_addr_deref ctx ptr = let may_contain_unknown_addr = @@ -517,4 +508,4 @@ struct end let _ = - MCP.register_analysis (module Spec : MCPSpec) \ No newline at end of file + MCP.register_analysis (module Spec : MCPSpec) From 7ddd47167395781152fe85efa66f57ca74caa477 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 11 Oct 2023 14:12:21 +0300 Subject: [PATCH 0849/1312] Improve MCP.D pretty --- src/analyses/mCPRegistry.ml | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/analyses/mCPRegistry.ml b/src/analyses/mCPRegistry.ml index d1311e0427..8560e5122d 100644 --- a/src/analyses/mCPRegistry.ml +++ b/src/analyses/mCPRegistry.ml @@ -149,20 +149,21 @@ struct let unop_map f x = List.rev @@ unop_fold (fun a n s d -> (n, f s d) :: a) [] x - let pretty () x = - let f a n (module S : Printable.S) x = Pretty.dprintf "%s:%a" (S.name ()) S.pretty (obj x) :: a in - let xs = unop_fold f [] x in - match xs with - | [] -> text "[]" - | x :: [] -> x - | x :: y -> - let rest = List.fold_left (fun p n->p ++ text "," ++ break ++ n) nil y in - text "[" ++ align ++ x ++ rest ++ unalign ++ text "]" + let pretty () xs = + let pretty_one a n (module S: Printable.S) x = + let doc = Pretty.dprintf "%s:%a" (find_spec_name n) S.pretty (obj x) in + match a with + | None -> Some doc + | Some a -> Some (a ++ text "," ++ line ++ doc) + in + let doc = Option.default Pretty.nil (unop_fold pretty_one None xs) in + Pretty.dprintf "[@[%a@]]" Pretty.insert doc let show x = let xs = unop_fold (fun a n (module S : Printable.S) x -> let analysis_name = find_spec_name n in - (analysis_name ^ ":(" ^ S.show (obj x) ^ ")") :: a) [] x + (analysis_name ^ ":(" ^ S.show (obj x) ^ ")") :: a + ) [] x in IO.to_string (List.print ~first:"[" ~last:"]" ~sep:", " String.print) (rev xs) From e2a585999e3f46642f4474aa339cd6567e429448 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 11 Oct 2023 14:12:45 +0300 Subject: [PATCH 0850/1312] Improve MCP.D pretty_diff --- src/analyses/mCPRegistry.ml | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/analyses/mCPRegistry.ml b/src/analyses/mCPRegistry.ml index 8560e5122d..32847bb3ed 100644 --- a/src/analyses/mCPRegistry.ml +++ b/src/analyses/mCPRegistry.ml @@ -370,12 +370,19 @@ struct let top () = map (fun (n,(module S : Lattice.S)) -> (n,repr @@ S.top ())) @@ domain_list () let bot () = map (fun (n,(module S : Lattice.S)) -> (n,repr @@ S.bot ())) @@ domain_list () - let pretty_diff () (x,y) = - let f a n (module S : Lattice.S) x y = - if S.leq (obj x) (obj y) then a - else a ++ S.pretty_diff () (obj x, obj y) ++ text ". " + let pretty_diff () (xs, ys) = + let pretty_one a n (module S: Lattice.S) x y = + if S.leq (obj x) (obj y) then + a + else ( + let doc = Pretty.dprintf "%s:%a" (find_spec_name n) S.pretty_diff (obj x, obj y) in + match a with + | None -> Some doc + | Some a -> Some (a ++ text "," ++ line ++ doc) + ) in - binop_fold f nil x y + let doc = Option.default Pretty.nil (binop_fold pretty_one None xs ys) in + Pretty.dprintf "[@[%a@]]" Pretty.insert doc end module DomVariantLattice0 (DLSpec : DomainListLatticeSpec) From 44f775942ce6d82736fe2150ff7af219dc0c1532 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 11 Oct 2023 14:35:44 +0300 Subject: [PATCH 0851/1312] Improve empty MapDomain pretty --- src/domains/mapDomain.ml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/domains/mapDomain.ml b/src/domains/mapDomain.ml index 6c40ab9792..76dec6f0d2 100644 --- a/src/domains/mapDomain.ml +++ b/src/domains/mapDomain.ml @@ -68,11 +68,14 @@ end module Print (D: Printable.S) (R: Printable.S) (M: Bindings with type key = D.t and type value = R.t) = struct let pretty () map = - let pretty_bindings () = M.fold (fun k v acc -> - acc ++ dprintf "%a ->@? @[%a@]\n" D.pretty k R.pretty v + let doc = M.fold (fun k v acc -> + acc ++ dprintf "%a ->@?@[%a@]\n" D.pretty k R.pretty v ) map nil in - dprintf "@[{\n @[%t@]}@]" pretty_bindings + if doc = Pretty.nil then + text "{}" + else + dprintf "@[{\n @[%a@]}@]" Pretty.insert doc let show map = GobPretty.sprint pretty map From 00b7e623a7a2ddaa36a91cfd21546de33008e10e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 11 Oct 2023 15:00:49 +0300 Subject: [PATCH 0852/1312] Add module names to Prod and Prod3 --- src/domains/printable.ml | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/domains/printable.ml b/src/domains/printable.ml index 1207d35db2..b0755fb730 100644 --- a/src/domains/printable.ml +++ b/src/domains/printable.ml @@ -366,9 +366,17 @@ struct let pretty () (x,y) = if expand_fst || expand_snd then text "(" + ++ text (Base1.name ()) + ++ text ":" + ++ align ++ (if expand_fst then Base1.pretty () x else text (Base1.show x)) + ++ unalign ++ text ", " + ++ text (Base2.name ()) + ++ text ":" + ++ align ++ (if expand_snd then Base2.pretty () y else text (Base2.show y)) + ++ unalign ++ text ")" else text (show (x,y)) @@ -403,12 +411,24 @@ struct "(" ^ !first ^ ", " ^ !second ^ ", " ^ !third ^ ")" let pretty () (x,y,z) = - text "(" ++ - Base1.pretty () x - ++ text ", " ++ - Base2.pretty () y - ++ text ", " ++ - Base3.pretty () z + text "(" + ++ text (Base1.name ()) + ++ text ":" + ++ align + ++ Base1.pretty () x + ++ unalign + ++ text ", " + ++ text (Base2.name ()) + ++ text ":" + ++ align + ++ Base2.pretty () y + ++ unalign + ++ text ", " + ++ text (Base3.name ()) + ++ text ":" + ++ align + ++ Base3.pretty () z + ++ unalign ++ text ")" let printXml f (x,y,z) = From 83fef2cd47fb15d937192392684c4e39d9d136bb Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 11 Oct 2023 15:01:03 +0300 Subject: [PATCH 0853/1312] Add names to mutex analysis domains --- src/analyses/mutexAnalysis.ml | 2 ++ src/cdomains/lockDomain.ml | 1 + 2 files changed, 3 insertions(+) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 5a61976ef5..ee050f55ca 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -30,6 +30,8 @@ struct include MapDomain.MapTop_LiftBot (ValueDomain.Addr) (Count) + let name () = "multiplicity" + let increment v x = let current = find v x in if current = max_count () then diff --git a/src/cdomains/lockDomain.ml b/src/cdomains/lockDomain.ml index 4bc97b34ab..107c1c0692 100644 --- a/src/cdomains/lockDomain.ml +++ b/src/cdomains/lockDomain.ml @@ -37,6 +37,7 @@ struct end include SetDomain.Reverse(SetDomain.ToppedSet (Lock) (struct let topname = "All mutexes" end)) + let name () = "lockset" let may_be_same_offset of1 of2 = (* Only reached with definite of2 and indefinite of1. *) From 151ccb15068bb262a0134ec818fe7ad307615379 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 11 Oct 2023 15:01:17 +0300 Subject: [PATCH 0854/1312] Add names to mallocWrapper analysis domains --- src/analyses/wrapperFunctionAnalysis.ml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/analyses/wrapperFunctionAnalysis.ml b/src/analyses/wrapperFunctionAnalysis.ml index 5c0176df48..e98597a66a 100644 --- a/src/analyses/wrapperFunctionAnalysis.ml +++ b/src/analyses/wrapperFunctionAnalysis.ml @@ -33,11 +33,20 @@ struct Introduce a function for this to keep things consistent. *) let node_for_ctx ctx = ctx.prev_node + module NodeFlatLattice = + struct + include NodeFlatLattice + let name () = "wrapper call" + end + module UniqueCount = UniqueCount (* Map for counting function call node visits up to n (of the current thread). *) module UniqueCallCounter = - MapDomain.MapBot_LiftTop(NodeFlatLattice)(UniqueCount) + struct + include MapDomain.MapBot_LiftTop(NodeFlatLattice)(UniqueCount) + let name () = "unique calls" + end (* Increase counter for given node. If it does not exist yet, create it. *) let add_unique_call counter node = From b0ce3691ec8525711f57889ddb29b44670090d76 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 11 Oct 2023 15:01:24 +0300 Subject: [PATCH 0855/1312] Add names to threadid analysis domains --- src/analyses/threadId.ml | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/analyses/threadId.ml b/src/analyses/threadId.ml index 4acf88a7ef..8144aea507 100644 --- a/src/analyses/threadId.ml +++ b/src/analyses/threadId.ml @@ -29,11 +29,30 @@ module Spec = struct include Analyses.IdentitySpec - module N = Lattice.Flat (VNI) (struct let bot_name = "unknown node" let top_name = "unknown node" end) + module N = + struct + include Lattice.Flat (VNI) (struct let bot_name = "unknown node" let top_name = "unknown node" end) + let name () = "wrapper call" + end module TD = Thread.D + module Created = + struct + module Current = + struct + include TD + let name () = "current function" + end + module Callees = + struct + include TD + let name () = "callees" + end + include Lattice.Prod (Current) (Callees) + let name () = "created" + end (** Uniqueness Counter * TID * (All thread creates of current thread * All thread creates of the current function and its callees) *) - module D = Lattice.Prod3 (N) (ThreadLifted) (Lattice.Prod(TD)(TD)) + module D = Lattice.Prod3 (N) (ThreadLifted) (Created) module C = D module P = IdentityP (D) From 0f70e17d5d13404b83d1caed8b4219471c32776f Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 11 Oct 2023 15:09:09 +0300 Subject: [PATCH 0856/1312] Fix MCP module names --- src/analyses/mCPRegistry.ml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/analyses/mCPRegistry.ml b/src/analyses/mCPRegistry.ml index 32847bb3ed..810da827ff 100644 --- a/src/analyses/mCPRegistry.ml +++ b/src/analyses/mCPRegistry.ml @@ -318,6 +318,7 @@ struct open Obj include DomListPrintable (PrintableOfRepresentativeSpec (DLSpec)) + let name () = "MCP.P" type elt = (int * unknown) list @@ -344,6 +345,7 @@ struct open Obj include DomListPrintable (PrintableOfLatticeSpec (DLSpec)) + let name () = "MCP.D" let binop_fold f a (x:t) (y:t) = GobList.fold_left3 (fun a (n,d) (n',d') (n'',s) -> assert (n = n' && n = n''); f a n s d d') a x y (domain_list ()) From d9afd55a63514ff47f163c34ff07a41ffd48a30c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 11 Oct 2023 15:09:17 +0300 Subject: [PATCH 0857/1312] Add names to region analysis domains --- src/cdomains/regionDomain.ml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/cdomains/regionDomain.ml b/src/cdomains/regionDomain.ml index 143ba086a6..b577e3499f 100644 --- a/src/cdomains/regionDomain.ml +++ b/src/cdomains/regionDomain.ml @@ -9,6 +9,15 @@ module B = Printable.UnitConf (struct let name = "•" end) module VFB = struct include Printable.Either (VF) (B) + let name () = "region" + + let pretty () = function + | `Right () -> Pretty.text "•" + | `Left x -> VF.pretty () x + + let show = function + | `Right () -> "•" + | `Left x -> VF.show x let printXml f = function | `Right () -> @@ -51,6 +60,7 @@ end module RS = struct include PartitionDomain.Set (VFB) + let name () = "regions" let single_vf vf = singleton (VFB.of_vf vf) let single_bullet = singleton (VFB.bullet) let remove_bullet x = remove VFB.bullet x From a2f36fb4179705a812cc3b0f589d5fd0c9649dc9 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 11 Oct 2023 16:46:33 +0200 Subject: [PATCH 0858/1312] Reorganize memsafety regr. tests --- .../{77-mem-oob => 74-invalid_deref}/01-oob-heap-simple.c | 0 .../{74-use_after_free => 74-invalid_deref}/02-conditional-uaf.c | 0 .../{74-use_after_free => 74-invalid_deref}/03-nested-ptr-uaf.c | 0 .../04-function-call-uaf.c | 0 .../{77-mem-oob => 74-invalid_deref}/05-oob-implicit-deref.c | 0 tests/regression/{77-mem-oob => 74-invalid_deref}/06-memset-oob.c | 0 tests/regression/{77-mem-oob => 74-invalid_deref}/07-memcpy-oob.c | 0 .../{77-mem-oob => 74-invalid_deref}/08-memset-memcpy-array.c | 0 .../{74-use_after_free => 74-invalid_deref}/09-juliet-uaf.c | 0 .../{77-mem-oob => 74-invalid_deref}/10-oob-two-loops.c | 0 .../{77-mem-oob => 74-invalid_deref}/11-address-offset-oob.c | 0 .../{77-mem-oob => 74-invalid_deref}/12-memcpy-oob-src.c | 0 .../{77-mem-oob => 74-invalid_deref}/13-mem-oob-packed-struct.c | 0 .../{74-use_after_free => 74-invalid_deref}/14-alloca-uaf.c | 0 .../15-juliet-uaf-global-var.c | 0 .../16-uaf-packed-struct.c | 0 .../17-scopes-no-static.c} | 0 .../01-simple-uaf.c => 74-invalid_deref/18-simple-uaf.c} | 0 .../19-oob-stack-simple.c} | 0 .../20-scopes-global-var.c} | 0 .../{77-mem-oob/03-oob-loop.c => 74-invalid_deref/21-oob-loop.c} | 0 .../03-scopes-static.c => 74-invalid_deref/22-scopes-static.c} | 0 .../23-oob-deref-after-ptr-arith.c} | 0 .../24-uaf-free-in-wrapper-fun.c} | 0 .../06-uaf-struct.c => 74-invalid_deref/25-uaf-struct.c} | 0 .../26-memset-memcpy-addr-offs.c} | 0 .../27-wrapper-funs-uaf.c} | 0 .../28-multi-threaded-uaf.c} | 0 .../29-multi-threaded-uaf-with-joined-thread.c} | 0 .../01-invalid-dealloc-simple.c | 0 .../02-invalid-dealloc-struct.c | 0 .../03-invalid-dealloc-array.c | 0 .../{75-invalid_dealloc => 75-invalid_free}/04-invalid-realloc.c | 0 .../{75-invalid_dealloc => 75-invalid_free}/05-free-at-offset.c | 0 .../06-realloc-at-offset.c | 0 .../07-free-at-struct-offset.c | 0 .../08-itc-no-double-free.c | 0 .../09-juliet-invalid-dealloc-alloca.c | 0 .../10-invalid-dealloc-union.c | 0 .../07-itc-double-free.c => 75-invalid_free/11-itc-double-free.c} | 0 .../12-realloc-at-struct-offset.c} | 0 .../13-juliet-double-free.c} | 0 42 files changed, 0 insertions(+), 0 deletions(-) rename tests/regression/{77-mem-oob => 74-invalid_deref}/01-oob-heap-simple.c (100%) rename tests/regression/{74-use_after_free => 74-invalid_deref}/02-conditional-uaf.c (100%) rename tests/regression/{74-use_after_free => 74-invalid_deref}/03-nested-ptr-uaf.c (100%) rename tests/regression/{74-use_after_free => 74-invalid_deref}/04-function-call-uaf.c (100%) rename tests/regression/{77-mem-oob => 74-invalid_deref}/05-oob-implicit-deref.c (100%) rename tests/regression/{77-mem-oob => 74-invalid_deref}/06-memset-oob.c (100%) rename tests/regression/{77-mem-oob => 74-invalid_deref}/07-memcpy-oob.c (100%) rename tests/regression/{77-mem-oob => 74-invalid_deref}/08-memset-memcpy-array.c (100%) rename tests/regression/{74-use_after_free => 74-invalid_deref}/09-juliet-uaf.c (100%) rename tests/regression/{77-mem-oob => 74-invalid_deref}/10-oob-two-loops.c (100%) rename tests/regression/{77-mem-oob => 74-invalid_deref}/11-address-offset-oob.c (100%) rename tests/regression/{77-mem-oob => 74-invalid_deref}/12-memcpy-oob-src.c (100%) rename tests/regression/{77-mem-oob => 74-invalid_deref}/13-mem-oob-packed-struct.c (100%) rename tests/regression/{74-use_after_free => 74-invalid_deref}/14-alloca-uaf.c (100%) rename tests/regression/{74-use_after_free => 74-invalid_deref}/15-juliet-uaf-global-var.c (100%) rename tests/regression/{74-use_after_free => 74-invalid_deref}/16-uaf-packed-struct.c (100%) rename tests/regression/{78-invalid-deref-scopes/01-scopes-no-static.c => 74-invalid_deref/17-scopes-no-static.c} (100%) rename tests/regression/{74-use_after_free/01-simple-uaf.c => 74-invalid_deref/18-simple-uaf.c} (100%) rename tests/regression/{77-mem-oob/02-oob-stack-simple.c => 74-invalid_deref/19-oob-stack-simple.c} (100%) rename tests/regression/{78-invalid-deref-scopes/02-scopes-global-var.c => 74-invalid_deref/20-scopes-global-var.c} (100%) rename tests/regression/{77-mem-oob/03-oob-loop.c => 74-invalid_deref/21-oob-loop.c} (100%) rename tests/regression/{78-invalid-deref-scopes/03-scopes-static.c => 74-invalid_deref/22-scopes-static.c} (100%) rename tests/regression/{77-mem-oob/04-oob-deref-after-ptr-arith.c => 74-invalid_deref/23-oob-deref-after-ptr-arith.c} (100%) rename tests/regression/{74-use_after_free/05-uaf-free-in-wrapper-fun.c => 74-invalid_deref/24-uaf-free-in-wrapper-fun.c} (100%) rename tests/regression/{74-use_after_free/06-uaf-struct.c => 74-invalid_deref/25-uaf-struct.c} (100%) rename tests/regression/{77-mem-oob/09-memset-memcpy-addr-offs.c => 74-invalid_deref/26-memset-memcpy-addr-offs.c} (100%) rename tests/regression/{74-use_after_free/11-wrapper-funs-uaf.c => 74-invalid_deref/27-wrapper-funs-uaf.c} (100%) rename tests/regression/{74-use_after_free/12-multi-threaded-uaf.c => 74-invalid_deref/28-multi-threaded-uaf.c} (100%) rename tests/regression/{74-use_after_free/13-multi-threaded-uaf-with-joined-thread.c => 74-invalid_deref/29-multi-threaded-uaf-with-joined-thread.c} (100%) rename tests/regression/{75-invalid_dealloc => 75-invalid_free}/01-invalid-dealloc-simple.c (100%) rename tests/regression/{75-invalid_dealloc => 75-invalid_free}/02-invalid-dealloc-struct.c (100%) rename tests/regression/{75-invalid_dealloc => 75-invalid_free}/03-invalid-dealloc-array.c (100%) rename tests/regression/{75-invalid_dealloc => 75-invalid_free}/04-invalid-realloc.c (100%) rename tests/regression/{75-invalid_dealloc => 75-invalid_free}/05-free-at-offset.c (100%) rename tests/regression/{75-invalid_dealloc => 75-invalid_free}/06-realloc-at-offset.c (100%) rename tests/regression/{75-invalid_dealloc => 75-invalid_free}/07-free-at-struct-offset.c (100%) rename tests/regression/{74-use_after_free => 75-invalid_free}/08-itc-no-double-free.c (100%) rename tests/regression/{75-invalid_dealloc => 75-invalid_free}/09-juliet-invalid-dealloc-alloca.c (100%) rename tests/regression/{75-invalid_dealloc => 75-invalid_free}/10-invalid-dealloc-union.c (100%) rename tests/regression/{74-use_after_free/07-itc-double-free.c => 75-invalid_free/11-itc-double-free.c} (100%) rename tests/regression/{75-invalid_dealloc/08-realloc-at-struct-offset.c => 75-invalid_free/12-realloc-at-struct-offset.c} (100%) rename tests/regression/{74-use_after_free/10-juliet-double-free.c => 75-invalid_free/13-juliet-double-free.c} (100%) diff --git a/tests/regression/77-mem-oob/01-oob-heap-simple.c b/tests/regression/74-invalid_deref/01-oob-heap-simple.c similarity index 100% rename from tests/regression/77-mem-oob/01-oob-heap-simple.c rename to tests/regression/74-invalid_deref/01-oob-heap-simple.c diff --git a/tests/regression/74-use_after_free/02-conditional-uaf.c b/tests/regression/74-invalid_deref/02-conditional-uaf.c similarity index 100% rename from tests/regression/74-use_after_free/02-conditional-uaf.c rename to tests/regression/74-invalid_deref/02-conditional-uaf.c diff --git a/tests/regression/74-use_after_free/03-nested-ptr-uaf.c b/tests/regression/74-invalid_deref/03-nested-ptr-uaf.c similarity index 100% rename from tests/regression/74-use_after_free/03-nested-ptr-uaf.c rename to tests/regression/74-invalid_deref/03-nested-ptr-uaf.c diff --git a/tests/regression/74-use_after_free/04-function-call-uaf.c b/tests/regression/74-invalid_deref/04-function-call-uaf.c similarity index 100% rename from tests/regression/74-use_after_free/04-function-call-uaf.c rename to tests/regression/74-invalid_deref/04-function-call-uaf.c diff --git a/tests/regression/77-mem-oob/05-oob-implicit-deref.c b/tests/regression/74-invalid_deref/05-oob-implicit-deref.c similarity index 100% rename from tests/regression/77-mem-oob/05-oob-implicit-deref.c rename to tests/regression/74-invalid_deref/05-oob-implicit-deref.c diff --git a/tests/regression/77-mem-oob/06-memset-oob.c b/tests/regression/74-invalid_deref/06-memset-oob.c similarity index 100% rename from tests/regression/77-mem-oob/06-memset-oob.c rename to tests/regression/74-invalid_deref/06-memset-oob.c diff --git a/tests/regression/77-mem-oob/07-memcpy-oob.c b/tests/regression/74-invalid_deref/07-memcpy-oob.c similarity index 100% rename from tests/regression/77-mem-oob/07-memcpy-oob.c rename to tests/regression/74-invalid_deref/07-memcpy-oob.c diff --git a/tests/regression/77-mem-oob/08-memset-memcpy-array.c b/tests/regression/74-invalid_deref/08-memset-memcpy-array.c similarity index 100% rename from tests/regression/77-mem-oob/08-memset-memcpy-array.c rename to tests/regression/74-invalid_deref/08-memset-memcpy-array.c diff --git a/tests/regression/74-use_after_free/09-juliet-uaf.c b/tests/regression/74-invalid_deref/09-juliet-uaf.c similarity index 100% rename from tests/regression/74-use_after_free/09-juliet-uaf.c rename to tests/regression/74-invalid_deref/09-juliet-uaf.c diff --git a/tests/regression/77-mem-oob/10-oob-two-loops.c b/tests/regression/74-invalid_deref/10-oob-two-loops.c similarity index 100% rename from tests/regression/77-mem-oob/10-oob-two-loops.c rename to tests/regression/74-invalid_deref/10-oob-two-loops.c diff --git a/tests/regression/77-mem-oob/11-address-offset-oob.c b/tests/regression/74-invalid_deref/11-address-offset-oob.c similarity index 100% rename from tests/regression/77-mem-oob/11-address-offset-oob.c rename to tests/regression/74-invalid_deref/11-address-offset-oob.c diff --git a/tests/regression/77-mem-oob/12-memcpy-oob-src.c b/tests/regression/74-invalid_deref/12-memcpy-oob-src.c similarity index 100% rename from tests/regression/77-mem-oob/12-memcpy-oob-src.c rename to tests/regression/74-invalid_deref/12-memcpy-oob-src.c diff --git a/tests/regression/77-mem-oob/13-mem-oob-packed-struct.c b/tests/regression/74-invalid_deref/13-mem-oob-packed-struct.c similarity index 100% rename from tests/regression/77-mem-oob/13-mem-oob-packed-struct.c rename to tests/regression/74-invalid_deref/13-mem-oob-packed-struct.c diff --git a/tests/regression/74-use_after_free/14-alloca-uaf.c b/tests/regression/74-invalid_deref/14-alloca-uaf.c similarity index 100% rename from tests/regression/74-use_after_free/14-alloca-uaf.c rename to tests/regression/74-invalid_deref/14-alloca-uaf.c diff --git a/tests/regression/74-use_after_free/15-juliet-uaf-global-var.c b/tests/regression/74-invalid_deref/15-juliet-uaf-global-var.c similarity index 100% rename from tests/regression/74-use_after_free/15-juliet-uaf-global-var.c rename to tests/regression/74-invalid_deref/15-juliet-uaf-global-var.c diff --git a/tests/regression/74-use_after_free/16-uaf-packed-struct.c b/tests/regression/74-invalid_deref/16-uaf-packed-struct.c similarity index 100% rename from tests/regression/74-use_after_free/16-uaf-packed-struct.c rename to tests/regression/74-invalid_deref/16-uaf-packed-struct.c diff --git a/tests/regression/78-invalid-deref-scopes/01-scopes-no-static.c b/tests/regression/74-invalid_deref/17-scopes-no-static.c similarity index 100% rename from tests/regression/78-invalid-deref-scopes/01-scopes-no-static.c rename to tests/regression/74-invalid_deref/17-scopes-no-static.c diff --git a/tests/regression/74-use_after_free/01-simple-uaf.c b/tests/regression/74-invalid_deref/18-simple-uaf.c similarity index 100% rename from tests/regression/74-use_after_free/01-simple-uaf.c rename to tests/regression/74-invalid_deref/18-simple-uaf.c diff --git a/tests/regression/77-mem-oob/02-oob-stack-simple.c b/tests/regression/74-invalid_deref/19-oob-stack-simple.c similarity index 100% rename from tests/regression/77-mem-oob/02-oob-stack-simple.c rename to tests/regression/74-invalid_deref/19-oob-stack-simple.c diff --git a/tests/regression/78-invalid-deref-scopes/02-scopes-global-var.c b/tests/regression/74-invalid_deref/20-scopes-global-var.c similarity index 100% rename from tests/regression/78-invalid-deref-scopes/02-scopes-global-var.c rename to tests/regression/74-invalid_deref/20-scopes-global-var.c diff --git a/tests/regression/77-mem-oob/03-oob-loop.c b/tests/regression/74-invalid_deref/21-oob-loop.c similarity index 100% rename from tests/regression/77-mem-oob/03-oob-loop.c rename to tests/regression/74-invalid_deref/21-oob-loop.c diff --git a/tests/regression/78-invalid-deref-scopes/03-scopes-static.c b/tests/regression/74-invalid_deref/22-scopes-static.c similarity index 100% rename from tests/regression/78-invalid-deref-scopes/03-scopes-static.c rename to tests/regression/74-invalid_deref/22-scopes-static.c diff --git a/tests/regression/77-mem-oob/04-oob-deref-after-ptr-arith.c b/tests/regression/74-invalid_deref/23-oob-deref-after-ptr-arith.c similarity index 100% rename from tests/regression/77-mem-oob/04-oob-deref-after-ptr-arith.c rename to tests/regression/74-invalid_deref/23-oob-deref-after-ptr-arith.c diff --git a/tests/regression/74-use_after_free/05-uaf-free-in-wrapper-fun.c b/tests/regression/74-invalid_deref/24-uaf-free-in-wrapper-fun.c similarity index 100% rename from tests/regression/74-use_after_free/05-uaf-free-in-wrapper-fun.c rename to tests/regression/74-invalid_deref/24-uaf-free-in-wrapper-fun.c diff --git a/tests/regression/74-use_after_free/06-uaf-struct.c b/tests/regression/74-invalid_deref/25-uaf-struct.c similarity index 100% rename from tests/regression/74-use_after_free/06-uaf-struct.c rename to tests/regression/74-invalid_deref/25-uaf-struct.c diff --git a/tests/regression/77-mem-oob/09-memset-memcpy-addr-offs.c b/tests/regression/74-invalid_deref/26-memset-memcpy-addr-offs.c similarity index 100% rename from tests/regression/77-mem-oob/09-memset-memcpy-addr-offs.c rename to tests/regression/74-invalid_deref/26-memset-memcpy-addr-offs.c diff --git a/tests/regression/74-use_after_free/11-wrapper-funs-uaf.c b/tests/regression/74-invalid_deref/27-wrapper-funs-uaf.c similarity index 100% rename from tests/regression/74-use_after_free/11-wrapper-funs-uaf.c rename to tests/regression/74-invalid_deref/27-wrapper-funs-uaf.c diff --git a/tests/regression/74-use_after_free/12-multi-threaded-uaf.c b/tests/regression/74-invalid_deref/28-multi-threaded-uaf.c similarity index 100% rename from tests/regression/74-use_after_free/12-multi-threaded-uaf.c rename to tests/regression/74-invalid_deref/28-multi-threaded-uaf.c diff --git a/tests/regression/74-use_after_free/13-multi-threaded-uaf-with-joined-thread.c b/tests/regression/74-invalid_deref/29-multi-threaded-uaf-with-joined-thread.c similarity index 100% rename from tests/regression/74-use_after_free/13-multi-threaded-uaf-with-joined-thread.c rename to tests/regression/74-invalid_deref/29-multi-threaded-uaf-with-joined-thread.c diff --git a/tests/regression/75-invalid_dealloc/01-invalid-dealloc-simple.c b/tests/regression/75-invalid_free/01-invalid-dealloc-simple.c similarity index 100% rename from tests/regression/75-invalid_dealloc/01-invalid-dealloc-simple.c rename to tests/regression/75-invalid_free/01-invalid-dealloc-simple.c diff --git a/tests/regression/75-invalid_dealloc/02-invalid-dealloc-struct.c b/tests/regression/75-invalid_free/02-invalid-dealloc-struct.c similarity index 100% rename from tests/regression/75-invalid_dealloc/02-invalid-dealloc-struct.c rename to tests/regression/75-invalid_free/02-invalid-dealloc-struct.c diff --git a/tests/regression/75-invalid_dealloc/03-invalid-dealloc-array.c b/tests/regression/75-invalid_free/03-invalid-dealloc-array.c similarity index 100% rename from tests/regression/75-invalid_dealloc/03-invalid-dealloc-array.c rename to tests/regression/75-invalid_free/03-invalid-dealloc-array.c diff --git a/tests/regression/75-invalid_dealloc/04-invalid-realloc.c b/tests/regression/75-invalid_free/04-invalid-realloc.c similarity index 100% rename from tests/regression/75-invalid_dealloc/04-invalid-realloc.c rename to tests/regression/75-invalid_free/04-invalid-realloc.c diff --git a/tests/regression/75-invalid_dealloc/05-free-at-offset.c b/tests/regression/75-invalid_free/05-free-at-offset.c similarity index 100% rename from tests/regression/75-invalid_dealloc/05-free-at-offset.c rename to tests/regression/75-invalid_free/05-free-at-offset.c diff --git a/tests/regression/75-invalid_dealloc/06-realloc-at-offset.c b/tests/regression/75-invalid_free/06-realloc-at-offset.c similarity index 100% rename from tests/regression/75-invalid_dealloc/06-realloc-at-offset.c rename to tests/regression/75-invalid_free/06-realloc-at-offset.c diff --git a/tests/regression/75-invalid_dealloc/07-free-at-struct-offset.c b/tests/regression/75-invalid_free/07-free-at-struct-offset.c similarity index 100% rename from tests/regression/75-invalid_dealloc/07-free-at-struct-offset.c rename to tests/regression/75-invalid_free/07-free-at-struct-offset.c diff --git a/tests/regression/74-use_after_free/08-itc-no-double-free.c b/tests/regression/75-invalid_free/08-itc-no-double-free.c similarity index 100% rename from tests/regression/74-use_after_free/08-itc-no-double-free.c rename to tests/regression/75-invalid_free/08-itc-no-double-free.c diff --git a/tests/regression/75-invalid_dealloc/09-juliet-invalid-dealloc-alloca.c b/tests/regression/75-invalid_free/09-juliet-invalid-dealloc-alloca.c similarity index 100% rename from tests/regression/75-invalid_dealloc/09-juliet-invalid-dealloc-alloca.c rename to tests/regression/75-invalid_free/09-juliet-invalid-dealloc-alloca.c diff --git a/tests/regression/75-invalid_dealloc/10-invalid-dealloc-union.c b/tests/regression/75-invalid_free/10-invalid-dealloc-union.c similarity index 100% rename from tests/regression/75-invalid_dealloc/10-invalid-dealloc-union.c rename to tests/regression/75-invalid_free/10-invalid-dealloc-union.c diff --git a/tests/regression/74-use_after_free/07-itc-double-free.c b/tests/regression/75-invalid_free/11-itc-double-free.c similarity index 100% rename from tests/regression/74-use_after_free/07-itc-double-free.c rename to tests/regression/75-invalid_free/11-itc-double-free.c diff --git a/tests/regression/75-invalid_dealloc/08-realloc-at-struct-offset.c b/tests/regression/75-invalid_free/12-realloc-at-struct-offset.c similarity index 100% rename from tests/regression/75-invalid_dealloc/08-realloc-at-struct-offset.c rename to tests/regression/75-invalid_free/12-realloc-at-struct-offset.c diff --git a/tests/regression/74-use_after_free/10-juliet-double-free.c b/tests/regression/75-invalid_free/13-juliet-double-free.c similarity index 100% rename from tests/regression/74-use_after_free/10-juliet-double-free.c rename to tests/regression/75-invalid_free/13-juliet-double-free.c From 3281c74399a6f4b9c16a64ca11e041897ff36d9a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 11 Oct 2023 17:54:59 +0300 Subject: [PATCH 0859/1312] Temporarily reintroduce old unqualified directory structure inside common library --- .github/workflows/options.yml | 6 +++--- .readthedocs.yaml | 2 +- docs/user-guide/configuring.md | 2 +- src/common/{ => cdomains}/basetype.ml | 0 src/common/{ => domains}/lattice.ml | 0 src/common/{ => domains}/myCheck.ml | 0 src/common/{ => domains}/printable.ml | 0 src/common/dune | 4 ++-- src/common/{ => framework}/analysisState.ml | 0 src/common/{ => framework}/controlSpecC.ml | 0 src/common/{ => framework}/controlSpecC.mli | 0 src/common/{ => framework}/edge.ml | 0 src/common/{ => framework}/myCFG.ml | 0 src/common/{ => framework}/node.ml | 0 src/common/{ => framework}/node0.ml | 0 src/common/{ => incremental}/updateCil0.ml | 0 src/common/{ => util}/afterConfig.ml | 0 src/common/{ => util}/cilType.ml | 0 src/common/{ => util}/cilfacade.ml | 0 src/common/{ => util}/cilfacade0.ml | 0 src/common/{ => util}/gobConfig.ml | 0 src/common/{ => util}/gobFormat.ml | 0 src/common/{ => util}/jsonSchema.ml | 0 src/common/{ => util}/lazyEval.ml | 0 src/common/{ => util}/messageCategory.ml | 0 src/common/{ => util}/messageUtil.ml | 0 src/common/{ => util}/messages.ml | 0 src/common/{ => util}/options.ml | 2 +- src/common/{ => util}/options.schema.json | 0 src/common/{ => util}/resettableLazy.ml | 0 src/common/{ => util}/resettableLazy.mli | 0 src/common/{ => util}/richVarinfo.ml | 0 src/common/{ => util}/richVarinfo.mli | 0 src/common/{ => util}/timing.ml | 0 src/common/{ => util}/tracing.ml | 0 src/common/{ => util}/xmlUtil.ml | 0 src/goblint_lib.ml | 2 +- 37 files changed, 9 insertions(+), 9 deletions(-) rename src/common/{ => cdomains}/basetype.ml (100%) rename src/common/{ => domains}/lattice.ml (100%) rename src/common/{ => domains}/myCheck.ml (100%) rename src/common/{ => domains}/printable.ml (100%) rename src/common/{ => framework}/analysisState.ml (100%) rename src/common/{ => framework}/controlSpecC.ml (100%) rename src/common/{ => framework}/controlSpecC.mli (100%) rename src/common/{ => framework}/edge.ml (100%) rename src/common/{ => framework}/myCFG.ml (100%) rename src/common/{ => framework}/node.ml (100%) rename src/common/{ => framework}/node0.ml (100%) rename src/common/{ => incremental}/updateCil0.ml (100%) rename src/common/{ => util}/afterConfig.ml (100%) rename src/common/{ => util}/cilType.ml (100%) rename src/common/{ => util}/cilfacade.ml (100%) rename src/common/{ => util}/cilfacade0.ml (100%) rename src/common/{ => util}/gobConfig.ml (100%) rename src/common/{ => util}/gobFormat.ml (100%) rename src/common/{ => util}/jsonSchema.ml (100%) rename src/common/{ => util}/lazyEval.ml (100%) rename src/common/{ => util}/messageCategory.ml (100%) rename src/common/{ => util}/messageUtil.ml (100%) rename src/common/{ => util}/messages.ml (100%) rename src/common/{ => util}/options.ml (98%) rename src/common/{ => util}/options.schema.json (100%) rename src/common/{ => util}/resettableLazy.ml (100%) rename src/common/{ => util}/resettableLazy.mli (100%) rename src/common/{ => util}/richVarinfo.ml (100%) rename src/common/{ => util}/richVarinfo.mli (100%) rename src/common/{ => util}/timing.ml (100%) rename src/common/{ => util}/tracing.ml (100%) rename src/common/{ => util}/xmlUtil.ml (100%) diff --git a/.github/workflows/options.yml b/.github/workflows/options.yml index 84906d4949..40652791fa 100644 --- a/.github/workflows/options.yml +++ b/.github/workflows/options.yml @@ -26,10 +26,10 @@ jobs: run: npm install -g ajv-cli - name: Migrate schema # https://github.com/ajv-validator/ajv-cli/issues/199 - run: ajv migrate -s src/common/options.schema.json + run: ajv migrate -s src/common/util/options.schema.json - name: Validate conf - run: ajv validate -s src/common/options.schema.json -d "conf/**/*.json" + run: ajv validate -s src/common/util/options.schema.json -d "conf/**/*.json" - name: Validate incremental tests - run: ajv validate -s src/common/options.schema.json -d "tests/incremental/*/*.json" + run: ajv validate -s src/common/util/options.schema.json -d "tests/incremental/*/*.json" diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 4827b825ef..08044d195c 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -20,4 +20,4 @@ build: - pip install json-schema-for-humans post_build: - mkdir _readthedocs/html/jsfh/ - - generate-schema-doc --config-file jsfh.yml src/common/options.schema.json _readthedocs/html/jsfh/ + - generate-schema-doc --config-file jsfh.yml src/common/util/options.schema.json _readthedocs/html/jsfh/ diff --git a/docs/user-guide/configuring.md b/docs/user-guide/configuring.md index 348e15dac4..9a32a14a4c 100644 --- a/docs/user-guide/configuring.md +++ b/docs/user-guide/configuring.md @@ -24,7 +24,7 @@ In `.vscode/settings.json` add the following: "/conf/*.json", "/tests/incremental/*/*.json" ], - "url": "/src/common/options.schema.json" + "url": "/src/common/util/options.schema.json" } ] } diff --git a/src/common/basetype.ml b/src/common/cdomains/basetype.ml similarity index 100% rename from src/common/basetype.ml rename to src/common/cdomains/basetype.ml diff --git a/src/common/lattice.ml b/src/common/domains/lattice.ml similarity index 100% rename from src/common/lattice.ml rename to src/common/domains/lattice.ml diff --git a/src/common/myCheck.ml b/src/common/domains/myCheck.ml similarity index 100% rename from src/common/myCheck.ml rename to src/common/domains/myCheck.ml diff --git a/src/common/printable.ml b/src/common/domains/printable.ml similarity index 100% rename from src/common/printable.ml rename to src/common/domains/printable.ml diff --git a/src/common/dune b/src/common/dune index 03a93a3030..b937ecdd02 100644 --- a/src/common/dune +++ b/src/common/dune @@ -1,4 +1,4 @@ -(include_subdirs no) +(include_subdirs unqualified) (library (name goblint_common) @@ -24,5 +24,5 @@ ppx_deriving_hash ppx_deriving_yojson ppx_blob)) - (preprocessor_deps (file options.schema.json))) + (preprocessor_deps (file util/options.schema.json))) diff --git a/src/common/analysisState.ml b/src/common/framework/analysisState.ml similarity index 100% rename from src/common/analysisState.ml rename to src/common/framework/analysisState.ml diff --git a/src/common/controlSpecC.ml b/src/common/framework/controlSpecC.ml similarity index 100% rename from src/common/controlSpecC.ml rename to src/common/framework/controlSpecC.ml diff --git a/src/common/controlSpecC.mli b/src/common/framework/controlSpecC.mli similarity index 100% rename from src/common/controlSpecC.mli rename to src/common/framework/controlSpecC.mli diff --git a/src/common/edge.ml b/src/common/framework/edge.ml similarity index 100% rename from src/common/edge.ml rename to src/common/framework/edge.ml diff --git a/src/common/myCFG.ml b/src/common/framework/myCFG.ml similarity index 100% rename from src/common/myCFG.ml rename to src/common/framework/myCFG.ml diff --git a/src/common/node.ml b/src/common/framework/node.ml similarity index 100% rename from src/common/node.ml rename to src/common/framework/node.ml diff --git a/src/common/node0.ml b/src/common/framework/node0.ml similarity index 100% rename from src/common/node0.ml rename to src/common/framework/node0.ml diff --git a/src/common/updateCil0.ml b/src/common/incremental/updateCil0.ml similarity index 100% rename from src/common/updateCil0.ml rename to src/common/incremental/updateCil0.ml diff --git a/src/common/afterConfig.ml b/src/common/util/afterConfig.ml similarity index 100% rename from src/common/afterConfig.ml rename to src/common/util/afterConfig.ml diff --git a/src/common/cilType.ml b/src/common/util/cilType.ml similarity index 100% rename from src/common/cilType.ml rename to src/common/util/cilType.ml diff --git a/src/common/cilfacade.ml b/src/common/util/cilfacade.ml similarity index 100% rename from src/common/cilfacade.ml rename to src/common/util/cilfacade.ml diff --git a/src/common/cilfacade0.ml b/src/common/util/cilfacade0.ml similarity index 100% rename from src/common/cilfacade0.ml rename to src/common/util/cilfacade0.ml diff --git a/src/common/gobConfig.ml b/src/common/util/gobConfig.ml similarity index 100% rename from src/common/gobConfig.ml rename to src/common/util/gobConfig.ml diff --git a/src/common/gobFormat.ml b/src/common/util/gobFormat.ml similarity index 100% rename from src/common/gobFormat.ml rename to src/common/util/gobFormat.ml diff --git a/src/common/jsonSchema.ml b/src/common/util/jsonSchema.ml similarity index 100% rename from src/common/jsonSchema.ml rename to src/common/util/jsonSchema.ml diff --git a/src/common/lazyEval.ml b/src/common/util/lazyEval.ml similarity index 100% rename from src/common/lazyEval.ml rename to src/common/util/lazyEval.ml diff --git a/src/common/messageCategory.ml b/src/common/util/messageCategory.ml similarity index 100% rename from src/common/messageCategory.ml rename to src/common/util/messageCategory.ml diff --git a/src/common/messageUtil.ml b/src/common/util/messageUtil.ml similarity index 100% rename from src/common/messageUtil.ml rename to src/common/util/messageUtil.ml diff --git a/src/common/messages.ml b/src/common/util/messages.ml similarity index 100% rename from src/common/messages.ml rename to src/common/util/messages.ml diff --git a/src/common/options.ml b/src/common/util/options.ml similarity index 98% rename from src/common/options.ml rename to src/common/util/options.ml index c9bd41038f..3046f70809 100644 --- a/src/common/options.ml +++ b/src/common/util/options.ml @@ -1,4 +1,4 @@ -(** [src/common/options.schema.json] low-level access. *) +(** [src/common/util/options.schema.json] low-level access. *) open Json_schema diff --git a/src/common/options.schema.json b/src/common/util/options.schema.json similarity index 100% rename from src/common/options.schema.json rename to src/common/util/options.schema.json diff --git a/src/common/resettableLazy.ml b/src/common/util/resettableLazy.ml similarity index 100% rename from src/common/resettableLazy.ml rename to src/common/util/resettableLazy.ml diff --git a/src/common/resettableLazy.mli b/src/common/util/resettableLazy.mli similarity index 100% rename from src/common/resettableLazy.mli rename to src/common/util/resettableLazy.mli diff --git a/src/common/richVarinfo.ml b/src/common/util/richVarinfo.ml similarity index 100% rename from src/common/richVarinfo.ml rename to src/common/util/richVarinfo.ml diff --git a/src/common/richVarinfo.mli b/src/common/util/richVarinfo.mli similarity index 100% rename from src/common/richVarinfo.mli rename to src/common/util/richVarinfo.mli diff --git a/src/common/timing.ml b/src/common/util/timing.ml similarity index 100% rename from src/common/timing.ml rename to src/common/util/timing.ml diff --git a/src/common/tracing.ml b/src/common/util/tracing.ml similarity index 100% rename from src/common/tracing.ml rename to src/common/util/tracing.ml diff --git a/src/common/xmlUtil.ml b/src/common/util/xmlUtil.ml similarity index 100% rename from src/common/xmlUtil.ml rename to src/common/util/xmlUtil.ml diff --git a/src/goblint_lib.ml b/src/goblint_lib.ml index a108058291..0b3829f11c 100644 --- a/src/goblint_lib.ml +++ b/src/goblint_lib.ml @@ -49,7 +49,7 @@ module VarQuery = VarQuery (** {2 Configuration} Runtime configuration is represented as JSON. - Options are specified and documented by the JSON schema [src/common/options.schema.json]. *) + Options are specified and documented by the JSON schema [src/common/util/options.schema.json]. *) module GobConfig = GobConfig module AfterConfig = AfterConfig From 956efd86e8f63aa75bc54808649354c4f7659e8b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 12 Oct 2023 10:39:24 +0300 Subject: [PATCH 0860/1312] Fix indentation in moved common library files --- src/common/domains/lattice.ml | 16 ++++++++-------- src/common/util/lazyEval.ml | 14 +++++++------- src/common/util/messageCategory.ml | 4 ++-- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/common/domains/lattice.ml b/src/common/domains/lattice.ml index 4cdaa8fb9f..79455aea62 100644 --- a/src/common/domains/lattice.ml +++ b/src/common/domains/lattice.ml @@ -4,12 +4,12 @@ module Pretty = GoblintCil.Pretty (* module type Rel = -sig - type t - type relation = Less | Equal | Greater | Uncomparable - val rel : t -> t -> relation - val in_rel : t -> relation -> t -> bool -end *) + sig + type t + type relation = Less | Equal | Greater | Uncomparable + val rel : t -> t -> relation + val in_rel : t -> relation -> t -> bool + end *) (* partial order: elements might not be comparable and no bot/top -> join etc. might fail with exception Uncomparable *) exception Uncomparable @@ -324,14 +324,14 @@ struct match (x,y) with | (`Lifted x, `Lifted y) -> (try `Lifted (Base.widen x y) - with Uncomparable -> `Top) + with Uncomparable -> `Top) | _ -> y let narrow x y = match (x,y) with | (`Lifted x, `Lifted y) -> (try `Lifted (Base.narrow x y) - with Uncomparable -> `Bot) + with Uncomparable -> `Bot) | _ -> x end diff --git a/src/common/util/lazyEval.ml b/src/common/util/lazyEval.ml index e49a5f4693..9007cdd089 100644 --- a/src/common/util/lazyEval.ml +++ b/src/common/util/lazyEval.ml @@ -5,10 +5,10 @@ Node -> CilType -> Printable -> Goblintutil -> GobConfig -> Tracing -> Node *) module Make (M : sig - type t - type result - val eval : t -> result -end) : sig + type t + type result + val eval : t -> result + end) : sig type t val make : M.t -> t val force : t -> M.result @@ -20,8 +20,8 @@ end = struct let force l = match l.value with | `Closure arg -> - let v = M.eval arg in - l.value <- `Computed v; - v + let v = M.eval arg in + l.value <- `Computed v; + v | `Computed v -> v end diff --git a/src/common/util/messageCategory.ml b/src/common/util/messageCategory.ml index 1bb31d6d5b..c70b8faf5f 100644 --- a/src/common/util/messageCategory.ml +++ b/src/common/util/messageCategory.ml @@ -260,8 +260,8 @@ let categoryName = function | Behavior x -> behaviorName x | Integer x -> (match x with - | Overflow -> "Overflow"; - | DivByZero -> "DivByZero") + | Overflow -> "Overflow"; + | DivByZero -> "DivByZero") | Float -> "Float" From 59462c3e2614d79f63a4b8376b2304050f24f6d6 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 12 Oct 2023 10:52:20 +0300 Subject: [PATCH 0861/1312] Use batteries.unthreaded everywhere to avoid Gobview exception --- gobview | 2 +- src/common/dune | 2 +- src/util/std/dune | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gobview b/gobview index 41be36b548..42b07f8253 160000 --- a/gobview +++ b/gobview @@ -1 +1 @@ -Subproject commit 41be36b54837b24e6de83740c34e810d3d1afdfb +Subproject commit 42b07f825316052ec030370daf0d00ebe28ec092 diff --git a/src/common/dune b/src/common/dune index b937ecdd02..c9ed9f9db2 100644 --- a/src/common/dune +++ b/src/common/dune @@ -5,7 +5,7 @@ (public_name goblint.common) (wrapped false) ; TODO: wrap (libraries - batteries + batteries.unthreaded zarith goblint_std goblint-cil diff --git a/src/util/std/dune b/src/util/std/dune index c85710a8d6..c6961a1725 100644 --- a/src/util/std/dune +++ b/src/util/std/dune @@ -4,7 +4,7 @@ (name goblint_std) (public_name goblint.std) (libraries - batteries + batteries.unthreaded zarith goblint-cil fpath From a0b376bfa6c587e293172c6d86aa2a1085ddb5c3 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 12 Oct 2023 11:40:03 +0300 Subject: [PATCH 0862/1312] Add odoc page for package --- src/common/common.mld | 74 +++++++++++++++++++++++++++++++++++++++++++ src/common/dune | 1 + src/dune | 2 ++ src/goblint_lib.ml | 1 + src/index.mld | 51 +++++++++++++++++++++++++++++ 5 files changed, 129 insertions(+) create mode 100644 src/common/common.mld create mode 100644 src/index.mld diff --git a/src/common/common.mld b/src/common/common.mld new file mode 100644 index 0000000000..662c789572 --- /dev/null +++ b/src/common/common.mld @@ -0,0 +1,74 @@ +{0 Library goblint.common} +This library is unwrapped and provides the following top-level modules. +For better context, see {!Goblint_lib} which also documents these modules. + + +{1 Framework} + +{2 CFG} +{!modules: +Node +Edge +MyCFG +} + +{2 Specification} +{!modules: +AnalysisState +ControlSpecC +} + +{2 Configuration} +{!modules: +GobConfig +AfterConfig +JsonSchema +Options +} + + +{1 Domains} +{!modules: +Printable +Lattice +} + +{2 Analysis-specific} + +{3 Other} +{!modules:Basetype} + + +{1 I/O} +{!modules: +Messages +Tracing +} + + +{1 Utilities} +{!modules:Timing} + +{2 General} +{!modules: +LazyEval +ResettableLazy +MessageUtil +XmlUtil +} + +{2 CIL} +{!modules: +CilType +Cilfacade +RichVarinfo +} + + +{1 Library extensions} + +{2 Standard library} +{!modules:GobFormat} + +{2 Other libraries} +{!modules:MyCheck} diff --git a/src/common/dune b/src/common/dune index c9ed9f9db2..c8f1564782 100644 --- a/src/common/dune +++ b/src/common/dune @@ -26,3 +26,4 @@ ppx_blob)) (preprocessor_deps (file util/options.schema.json))) +(documentation) diff --git a/src/dune b/src/dune index df19f85340..acd5348acb 100644 --- a/src/dune +++ b/src/dune @@ -125,3 +125,5 @@ (flags (:standard -warn-error -A -w -unused-var-strict -w -unused-functor-parameter -w +9)) ; https://dune.readthedocs.io/en/stable/faq.html#how-to-make-warnings-non-fatal ) ) + +(documentation) diff --git a/src/goblint_lib.ml b/src/goblint_lib.ml index 0b3829f11c..dadeb2cda1 100644 --- a/src/goblint_lib.ml +++ b/src/goblint_lib.ml @@ -1,3 +1,4 @@ +(** Main library. *) (** {1 Framework} *) diff --git a/src/index.mld b/src/index.mld new file mode 100644 index 0000000000..2afbbc97ae --- /dev/null +++ b/src/index.mld @@ -0,0 +1,51 @@ +{0 goblint index} + +{1 Goblint} +The following libraries make up Goblint's main codebase. + +{2 Library goblint.lib} +{!modules:Goblint_lib} +This library currently contains the majority of Goblint and is in the process of being split into smaller libraries. + +{2 Library goblint.common} +This {{!page-common}unwrapped library} contains various common modules extracted from {!Goblint_lib}. + + +{1 Library extensions} +The following libraries provide extensions to other OCaml libraries. + +{2 Library goblint.std} +{!modules:Goblint_std} + + +{1 Package utilities} +The following libraries provide [goblint] package metadata for executables. + +{2 Library goblint.build-info} +{!modules:Goblint_build_info} +This library is virtual and has the following implementations +- goblint.build-info.dune for native executables, +- goblint.build-info.js for js_of_ocaml executables. + +{2 Library goblint.sites} +{!modules:Goblint_sites} +This library is virtual and has the following implementations +- goblint.sites.dune for native executables, +- goblint.sites.js for js_of_ocaml executables. + + +{1 Independent utilities} +The following libraries provide utilities which are completely independent of Goblint. + +{2 Library goblint.backtrace} +{!modules:Goblint_backtrace} + +{2 Library goblint.timing} +{!modules:Goblint_timing} + + +{1 Vendored} +The following libraries are vendored in Goblint. + +{2 Library goblint.zarith.mlgmpidl} +{!modules:Z_mlgmpidl} From 47cce4f58634308a4326683ab8031499eab2e9e0 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 12 Oct 2023 11:41:24 +0300 Subject: [PATCH 0863/1312] Use goblint library documentation page in Readthedocs --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index 558c381e66..428e28078d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -30,7 +30,7 @@ nav: - 👶 Your first analysis: developer-guide/firstanalysis.md - 🏫 Extending library: developer-guide/extending-library.md - 📢 Messaging: developer-guide/messaging.md - - 🗃️ API reference: https://goblint.github.io/analyzer/ + - 🗃️ API reference: https://goblint.github.io/analyzer/goblint/ - 🚨 Testing: developer-guide/testing.md - 🪲 Debugging: developer-guide/debugging.md - 📉 Profiling: developer-guide/profiling.md From 7ebf97e9ef2f9167898f40bd304880d48f10cb08 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 12 Oct 2023 15:38:49 +0300 Subject: [PATCH 0864/1312] Fix scripts/goblint-lib-modules.py --- scripts/goblint-lib-modules.py | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/scripts/goblint-lib-modules.py b/scripts/goblint-lib-modules.py index 342f9a76bd..5f02271616 100755 --- a/scripts/goblint-lib-modules.py +++ b/scripts/goblint-lib-modules.py @@ -6,16 +6,20 @@ src_root_path = Path("./src") -goblint_lib_path = src_root_path / "goblint_lib.ml" +goblint_lib_paths = [ + src_root_path / "goblint_lib.ml", + src_root_path / "util" / "std" / "goblint_std.ml", +] goblint_lib_modules = set() -with goblint_lib_path.open() as goblint_lib_file: - for line in goblint_lib_file: - line = line.strip() - m = re.match(r"module (.*) = .*", line) - if m is not None: - module_name = m.group(1) - goblint_lib_modules.add(module_name) +for goblint_lib_path in goblint_lib_paths: + with goblint_lib_path.open() as goblint_lib_file: + for line in goblint_lib_file: + line = line.strip() + m = re.match(r"module (.*) = .*", line) + if m is not None: + module_name = m.group(1) + goblint_lib_modules.add(module_name) src_vendor_path = src_root_path / "vendor" exclude_module_names = set([ @@ -29,15 +33,21 @@ "Mainspec", # libraries + "Goblint_std", "Goblint_timing", "Goblint_backtrace", "Goblint_sites", "Goblint_build_info", + "Dune_build_info", "MessageCategory", # included in Messages "PreValueDomain", # included in ValueDomain "SpecCore", # spec stuff "SpecUtil", # spec stuff + + "ConfigVersion", + "ConfigProfile", + "ConfigOcaml", ]) src_modules = set() From 910a11f903e217efcc946d7d9d988c50575cd3ae Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 12 Oct 2023 14:50:36 +0200 Subject: [PATCH 0865/1312] Activate `cil.addNestedScopeAttr` when `memOutOfBounds` analysis is active --- src/maingoblint.ml | 1 + tests/regression/74-invalid_deref/08-memset-memcpy-array.c | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/maingoblint.ml b/src/maingoblint.ml index 97f35214be..b5998df2d1 100644 --- a/src/maingoblint.ml +++ b/src/maingoblint.ml @@ -135,6 +135,7 @@ let check_arguments () = if get_bool "allfuns" && not (get_bool "exp.earlyglobs") then (set_bool "exp.earlyglobs" true; warn "allfuns enables exp.earlyglobs.\n"); if not @@ List.mem "escape" @@ get_string_list "ana.activated" then warn "Without thread escape analysis, every local variable whose address is taken is considered escaped, i.e., global!"; if List.mem "malloc_null" @@ get_string_list "ana.activated" && not @@ get_bool "sem.malloc.fail" then (set_bool "sem.malloc.fail" true; warn "The malloc_null analysis enables sem.malloc.fail."); + if List.mem "memOutOfBounds" @@ get_string_list "ana.activated" && not @@ get_bool "cil.addNestedScopeAttr" then (set_bool "cil.addNestedScopeAttr" true; warn "The memOutOfBounds analysis enables cil.addNestedScopeAttr."); if get_bool "ana.base.context.int" && not (get_bool "ana.base.context.non-ptr") then (set_bool "ana.base.context.int" false; warn "ana.base.context.int implicitly disabled by ana.base.context.non-ptr"); (* order matters: non-ptr=false, int=true -> int=false cascades to interval=false with warning *) if get_bool "ana.base.context.interval" && not (get_bool "ana.base.context.int") then (set_bool "ana.base.context.interval" false; warn "ana.base.context.interval implicitly disabled by ana.base.context.int"); diff --git a/tests/regression/74-invalid_deref/08-memset-memcpy-array.c b/tests/regression/74-invalid_deref/08-memset-memcpy-array.c index f231ba2dc4..210a61d459 100644 --- a/tests/regression/74-invalid_deref/08-memset-memcpy-array.c +++ b/tests/regression/74-invalid_deref/08-memset-memcpy-array.c @@ -6,13 +6,14 @@ int main(int argc, char const *argv[]) { int arr[42]; // Size should be 168 bytes (with 4 byte ints) int *b = arr; - + int random; + memset(b, 0, 168); //NOWARN memset(b, 0, sizeof(arr)); //NOWARN memset(b, 0, 169); //WARN memset(b, 0, sizeof(arr) + 1); //WARN - + int *c = malloc(sizeof(arr)); // Size should be 168 bytes (with 4 byte ints) memcpy(b, c, 168); //NOWARN memcpy(b, c, sizeof(arr)); //NOWARN @@ -26,7 +27,7 @@ int main(int argc, char const *argv[]) { memset(b, 0, 168); //WARN memcpy(b, c, 168); //WARN } else if (*(argv + 5)) { - int random = rand(); + random = rand(); b = &random; memset(b, 0, 168); //WARN memcpy(b, c, 168); //WARN From 44b44928668346c722ca23fb448c6da73f471276 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 12 Oct 2023 15:37:25 +0200 Subject: [PATCH 0866/1312] Issue non-termination warning for `longjmp` calls --- src/framework/constraints.ml | 9 +++++++++ tests/regression/78-termination/49-longjmp.c | 11 +++++++++++ 2 files changed, 20 insertions(+) create mode 100644 tests/regression/78-termination/49-longjmp.c diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 924794b9ce..cf439f2c45 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1684,6 +1684,15 @@ struct ) in List.iter handle_path (S.paths_as_set conv_ctx); + if !AnalysisState.should_warn && List.mem "termination" @@ get_string_list "ana.activated" then ( + AnalysisState.svcomp_may_not_terminate := true; + let msgs = + [(Pretty.dprintf + "The program might not terminate! (Longjmp)", + None + );] in + M.msg_group Warning ~category:NonTerminating "Possibly non terminating loops" msgs + ); S.D.bot () | _ -> S.special conv_ctx lv f args let threadenter ctx = S.threadenter (conv ctx) diff --git a/tests/regression/78-termination/49-longjmp.c b/tests/regression/78-termination/49-longjmp.c new file mode 100644 index 0000000000..be13cb286c --- /dev/null +++ b/tests/regression/78-termination/49-longjmp.c @@ -0,0 +1,11 @@ +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include +jmp_buf buf; +int main() +{ + if(setjmp(buf)) { + + } + + longjmp(buf, 1); +} From ea38918e9e3247c622d8d1acf754f7ea021e6f9c Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 12 Oct 2023 15:42:42 +0200 Subject: [PATCH 0867/1312] Include termination tests into `coverage` and `unlocked` --- .github/workflows/coverage.yml | 3 +++ .github/workflows/unlocked.yml | 3 +++ 2 files changed, 6 insertions(+) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 5635ebbeea..0208af7c7a 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -65,6 +65,9 @@ jobs: - name: Test apron regression (Mukherjee et. al SAS '17 paper') # skipped by default but CI has apron, so explicitly test group (which ignores skipping -- it's now a feature!) run: ruby scripts/update_suite.rb group apron-mukherjee -s + - name: Test apron termination regression # skipped by default but CI has apron, so explicitly test group (which ignores skipping -- it's now a feature!) + run: ruby scripts/update_suite.rb group termination -s + - name: Test regression cram run: opam exec -- dune runtest tests/regression diff --git a/.github/workflows/unlocked.yml b/.github/workflows/unlocked.yml index 6c23c7cdd4..22e1417ea4 100644 --- a/.github/workflows/unlocked.yml +++ b/.github/workflows/unlocked.yml @@ -92,6 +92,9 @@ jobs: if: ${{ matrix.apron }} run: ruby scripts/update_suite.rb group apron-mukherjee -s + - name: Test apron termination regression # skipped by default but CI has apron, so explicitly test group (which ignores skipping -- it's now a feature!) + run: ruby scripts/update_suite.rb group termination -s + - name: Test regression cram run: opam exec -- dune runtest tests/regression From e28495363a932e40d4d0ec519e74e804aaaa55da Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 12 Oct 2023 16:39:27 +0200 Subject: [PATCH 0868/1312] Simplify global invariant Co-authored-by: Simmo Saan --- src/framework/analyses.ml | 16 +++++++++ src/framework/constraints.ml | 70 ++++++++++++++++-------------------- 2 files changed, 47 insertions(+), 39 deletions(-) diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index bb2170509d..3426f98675 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -88,6 +88,22 @@ struct | `Right _ -> true end +module GVarFC (V:SpecSysVar) (C:Printable.S) = +struct + include Printable.Either (V) (Printable.Prod (CilType.Fundec) (C)) + let name () = "FromSpec" + let spec x = `Left x + let call (x, c) = `Right (x, c) + + (* from Basetype.Variables *) + let var_id = show + let node _ = MyCFG.Function Cil.dummyFunDec + let pretty_trace = pretty + let is_write_only = function + | `Left x -> V.is_write_only x + | `Right _ -> true +end + module GVarG (G: Lattice.S) (C: Printable.S) = struct module CSet = diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index cf439f2c45..038068ea0d 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1723,36 +1723,35 @@ module RecursionTermLifter (S: Spec) struct include S - (* contains all the callee fundecs*) - module V = GVarF(S.V) + (* contains all the callee fundecs and contexts *) + module V = GVarFC(S.V)(S.C) - (* Tuple containing the fundec and context of the caller *) - module CallGraphTuple = Printable.Prod (CilType.Fundec) (S.C) + (* Tuple containing the fundec and context of a caller *) + module Call = Printable.Prod (CilType.Fundec) (S.C) (* Set containing multiple caller tuples *) - module CallGraphSet = SetDomain.Make (CallGraphTuple) - - (* Mapping from the callee context to the set of all caller tuples*) - module CallGraphMap = MapDomain.MapBot (S.C) (CallGraphSet) + module CallerSet = SetDomain.Make (Call) module G = struct - include Lattice.Lift2 (G) (CallGraphMap) (Printable.DefaultNames) + include Lattice.Lift2 (G) (CallerSet) (Printable.DefaultNames) let spec = function | `Bot -> G.bot () | `Lifted1 x -> x | _ -> failwith "RecursionTermLifter.spec" - let callGraph = function - | `Bot -> CallGraphMap.bot () + + let callers = function + | `Bot -> CallerSet.bot () | `Lifted2 x -> x | _ -> failwith "RecursionTermLifter.callGraph" + let create_spec spec = `Lifted1 spec - let create_callGraph callGraph = `Lifted2 callGraph + let create_singleton_caller caller = `Lifted2 (CallerSet.singleton caller) let printXml f = function | `Lifted1 x -> G.printXml f x - | `Lifted2 x -> BatPrintf.fprintf f "%a" CallGraphMap.printXml x + | `Lifted2 x -> BatPrintf.fprintf f "%a" CallerSet.printXml x | x -> BatPrintf.fprintf f "%a" printXml x end @@ -1785,19 +1784,15 @@ struct else if not (LH.mem global_visited_calls call) then begin LH.replace global_visited_calls call (); let new_path_visited_calls = LS.add call path_visited_calls in - let fundec_e_typeV: V.t = V.relift (`Right fundec_e) in - let gmap = G.callGraph (ctx.global (fundec_e_typeV)) in - let callers: CallGraphSet.t = CallGraphMap.find (context_e) gmap in - CallGraphSet.iter (fun to_call -> + let gvar = V.call (fundec_e, context_e) in + let callers = G.callers (ctx.global gvar) in + CallerSet.iter (fun to_call -> iter_call new_path_visited_calls to_call ) callers; end in - let gmap = G.callGraph (ctx.global (v)) in - CallGraphMap.iter(fun key value -> - let call = (v', key) in - iter_call LS.empty call - ) gmap (* try all fundec + context pairs that are in the map *) + let callers = G.callers (ctx.global v) in + CallerSet.iter (iter_call LS.empty) callers let query ctx (type a) (q: a Queries.t): a Queries.result = match q with @@ -1825,30 +1820,27 @@ struct let assign ctx = S.assign (conv ctx) let vdecl ctx = S.vdecl (conv ctx) - (* c = context - t = set of tuples (fundec * context) - *) - let side_context sideg f c t = - if !AnalysisState.postsolving then - sideg (V.contexts f) (G.create_callGraph (CallGraphMap.singleton (c) (t))) + + let record_call sideg callee caller = + sideg (V.call callee) (G.create_singleton_caller caller) let enter ctx = S.enter (conv ctx) let paths_as_set ctx = S.paths_as_set (conv ctx) let body ctx = S.body (conv ctx) let return ctx = S.return (conv ctx) let combine_env ctx r fe f args fc es f_ask = - if !AnalysisState.postsolving then - let c_r: S.C.t = ctx.context () in (*Caller context*) + if !AnalysisState.postsolving then ( + let c_r: S.C.t = ctx.context () in (* Caller context *) let nodeF = ctx.node in - let fd_r : fundec = Node.find_fundec nodeF in (*Caller fundec*) - let c_e: S.C.t = Option.get fc in (*Callee context*) - let fd_e : fundec = f in (*Callee fundec*) - let tup: (fundec * S.C.t) = (fd_r, c_r) in - let t = CallGraphSet.singleton (tup) in - side_context ctx.sideg fd_e (c_e) t; - S.combine_env (conv ctx) r fe f args fc es f_ask - else - S.combine_env (conv ctx) r fe f args fc es f_ask + let fd_r : fundec = Node.find_fundec nodeF in (* Caller fundec *) + let caller: (fundec * S.C.t) = (fd_r, c_r) in + let c_e: S.C.t = Option.get fc in (* Callee context *) + let fd_e : fundec = f in (* Callee fundec *) + let callee = (fd_e, c_e) in + record_call ctx.sideg callee caller + ); + S.combine_env (conv ctx) r fe f args fc es f_ask + let combine_assign ctx = S.combine_assign (conv ctx) let special ctx = S.special (conv ctx) let threadenter ctx = S.threadenter (conv ctx) From 7f3cadfaea69592fe03fb311613b14efc0cf4718 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 12 Oct 2023 16:42:09 +0200 Subject: [PATCH 0869/1312] Adapt comments to simplified global invariant --- src/framework/constraints.ml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 038068ea0d..b4b72146d5 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1710,14 +1710,14 @@ module RecursionTermLifter (S: Spec) and module C = S.C = (* two global invariants: - - V -> G + - S.V -> S.G Needed to store the previously built global invariants - - fundec -> Map (S.C) (Set (fundec * S.C)) - The second global invariant maps from the callee fundec to a map, containing the callee context and the caller fundec and context. + - fundec * S.C -> (Set (fundec * S.C)) + The second global invariant maps from the callee fundec and context to a set of caller fundecs and contexts. This structure therefore stores the context-sensitive call graph. For example: let the function f in context c call function g in context c'. - In the global invariant structure it would be stored like this: g -> {c' -> {(f, c)}} + In the global invariant structure it would be stored like this: (g,c') -> {(f, c)} *) struct From 515a8bd2f300fdfe2134d3608fab52c9ca56a6f4 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 12 Oct 2023 16:55:08 +0200 Subject: [PATCH 0870/1312] Simplify cycle detection --- src/framework/constraints.ml | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index b4b72146d5..ad35ef2633 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1764,35 +1764,33 @@ struct sideg = (fun v g -> ctx.sideg (V.spec v) (G.create_spec g)); } - let cycleDetection ctx v v' = + let cycleDetection ctx call = let module LH = Hashtbl.Make (Printable.Prod (CilType.Fundec) (S.C)) in let module LS = Set.Make (Printable.Prod (CilType.Fundec) (S.C)) in (* find all cycles/SCCs *) let global_visited_calls = LH.create 100 in (* DFS *) - let rec iter_call (path_visited_calls: LS.t) (call:Printable.Prod (CilType.Fundec) (S.C).t) = - let ((fundec_e:fundec), (context_e: C.t)) = call in (*unpack tuple for later use*) + let rec iter_call (path_visited_calls: LS.t) ((fundec, _) as call) = if LS.mem call path_visited_calls then ( AnalysisState.svcomp_may_not_terminate := true; (*set the indicator for a non-terminating program for the sv comp*) (*Cycle found*) let msgs = [ - (Pretty.dprintf "The program might not terminate! (Fundec %a is contained in a call graph cycle)" CilType.Fundec.pretty fundec_e, Some (M.Location.CilLocation fundec_e.svar.vdecl)); + (Pretty.dprintf "The program might not terminate! (Fundec %a is contained in a call graph cycle)" CilType.Fundec.pretty fundec, Some (M.Location.CilLocation fundec.svar.vdecl)); ] in M.msg_group Warning ~category:NonTerminating "Recursion cycle" msgs) (* output a warning for non-termination*) else if not (LH.mem global_visited_calls call) then begin LH.replace global_visited_calls call (); let new_path_visited_calls = LS.add call path_visited_calls in - let gvar = V.call (fundec_e, context_e) in + let gvar = V.call call in let callers = G.callers (ctx.global gvar) in CallerSet.iter (fun to_call -> iter_call new_path_visited_calls to_call ) callers; end in - let callers = G.callers (ctx.global v) in - CallerSet.iter (iter_call LS.empty) callers + iter_call LS.empty call let query ctx (type a) (q: a Queries.t): a Queries.result = match q with @@ -1804,7 +1802,7 @@ struct begin match v with | `Left v' -> S.query (conv ctx) (WarnGlobal (Obj.repr v')) - | `Right v' -> cycleDetection ctx v v' (* Note: to make it more efficient, one could only execute the cycle detection in case the loop analysis returns true, because otherwise the program will probably not terminate anyway*) + | `Right call -> cycleDetection ctx call (* Note: to make it more efficient, one could only execute the cycle detection in case the loop analysis returns true, because otherwise the program will probably not terminate anyway*) end | InvariantGlobal v -> let v: V.t = Obj.obj v in From b3016ff054bd714d7c9f9527a25226b2a1f4f06b Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 12 Oct 2023 17:05:15 +0200 Subject: [PATCH 0871/1312] Failwith meaningful message for invaliud calls to `__goblint_bounded` --- src/analyses/loopTermination.ml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/analyses/loopTermination.ml b/src/analyses/loopTermination.ml index bfc600f830..61034a57c0 100644 --- a/src/analyses/loopTermination.ml +++ b/src/analyses/loopTermination.ml @@ -72,9 +72,7 @@ struct M.msg_group Warning ~category:NonTerminating "Possibly non terminating loops" msgs); () with Not_found -> - (* This should not happen as long as __goblint_bounded is only used - * for this analysis. *) - ()) + failwith "Encountered a call to __goblint_bounded with an unknown loop counter variable.") | _ -> () else () From fe369158efccfe1c531429f18e03b79b49dab38e Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sat, 14 Oct 2023 11:04:13 +0200 Subject: [PATCH 0872/1312] Add `AnalysisStateUtil` to `goblint_lib.ml` (#1201) --- src/goblint_lib.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/goblint_lib.ml b/src/goblint_lib.ml index dadeb2cda1..a71a0c9684 100644 --- a/src/goblint_lib.ml +++ b/src/goblint_lib.ml @@ -23,6 +23,7 @@ module CfgTools = CfgTools module Analyses = Analyses module Constraints = Constraints module AnalysisState = AnalysisState +module AnalysisStateUtil = AnalysisStateUtil module ControlSpecC = ControlSpecC (** Master control program (MCP) is the analysis specification for the dynamic product of activated analyses. *) From 14c3ead5c61242b46be22b2c100082f66454d95e Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sat, 14 Oct 2023 11:19:33 +0200 Subject: [PATCH 0873/1312] Merge branch 'master' into term --- scripts/goblint-lib-modules.py | 26 ++- src/analyses/base.ml | 117 ++++++++++-- src/analyses/memLeak.ml | 16 +- src/analyses/memOutOfBounds.ml | 171 ++++++++++++++---- src/analyses/useAfterFree.ml | 43 ++--- src/autoTune.ml | 40 +++- src/cdomains/arrayDomain.ml | 5 + src/common/framework/analysisState.ml | 5 +- src/common/util/cilfacade.ml | 3 +- src/common/util/options.schema.json | 6 + src/goblint_lib.ml | 1 + src/maingoblint.ml | 3 + src/util/analysisStateUtil.ml | 13 ++ src/witness/svcomp.ml | 2 + src/witness/svcompSpec.ml | 26 ++- src/witness/witness.ml | 45 +---- .../01-oob-heap-simple.c | 0 .../02-conditional-uaf.c | 0 .../03-nested-ptr-uaf.c | 0 .../04-function-call-uaf.c | 0 .../05-oob-implicit-deref.c | 0 .../06-memset-oob.c | 0 .../07-memcpy-oob.c | 4 +- .../08-memset-memcpy-array.c | 7 +- .../09-juliet-uaf.c | 0 .../74-invalid_deref/10-oob-two-loops.c | 22 +++ .../74-invalid_deref/11-address-offset-oob.c | 16 ++ .../74-invalid_deref/12-memcpy-oob-src.c | 43 +++++ .../13-mem-oob-packed-struct.c | 33 ++++ .../14-alloca-uaf.c | 0 .../15-juliet-uaf-global-var.c | 22 +++ .../74-invalid_deref/16-uaf-packed-struct.c | 40 ++++ .../74-invalid_deref/17-scopes-no-static.c | 22 +++ .../18-simple-uaf.c} | 0 .../19-oob-stack-simple.c} | 0 .../74-invalid_deref/20-scopes-global-var.c | 29 +++ .../21-oob-loop.c} | 0 .../74-invalid_deref/22-scopes-static.c | 52 ++++++ .../23-oob-deref-after-ptr-arith.c} | 0 .../24-uaf-free-in-wrapper-fun.c} | 0 .../25-uaf-struct.c} | 0 .../26-memset-memcpy-addr-offs.c} | 0 .../27-wrapper-funs-uaf.c} | 0 .../28-multi-threaded-uaf.c} | 0 ...9-multi-threaded-uaf-with-joined-thread.c} | 0 .../01-invalid-dealloc-simple.c | 0 .../02-invalid-dealloc-struct.c | 0 .../03-invalid-dealloc-array.c | 0 .../04-invalid-realloc.c | 0 .../05-free-at-offset.c | 0 .../06-realloc-at-offset.c | 0 .../07-free-at-struct-offset.c | 0 .../08-itc-no-double-free.c | 0 .../09-juliet-invalid-dealloc-alloca.c | 0 .../10-invalid-dealloc-union.c | 42 +++++ .../11-itc-double-free.c} | 0 .../12-realloc-at-struct-offset.c} | 0 .../13-juliet-double-free.c} | 0 tests/sv-comp/valid-memcleanup.prp | 2 + 59 files changed, 707 insertions(+), 149 deletions(-) create mode 100644 src/util/analysisStateUtil.ml rename tests/regression/{77-mem-oob => 74-invalid_deref}/01-oob-heap-simple.c (100%) rename tests/regression/{74-use_after_free => 74-invalid_deref}/02-conditional-uaf.c (100%) rename tests/regression/{74-use_after_free => 74-invalid_deref}/03-nested-ptr-uaf.c (100%) rename tests/regression/{74-use_after_free => 74-invalid_deref}/04-function-call-uaf.c (100%) rename tests/regression/{77-mem-oob => 74-invalid_deref}/05-oob-implicit-deref.c (100%) rename tests/regression/{77-mem-oob => 74-invalid_deref}/06-memset-oob.c (100%) rename tests/regression/{77-mem-oob => 74-invalid_deref}/07-memcpy-oob.c (94%) rename tests/regression/{77-mem-oob => 74-invalid_deref}/08-memset-memcpy-array.c (96%) rename tests/regression/{74-use_after_free => 74-invalid_deref}/09-juliet-uaf.c (100%) create mode 100644 tests/regression/74-invalid_deref/10-oob-two-loops.c create mode 100644 tests/regression/74-invalid_deref/11-address-offset-oob.c create mode 100644 tests/regression/74-invalid_deref/12-memcpy-oob-src.c create mode 100644 tests/regression/74-invalid_deref/13-mem-oob-packed-struct.c rename tests/regression/{74-use_after_free => 74-invalid_deref}/14-alloca-uaf.c (100%) create mode 100644 tests/regression/74-invalid_deref/15-juliet-uaf-global-var.c create mode 100644 tests/regression/74-invalid_deref/16-uaf-packed-struct.c create mode 100644 tests/regression/74-invalid_deref/17-scopes-no-static.c rename tests/regression/{74-use_after_free/01-simple-uaf.c => 74-invalid_deref/18-simple-uaf.c} (100%) rename tests/regression/{77-mem-oob/02-oob-stack-simple.c => 74-invalid_deref/19-oob-stack-simple.c} (100%) create mode 100644 tests/regression/74-invalid_deref/20-scopes-global-var.c rename tests/regression/{77-mem-oob/03-oob-loop.c => 74-invalid_deref/21-oob-loop.c} (100%) create mode 100644 tests/regression/74-invalid_deref/22-scopes-static.c rename tests/regression/{77-mem-oob/04-oob-deref-after-ptr-arith.c => 74-invalid_deref/23-oob-deref-after-ptr-arith.c} (100%) rename tests/regression/{74-use_after_free/05-uaf-free-in-wrapper-fun.c => 74-invalid_deref/24-uaf-free-in-wrapper-fun.c} (100%) rename tests/regression/{74-use_after_free/06-uaf-struct.c => 74-invalid_deref/25-uaf-struct.c} (100%) rename tests/regression/{77-mem-oob/09-memset-memcpy-addr-offs.c => 74-invalid_deref/26-memset-memcpy-addr-offs.c} (100%) rename tests/regression/{74-use_after_free/11-wrapper-funs-uaf.c => 74-invalid_deref/27-wrapper-funs-uaf.c} (100%) rename tests/regression/{74-use_after_free/12-multi-threaded-uaf.c => 74-invalid_deref/28-multi-threaded-uaf.c} (100%) rename tests/regression/{74-use_after_free/13-multi-threaded-uaf-with-joined-thread.c => 74-invalid_deref/29-multi-threaded-uaf-with-joined-thread.c} (100%) rename tests/regression/{75-invalid_dealloc => 75-invalid_free}/01-invalid-dealloc-simple.c (100%) rename tests/regression/{75-invalid_dealloc => 75-invalid_free}/02-invalid-dealloc-struct.c (100%) rename tests/regression/{75-invalid_dealloc => 75-invalid_free}/03-invalid-dealloc-array.c (100%) rename tests/regression/{75-invalid_dealloc => 75-invalid_free}/04-invalid-realloc.c (100%) rename tests/regression/{75-invalid_dealloc => 75-invalid_free}/05-free-at-offset.c (100%) rename tests/regression/{75-invalid_dealloc => 75-invalid_free}/06-realloc-at-offset.c (100%) rename tests/regression/{75-invalid_dealloc => 75-invalid_free}/07-free-at-struct-offset.c (100%) rename tests/regression/{74-use_after_free => 75-invalid_free}/08-itc-no-double-free.c (100%) rename tests/regression/{75-invalid_dealloc => 75-invalid_free}/09-juliet-invalid-dealloc-alloca.c (100%) create mode 100644 tests/regression/75-invalid_free/10-invalid-dealloc-union.c rename tests/regression/{74-use_after_free/07-itc-double-free.c => 75-invalid_free/11-itc-double-free.c} (100%) rename tests/regression/{75-invalid_dealloc/08-realloc-at-struct-offset.c => 75-invalid_free/12-realloc-at-struct-offset.c} (100%) rename tests/regression/{74-use_after_free/10-juliet-double-free.c => 75-invalid_free/13-juliet-double-free.c} (100%) create mode 100644 tests/sv-comp/valid-memcleanup.prp diff --git a/scripts/goblint-lib-modules.py b/scripts/goblint-lib-modules.py index 342f9a76bd..5f02271616 100755 --- a/scripts/goblint-lib-modules.py +++ b/scripts/goblint-lib-modules.py @@ -6,16 +6,20 @@ src_root_path = Path("./src") -goblint_lib_path = src_root_path / "goblint_lib.ml" +goblint_lib_paths = [ + src_root_path / "goblint_lib.ml", + src_root_path / "util" / "std" / "goblint_std.ml", +] goblint_lib_modules = set() -with goblint_lib_path.open() as goblint_lib_file: - for line in goblint_lib_file: - line = line.strip() - m = re.match(r"module (.*) = .*", line) - if m is not None: - module_name = m.group(1) - goblint_lib_modules.add(module_name) +for goblint_lib_path in goblint_lib_paths: + with goblint_lib_path.open() as goblint_lib_file: + for line in goblint_lib_file: + line = line.strip() + m = re.match(r"module (.*) = .*", line) + if m is not None: + module_name = m.group(1) + goblint_lib_modules.add(module_name) src_vendor_path = src_root_path / "vendor" exclude_module_names = set([ @@ -29,15 +33,21 @@ "Mainspec", # libraries + "Goblint_std", "Goblint_timing", "Goblint_backtrace", "Goblint_sites", "Goblint_build_info", + "Dune_build_info", "MessageCategory", # included in Messages "PreValueDomain", # included in ValueDomain "SpecCore", # spec stuff "SpecUtil", # spec stuff + + "ConfigVersion", + "ConfigProfile", + "ConfigOcaml", ]) src_modules = set() diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 7b87d3ff51..908dc88401 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1043,10 +1043,24 @@ struct | Mem n, ofs -> begin match (eval_rv a gs st n) with | Address adr -> - (if AD.is_null adr - then M.error ~category:M.Category.Behavior.Undefined.nullpointer_dereference ~tags:[CWE 476] "Must dereference NULL pointer" - else if AD.may_be_null adr - then M.warn ~category:M.Category.Behavior.Undefined.nullpointer_dereference ~tags:[CWE 476] "May dereference NULL pointer"); + ( + if AD.is_null adr then ( + AnalysisStateUtil.set_mem_safety_flag InvalidDeref; + M.error ~category:M.Category.Behavior.Undefined.nullpointer_dereference ~tags:[CWE 476] "Must dereference NULL pointer" + ) + else if AD.may_be_null adr then ( + AnalysisStateUtil.set_mem_safety_flag InvalidDeref; + M.warn ~category:M.Category.Behavior.Undefined.nullpointer_dereference ~tags:[CWE 476] "May dereference NULL pointer" + ); + (* Warn if any of the addresses contains a non-local and non-global variable *) + if AD.exists (function + | AD.Addr.Addr (v, _) -> not (CPA.mem v st.cpa) && not (is_global a v) + | _ -> false + ) adr then ( + AnalysisStateUtil.set_mem_safety_flag InvalidDeref; + M.warn "lval %a points to a non-local variable. Invalid pointer dereference may occur" d_lval lval + ) + ); AD.map (add_offset_varinfo (convert_offset a gs st ofs)) adr | _ -> M.debug ~category:Analyzer "Failed evaluating %a to lvalue" d_lval lval; @@ -2023,14 +2037,78 @@ struct in match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local ptr with | Address a -> - if AD.is_top a then + if AD.is_top a then ( + AnalysisStateUtil.set_mem_safety_flag InvalidFree; M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Points-to set for pointer %a in function %s is top. Potentially invalid memory deallocation may occur" d_exp ptr special_fn.vname - else if has_non_heap_var a then + ) else if has_non_heap_var a then ( + AnalysisStateUtil.set_mem_safety_flag InvalidFree; M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Free of non-dynamically allocated memory in function %s for pointer %a" special_fn.vname d_exp ptr - else if has_non_zero_offset a then + ) else if has_non_zero_offset a then ( + AnalysisStateUtil.set_mem_safety_flag InvalidFree; M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 761] "Free of memory not at start of buffer in function %s for pointer %a" special_fn.vname d_exp ptr - | _ -> M.warn ~category:MessageCategory.Analyzer "Pointer %a in function %s doesn't evaluate to a valid address." d_exp ptr special_fn.vname + ) + | _ -> + AnalysisStateUtil.set_mem_safety_flag InvalidFree; + M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Pointer %a in function %s doesn't evaluate to a valid address. Invalid memory deallocation may occur" d_exp ptr special_fn.vname + + let points_to_heap_only ctx ptr = + match ctx.ask (Queries.MayPointTo ptr) with + | a when not (Queries.AD.is_top a)-> + Queries.AD.for_all (function + | Addr (v, _) -> ctx.ask (Queries.IsHeapVar v) + | _ -> false + ) a + | _ -> false + let get_size_of_ptr_target ctx ptr = + let intdom_of_int x = + ID.of_int (Cilfacade.ptrdiff_ikind ()) (Z.of_int x) + in + let size_of_type_in_bytes typ = + let typ_size_in_bytes = (bitsSizeOf typ) / 8 in + intdom_of_int typ_size_in_bytes + in + if points_to_heap_only ctx ptr then + (* Ask for BlobSize from the base address (the second component being set to true) in order to avoid BlobSize giving us bot *) + ctx.ask (Queries.BlobSize {exp = ptr; base_address = true}) + else + match ctx.ask (Queries.MayPointTo ptr) with + | a when not (Queries.AD.is_top a) -> + let pts_list = Queries.AD.elements a in + let pts_elems_to_sizes (addr: Queries.AD.elt) = + begin match addr with + | Addr (v, _) -> + begin match v.vtype with + | TArray (item_typ, _, _) -> + let item_typ_size_in_bytes = size_of_type_in_bytes item_typ in + begin match ctx.ask (Queries.EvalLength ptr) with + | `Lifted arr_len -> + let arr_len_casted = ID.cast_to (Cilfacade.ptrdiff_ikind ()) arr_len in + begin + try `Lifted (ID.mul item_typ_size_in_bytes arr_len_casted) + with IntDomain.ArithmeticOnIntegerBot _ -> `Bot + end + | `Bot -> `Bot + | `Top -> `Top + end + | _ -> + let type_size_in_bytes = size_of_type_in_bytes v.vtype in + `Lifted type_size_in_bytes + end + | _ -> `Top + end + in + (* Map each points-to-set element to its size *) + let pts_sizes = List.map pts_elems_to_sizes pts_list in + (* Take the smallest of all sizes that ptr's contents may have *) + begin match pts_sizes with + | [] -> `Bot + | [x] -> x + | x::xs -> List.fold_left ValueDomainQueries.ID.join x xs + end + | _ -> + (M.warn "Pointer %a has a points-to-set of top. An invalid memory access might occur" d_exp ptr; + `Top) let special ctx (lv:lval option) (f: varinfo) (args: exp list) = let invalidate_ret_lv st = match lv with @@ -2050,13 +2128,28 @@ struct let st: store = ctx.local in let gs = ctx.global in let desc = LF.find f in - let memory_copying dst src = + let memory_copying dst src n = + let dest_size = get_size_of_ptr_target ctx dst in + let n_intdom = Option.map_default (fun exp -> ctx.ask (Queries.EvalInt exp)) `Bot n in + let dest_size_equal_n = + match dest_size, n_intdom with + | `Lifted ds, `Lifted n -> + let casted_ds = ID.cast_to (Cilfacade.ptrdiff_ikind ()) ds in + let casted_n = ID.cast_to (Cilfacade.ptrdiff_ikind ()) n in + let ds_eq_n = + begin try ID.eq casted_ds casted_n + with IntDomain.ArithmeticOnIntegerBot _ -> ID.top_of @@ Cilfacade.ptrdiff_ikind () + end + in + Option.default false (ID.to_bool ds_eq_n) + | _ -> false + in let dest_a, dest_typ = addr_type_of_exp dst in let src_lval = mkMem ~addr:(Cil.stripCasts src) ~off:NoOffset in let src_typ = eval_lv (Analyses.ask_of_ctx ctx) gs st src_lval |> AD.type_of in (* when src and destination type coincide, take value from the source, otherwise use top *) - let value = if typeSig dest_typ = typeSig src_typ then + let value = if (typeSig dest_typ = typeSig src_typ) && dest_size_equal_n then let src_cast_lval = mkMem ~addr:(Cilfacade.mkCast ~e:src ~newt:(TPtr (dest_typ, []))) ~off:NoOffset in eval_rv (Analyses.ask_of_ctx ctx) gs st (Lval src_cast_lval) else @@ -2117,13 +2210,13 @@ struct let value = VD.zero_init_value dest_typ in set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value | Memcpy { dest = dst; src; n; }, _ -> (* TODO: use n *) - memory_copying dst src + memory_copying dst src (Some n) (* strcpy(dest, src); *) | Strcpy { dest = dst; src; n = None }, _ -> let dest_a, dest_typ = addr_type_of_exp dst in (* when dest surely isn't a string literal, try copying src to dest *) if AD.string_writing_defined dest_a then - memory_copying dst src + memory_copying dst src None else (* else return top (after a warning was issued) *) set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ (VD.top_value (unrollType dest_typ)) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index 8576096dfe..dbaa2d69fc 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -3,6 +3,7 @@ open GoblintCil open Analyses open MessageCategory +open AnalysisStateUtil module ToppedVarInfoSet = SetDomain.ToppedSet(CilType.Varinfo)(struct let topname = "All Heap Variables" end) @@ -19,15 +20,24 @@ struct (* HELPER FUNCTIONS *) let warn_for_multi_threaded ctx = - if not (ctx.ask (Queries.MustBeSingleThreaded { since_start = true })) then + if not (ctx.ask (Queries.MustBeSingleThreaded { since_start = true })) then ( + set_mem_safety_flag InvalidMemTrack; + set_mem_safety_flag InvalidMemcleanup; M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Program isn't running in single-threaded mode. A memory leak might occur due to multi-threading" + ) let check_for_mem_leak ?(assert_exp_imprecise = false) ?(exp = None) ctx = let state = ctx.local in if not @@ D.is_empty state then match assert_exp_imprecise, exp with - | true, Some exp -> M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "assert expression %a is unknown. Memory leak might possibly occur for heap variables: %a" d_exp exp D.pretty state - | _ -> M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory leak detected for heap variables: %a" D.pretty state + | true, Some exp -> + set_mem_safety_flag InvalidMemTrack; + set_mem_safety_flag InvalidMemcleanup; + M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "assert expression %a is unknown. Memory leak might possibly occur for heap variables: %a" d_exp exp D.pretty state + | _ -> + set_mem_safety_flag InvalidMemTrack; + set_mem_safety_flag InvalidMemcleanup; + M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory leak detected for heap variables: %a" D.pretty state (* TRANSFER FUNCTIONS *) let return ctx (exp:exp option) (f:fundec) : D.t = diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index c715a1d2e7..fc60352298 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -3,6 +3,7 @@ open GoblintCil open Analyses open MessageCategory +open AnalysisStateUtil module AS = AnalysisState module VDQ = ValueDomainQueries @@ -88,6 +89,10 @@ struct let pts_elems_to_sizes (addr: Queries.AD.elt) = begin match addr with | Addr (v, _) -> + if hasAttribute "goblint_cil_nested" v.vattr then ( + set_mem_safety_flag InvalidDeref; + M.warn "Var %a is potentially accessed out-of-scope. Invalid memory access may occur" CilType.Varinfo.pretty v + ); begin match v.vtype with | TArray (item_typ, _, _) -> let item_typ_size_in_bytes = size_of_type_in_bytes item_typ in @@ -117,8 +122,9 @@ struct | x::xs -> List.fold_left VDQ.ID.join x xs end | _ -> - M.warn "Pointer %a has a points-to-set of top. An invalid memory access might occur" d_exp ptr; - `Top + (set_mem_safety_flag InvalidDeref; + M.warn "Pointer %a has a points-to-set of top. An invalid memory access might occur" d_exp ptr; + `Top) let get_ptr_deref_type ptr_typ = match ptr_typ with @@ -159,6 +165,50 @@ struct with IntDomain.ArithmeticOnIntegerBot _ -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () end + let cil_offs_to_idx ctx typ offs = + (* TODO: Some duplication with convert_offset in base.ml, unclear how to immediately get more reuse *) + let rec convert_offset (ofs: offset) = + match ofs with + | NoOffset -> `NoOffset + | Field (fld, ofs) -> `Field (fld, convert_offset ofs) + | Index (exp, ofs) when CilType.Exp.equal exp Offset.Index.Exp.any -> (* special offset added by convertToQueryLval *) + `Index (ID.top (), convert_offset ofs) + | Index (exp, ofs) -> + let i = match ctx.ask (Queries.EvalInt exp) with + | `Lifted x -> x + | _ -> ID.top_of @@ Cilfacade.ptrdiff_ikind () + in + `Index (i, convert_offset ofs) + in + PreValueDomain.Offs.to_index (convert_offset offs) + + + let check_unknown_addr_deref ctx ptr = + let may_contain_unknown_addr = + match ctx.ask (Queries.EvalValue ptr) with + | a when not (Queries.VD.is_top a) -> + begin match a with + | Address a -> ValueDomain.AD.may_be_unknown a + | _ -> false + end + (* Intuition: if ptr evaluates to top, it could potentially evaluate to the unknown address *) + | _ -> true + in + if may_contain_unknown_addr then begin + set_mem_safety_flag InvalidDeref; + M.warn ~category:(Behavior (Undefined Other)) "Pointer %a contains an unknown address. Invalid dereference may occur" d_exp ptr + end + + let ptr_only_has_str_addr ctx ptr = + match ctx.ask (Queries.EvalValue ptr) with + | a when not (Queries.VD.is_top a) -> + begin match a with + | Address a -> ValueDomain.AD.for_all (fun addr -> match addr with | StrPtr _ -> true | _ -> false) a + | _ -> false + end + (* Intuition: if ptr evaluates to top, it could all sorts of things and not only string addresses *) + | _ -> false + let rec get_addr_offs ctx ptr = match ctx.ask (Queries.MayPointTo ptr) with | a when not (VDQ.AD.is_top a) -> @@ -174,22 +224,25 @@ struct | Addr (_, o) -> ID.is_bot @@ offs_to_idx t o | _ -> false ) a then ( - (* TODO: Uncomment once staging-memsafety branch changes are applied *) - (* set_mem_safety_flag InvalidDeref; *) + set_mem_safety_flag InvalidDeref; M.warn "Pointer %a has a bot address offset. An invalid memory access may occur" d_exp ptr ) else if VDQ.AD.exists (function - | Addr (_, o) -> ID.is_bot @@ offs_to_idx t o + | Addr (_, o) -> ID.is_top_of (Cilfacade.ptrdiff_ikind ()) (offs_to_idx t o) | _ -> false ) a then ( - (* TODO: Uncomment once staging-memsafety branch changes are applied *) - (* set_mem_safety_flag InvalidDeref; *) + set_mem_safety_flag InvalidDeref; M.warn "Pointer %a has a top address offset. An invalid memory access may occur" d_exp ptr ); - (* Offset should be the same for all elements in the points-to set *) - (* Hence, we can just pick one element and obtain its offset *) - begin match VDQ.AD.choose a with - | Addr (_, o) -> offs_to_idx t o - | _ -> ID.top_of @@ Cilfacade.ptrdiff_ikind () + (* Get the address offsets of all points-to set elements *) + let addr_offsets = + VDQ.AD.filter (function Addr (v, o) -> true | _ -> false) a + |> VDQ.AD.to_mval + |> List.map (fun (_, o) -> offs_to_idx t o) + in + begin match addr_offsets with + | [] -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () + | [x] -> x + | x::xs -> List.fold_left ID.join x xs end end | None -> @@ -197,18 +250,55 @@ struct ID.top_of @@ Cilfacade.ptrdiff_ikind () end | _ -> + set_mem_safety_flag InvalidDeref; M.warn "Pointer %a has a points-to-set of top. An invalid memory access might occur" d_exp ptr; ID.top_of @@ Cilfacade.ptrdiff_ikind () and check_lval_for_oob_access ctx ?(is_implicitly_derefed = false) lval = - if not @@ lval_contains_a_ptr lval then () + (* If the lval does not contain a pointer or if it does contain a pointer, but only points to string addresses, then no need to WARN *) + if (not @@ lval_contains_a_ptr lval) || ptr_only_has_str_addr ctx (Lval lval) then () else (* If the lval doesn't indicate an explicit dereference, we still need to check for an implicit dereference *) (* An implicit dereference is, e.g., printf("%p", ptr), where ptr is a pointer *) match lval, is_implicitly_derefed with | (Var _, _), false -> () | (Var v, _), true -> check_no_binop_deref ctx (Lval lval) - | (Mem e, _), _ -> + | (Mem e, o), _ -> + let ptr_deref_type = get_ptr_deref_type @@ typeOf e in + let offs_intdom = begin match ptr_deref_type with + | Some t -> cil_offs_to_idx ctx t o + | None -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () + end in + let e_size = get_size_of_ptr_target ctx e in + let () = begin match e_size with + | `Top -> + (set_mem_safety_flag InvalidDeref; + M.warn "Size of lval dereference expression %a is top. Out-of-bounds memory access may occur" d_exp e) + | `Bot -> + (set_mem_safety_flag InvalidDeref; + M.warn "Size of lval dereference expression %a is bot. Out-of-bounds memory access may occur" d_exp e) + | `Lifted es -> + let casted_es = ID.cast_to (Cilfacade.ptrdiff_ikind ()) es in + let one = intdom_of_int 1 in + let casted_es = ID.sub casted_es one in + let casted_offs = ID.cast_to (Cilfacade.ptrdiff_ikind ()) offs_intdom in + let ptr_size_lt_offs = + begin try ID.lt casted_es casted_offs + with IntDomain.ArithmeticOnIntegerBot _ -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () + end + in + let behavior = Undefined MemoryOutOfBoundsAccess in + let cwe_number = 823 in + begin match ID.to_bool ptr_size_lt_offs with + | Some true -> + (set_mem_safety_flag InvalidDeref; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of lval dereference expression is %a (in bytes). It is offset by %a (in bytes). Memory out-of-bounds access must occur" ID.pretty casted_es ID.pretty casted_offs) + | Some false -> () + | None -> + (set_mem_safety_flag InvalidDeref; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Could not compare size of lval dereference expression (%a) (in bytes) with offset by (%a) (in bytes). Memory out-of-bounds access might occur" ID.pretty casted_es ID.pretty casted_offs) + end + end in begin match e with | Lval (Var v, _) as lval_exp -> check_no_binop_deref ctx lval_exp | BinOp (binop, e1, e2, t) when binop = PlusPI || binop = MinusPI || binop = IndexPI -> @@ -219,6 +309,7 @@ struct end and check_no_binop_deref ctx lval_exp = + check_unknown_addr_deref ctx lval_exp; let behavior = Undefined MemoryOutOfBoundsAccess in let cwe_number = 823 in let ptr_size = get_size_of_ptr_target ctx lval_exp in @@ -229,10 +320,10 @@ struct | Some t -> begin match ptr_size, addr_offs with | `Top, _ -> - AS.svcomp_may_invalid_deref := true; + set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer %a is top. Memory out-of-bounds access might occur due to pointer arithmetic" d_exp lval_exp | `Bot, _ -> - AS.svcomp_may_invalid_deref := true; + set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer %a is bot. Memory out-of-bounds access might occur due to pointer arithmetic" d_exp lval_exp | `Lifted ps, ao -> let casted_ps = ID.cast_to (Cilfacade.ptrdiff_ikind ()) ps in @@ -240,11 +331,11 @@ struct let ptr_size_lt_offs = ID.lt casted_ps casted_ao in begin match ID.to_bool ptr_size_lt_offs with | Some true -> - AnalysisState.svcomp_may_invalid_deref := true; + set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer is %a (in bytes). It is offset by %a (in bytes) due to pointer arithmetic. Memory out-of-bounds access must occur" ID.pretty casted_ps ID.pretty casted_ao | Some false -> () | None -> - AnalysisState.svcomp_may_invalid_deref := true; + set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Could not compare size of pointer (%a) (in bytes) with offset by (%a) (in bytes). Memory out-of-bounds access might occur" ID.pretty casted_ps ID.pretty casted_ao end end @@ -275,6 +366,7 @@ struct | AddrOf lval -> check_lval_for_oob_access ctx ~is_implicitly_derefed lval and check_binop_exp ctx binop e1 e2 t = + check_unknown_addr_deref ctx e1; let binopexp = BinOp (binop, e1, e2, t) in let behavior = Undefined MemoryOutOfBoundsAccess in let cwe_number = 823 in @@ -303,16 +395,16 @@ struct in begin match ptr_size, offset_size_with_addr_size with | `Top, _ -> - AS.svcomp_may_invalid_deref := true; + set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer %a in expression %a is top. Memory out-of-bounds access might occur" d_exp e1 d_exp binopexp | _, `Top -> - AS.svcomp_may_invalid_deref := true; + set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Operand value for pointer arithmetic in expression %a is top. Memory out-of-bounds access might occur" d_exp binopexp | `Bot, _ -> - AS.svcomp_may_invalid_deref := true; + set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer %a in expression %a is bottom. Memory out-of-bounds access might occur" d_exp e1 d_exp binopexp | _, `Bot -> - AS.svcomp_may_invalid_deref := true; + set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Operand value for pointer arithmetic in expression %a is bottom. Memory out-of-bounds access might occur" d_exp binopexp | `Lifted ps, `Lifted o -> let casted_ps = ID.cast_to (Cilfacade.ptrdiff_ikind ()) ps in @@ -320,11 +412,11 @@ struct let ptr_size_lt_offs = ID.lt casted_ps casted_o in begin match ID.to_bool ptr_size_lt_offs with | Some true -> - AS.svcomp_may_invalid_deref := true; + set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer in expression %a is %a (in bytes). It is offset by %a (in bytes). Memory out-of-bounds access must occur" d_exp binopexp ID.pretty casted_ps ID.pretty casted_o | Some false -> () | None -> - AnalysisState.svcomp_may_invalid_deref := true; + set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Could not compare pointer size (%a) with offset (%a). Memory out-of-bounds access may occur" ID.pretty casted_ps ID.pretty casted_o end end @@ -333,23 +425,24 @@ struct | _ -> () (* For memset() and memcpy() *) - let check_count ctx fun_name dest n = + let check_count ctx fun_name ptr n = let (behavior:MessageCategory.behavior) = Undefined MemoryOutOfBoundsAccess in let cwe_number = 823 in - let dest_size = get_size_of_ptr_target ctx dest in + let ptr_size = get_size_of_ptr_target ctx ptr in let eval_n = ctx.ask (Queries.EvalInt n) in - let addr_offs = get_addr_offs ctx dest in - match dest_size, eval_n with + let addr_offs = get_addr_offs ctx ptr in + match ptr_size, eval_n with | `Top, _ -> - AnalysisState.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest %a in function %s is unknown. Memory out-of-bounds access might occur" d_exp dest fun_name + set_mem_safety_flag InvalidDeref; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest %a in function %s is unknown. Memory out-of-bounds access might occur" d_exp ptr fun_name | _, `Top -> - AnalysisState.svcomp_may_invalid_deref := true; + set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Count parameter, passed to function %s is unknown. Memory out-of-bounds access might occur" fun_name | `Bot, _ -> - AnalysisState.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest %a in function %s is bottom. Memory out-of-bounds access might occur" d_exp dest fun_name + set_mem_safety_flag InvalidDeref; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest %a in function %s is bottom. Memory out-of-bounds access might occur" d_exp ptr fun_name | _, `Bot -> + set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Count parameter, passed to function %s is bottom" fun_name | `Lifted ds, `Lifted en -> let casted_ds = ID.cast_to (Cilfacade.ptrdiff_ikind ()) ds in @@ -358,11 +451,11 @@ struct let dest_size_lt_count = ID.lt casted_ds (ID.add casted_en casted_ao) in begin match ID.to_bool dest_size_lt_count with | Some true -> - AnalysisState.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest in function %s is %a (in bytes) with an address offset of %a (in bytes). Count is %a (in bytes). Memory out-of-bounds access must occur" fun_name ID.pretty casted_ds ID.pretty casted_ao ID.pretty casted_en + set_mem_safety_flag InvalidDeref; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of %a in function %s is %a (in bytes) with an address offset of %a (in bytes). Count is %a (in bytes). Memory out-of-bounds access must occur" d_exp ptr fun_name ID.pretty casted_ds ID.pretty casted_ao ID.pretty casted_en | Some false -> () | None -> - AnalysisState.svcomp_may_invalid_deref := true; + set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Could not compare size of dest (%a) with address offset (%a) count (%a) in function %s. Memory out-of-bounds access may occur" ID.pretty casted_ds ID.pretty casted_ao ID.pretty casted_en fun_name end @@ -396,7 +489,9 @@ struct (* Check calls to memset and memcpy for out-of-bounds-accesses *) match desc.special arglist with | Memset { dest; ch; count; } -> check_count ctx f.vname dest count; - | Memcpy { dest; src; n = count; } -> check_count ctx f.vname dest count; + | Memcpy { dest; src; n = count; } -> + (check_count ctx f.vname src count; + check_count ctx f.vname dest count;) | _ -> ctx.local let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = @@ -412,4 +507,4 @@ struct end let _ = - MCP.register_analysis (module Spec : MCPSpec) \ No newline at end of file + MCP.register_analysis (module Spec : MCPSpec) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index 02231336c0..ef63ab3e91 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -3,6 +3,7 @@ open GoblintCil open Analyses open MessageCategory +open AnalysisStateUtil module AllocaVars = SetDomain.ToppedSet(CilType.Varinfo)(struct let topname = "All alloca() Variables" end) module HeapVars = SetDomain.ToppedSet(CilType.Varinfo)(struct let topname = "All Heap Variables" end) @@ -14,7 +15,7 @@ module ThreadIdToJoinedThreadsMap = MapDomain.MapBot(ThreadIdDomain.ThreadLifted module Spec : Analyses.MCPSpec = struct - include Analyses.DefaultSpec + include Analyses.IdentitySpec let name () = "useAfterFree" @@ -23,18 +24,11 @@ struct module G = ThreadIdToJoinedThreadsMap module V = VarinfoV - (** TODO: Try out later in benchmarks to see how we perform with and without context-sensititivty *) let context _ _ = () (* HELPER FUNCTIONS *) - let set_global_svcomp_var is_double_free = - if is_double_free then - AnalysisState.svcomp_may_invalid_free := true - else - AnalysisState.svcomp_may_invalid_deref := true - let get_current_threadid ctx = ctx.ask Queries.CurrentThreadId @@ -70,23 +64,23 @@ struct | `Lifted current -> let possibly_started = G.exists (possibly_started current) freeing_threads in if possibly_started then begin - set_global_svcomp_var is_double_free; + if is_double_free then set_mem_safety_flag InvalidFree else set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "There's a thread that's been started in parallel with the memory-freeing threads for heap variable %a. %s might occur" CilType.Varinfo.pretty heap_var bug_name end else begin let current_is_unique = ThreadId.Thread.is_unique current in let any_equal_current threads = G.exists (equal_current current) threads in if not current_is_unique && any_equal_current freeing_threads then begin - set_global_svcomp_var is_double_free; + if is_double_free then set_mem_safety_flag InvalidFree else set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Current thread is not unique and a %s might occur for heap variable %a" bug_name CilType.Varinfo.pretty heap_var end else if HeapVars.mem heap_var (snd ctx.local) then begin - set_global_svcomp_var is_double_free; + if is_double_free then set_mem_safety_flag InvalidFree else set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "%s might occur in current unique thread %a for heap variable %a" bug_name ThreadIdDomain.FlagConfiguredTID.pretty current CilType.Varinfo.pretty heap_var end end | `Top -> - set_global_svcomp_var is_double_free; + if is_double_free then set_mem_safety_flag InvalidFree else set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "CurrentThreadId is top. %s might occur for heap variable %a" bug_name CilType.Varinfo.pretty heap_var | `Bot -> M.warn ~category:MessageCategory.Analyzer "CurrentThreadId is bottom" @@ -115,8 +109,10 @@ struct begin match ctx.ask (Queries.MayPointTo lval_to_query) with | ad when not (Queries.AD.is_top ad) -> let warn_for_heap_var v = - if HeapVars.mem v (snd state) then + if HeapVars.mem v (snd state) then begin + if is_double_free then set_mem_safety_flag InvalidFree else set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "lval (%s) in \"%s\" points to a maybe freed memory region" v.vname transfer_fn_name + end in let pointed_to_heap_vars = Queries.AD.fold (fun addr vars -> @@ -179,9 +175,6 @@ struct warn_exp_might_contain_freed "branch" ctx exp; ctx.local - let body ctx (f:fundec) : D.t = - ctx.local - let return ctx (exp:exp option) (f:fundec) : D.t = Option.iter (fun x -> warn_exp_might_contain_freed "return" ctx x) exp; ctx.local @@ -189,17 +182,10 @@ struct let enter ctx (lval:lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = let caller_state = ctx.local in List.iter (fun arg -> warn_exp_might_contain_freed "enter" ctx arg) args; - if AllocaVars.is_empty (fst caller_state) && HeapVars.is_empty (snd caller_state) then - [caller_state, caller_state] - else ( - let reachable_from_args = List.fold_left (fun ad arg -> Queries.AD.join ad (ctx.ask (ReachableFrom arg))) (Queries.AD.empty ()) args in - if Queries.AD.is_top reachable_from_args || D.is_top caller_state then - [caller_state, caller_state] - else - let reachable_vars = Queries.AD.to_var_may reachable_from_args in - let callee_state = (AllocaVars.empty (), HeapVars.filter (fun var -> List.mem var reachable_vars) (snd caller_state)) in (* TODO: use AD.mem directly *) - [caller_state, callee_state] - ) + (* TODO: The 2nd component of the callee state needs to contain only the heap vars from the caller state which are reachable from: *) + (* * Global program variables *) + (* * The callee arguments *) + [caller_state, (AllocaVars.empty (), snd caller_state)] let combine_env ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (callee_local:D.t) (f_ask:Queries.ask) : D.t = let (caller_stack_state, caller_heap_state) = ctx.local in @@ -251,9 +237,6 @@ struct end | _ -> state - let threadenter ctx lval f args = [ctx.local] - let threadspawn ctx lval f args fctx = ctx.local - let startstate v = D.bot () let exitstate v = D.top () diff --git a/src/autoTune.ml b/src/autoTune.ml index e72764ceb6..9e89a18045 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -210,6 +210,38 @@ let activateLongjmpAnalysesWhenRequired () = enableAnalyses longjmpAnalyses; ) +let focusOnMemSafetySpecification () = + match Svcomp.Specification.of_option () with + | ValidFree -> (* Enable the useAfterFree analysis *) + let uafAna = ["useAfterFree"] in + print_endline @@ "Specification: ValidFree -> enabling useAfterFree analysis \"" ^ (String.concat ", " uafAna) ^ "\""; + enableAnalyses uafAna + | ValidDeref -> (* Enable the memOutOfBounds analysis *) + let memOobAna = ["memOutOfBounds"] in + print_endline "Setting \"cil.addNestedScopeAttr\" to true"; + set_bool "cil.addNestedScopeAttr" true; + print_endline @@ "Specification: ValidDeref -> enabling memOutOfBounds analysis \"" ^ (String.concat ", " memOobAna) ^ "\""; + enableAnalyses memOobAna + | ValidMemtrack + | ValidMemcleanup -> (* Enable the memLeak analysis *) + let memLeakAna = ["memLeak"] in + if (get_int "ana.malloc.unique_address_count") < 1 then ( + print_endline "Setting \"ana.malloc.unique_address_count\" to 1"; + set_int "ana.malloc.unique_address_count" 1; + ); + print_endline @@ "Specification: ValidMemtrack and ValidMemcleanup -> enabling memLeak analysis \"" ^ (String.concat ", " memLeakAna) ^ "\""; + enableAnalyses memLeakAna + | MemorySafety -> (* TODO: This is a temporary solution for the memory safety category *) + (print_endline "Setting \"cil.addNestedScopeAttr\" to true"; + set_bool "cil.addNestedScopeAttr" true; + if (get_int "ana.malloc.unique_address_count") < 1 then ( + print_endline "Setting \"ana.malloc.unique_address_count\" to 1"; + set_int "ana.malloc.unique_address_count" 1; + ); + let memSafetyAnas = ["memOutOfBounds"; "memLeak"; "useAfterFree";] in + enableAnalyses memSafetyAnas) + | _ -> () + let focusOnSpecification () = match Svcomp.Specification.of_option () with | UnreachCall s -> () @@ -220,13 +252,7 @@ let focusOnSpecification () = | NoOverflow -> (*We focus on integer analysis*) set_bool "ana.int.def_exc" true; set_bool "ana.int.interval" true - | ValidFree -> (* Enable the useAfterFree analysis *) - let uafAna = ["useAfterFree"] in - print_endline @@ "Specification: ValidFree -> enabling useAfterFree analysis \"" ^ (String.concat ", " uafAna) ^ "\""; - enableAnalyses uafAna - (* TODO: Finish these two below later *) - | ValidDeref - | ValidMemtrack -> () + | _ -> () (*Detect enumerations and enable the "ana.int.enums" option*) exception EnumFound diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index c099a94f96..2f91e47663 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -787,14 +787,19 @@ let array_oob_check ( type a ) (module Idx: IntDomain.Z with type t = a) (x, l) | Some true, Some true -> (* Certainly in bounds on both sides.*) () | Some true, Some false -> (* The following matching differentiates the must and may cases*) + AnalysisStateUtil.set_mem_safety_flag InvalidDeref; M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Must access array past end" | Some true, None -> + AnalysisStateUtil.set_mem_safety_flag InvalidDeref; M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "May access array past end" | Some false, Some true -> + AnalysisStateUtil.set_mem_safety_flag InvalidDeref; M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.before_start "Must access array before start" | None, Some true -> + AnalysisStateUtil.set_mem_safety_flag InvalidDeref; M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.before_start "May access array before start" | _ -> + AnalysisStateUtil.set_mem_safety_flag InvalidDeref; M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.unknown "May access array out of bounds" else () diff --git a/src/common/framework/analysisState.ml b/src/common/framework/analysisState.ml index 19377520cd..fd76e1bb67 100644 --- a/src/common/framework/analysisState.ml +++ b/src/common/framework/analysisState.ml @@ -15,9 +15,12 @@ let svcomp_may_invalid_free = ref false (** Whether an invalid pointer dereference happened *) let svcomp_may_invalid_deref = ref false -(** Whether an invalid memtrack happened *) +(** Whether a memory leak occurred and there's no reference to the leaked memory *) let svcomp_may_invalid_memtrack = ref false +(** Whether a memory leak occurred *) +let svcomp_may_invalid_memcleanup = ref false + (** A hack to see if we are currently doing global inits *) let global_initialization = ref false diff --git a/src/common/util/cilfacade.ml b/src/common/util/cilfacade.ml index 52d5f52f25..2075cda890 100644 --- a/src/common/util/cilfacade.ml +++ b/src/common/util/cilfacade.ml @@ -40,7 +40,8 @@ let is_first_field x = match x.fcomp.cfields with let init_options () = Mergecil.merge_inlines := get_bool "cil.merge.inlines"; Cil.cstd := Cil.cstd_of_string (get_string "cil.cstd"); - Cil.gnu89inline := get_bool "cil.gnu89inline" + Cil.gnu89inline := get_bool "cil.gnu89inline"; + Cabs2cil.addNestedScopeAttr := get_bool "cil.addNestedScopeAttr" let init () = initCIL (); diff --git a/src/common/util/options.schema.json b/src/common/util/options.schema.json index 3dd43530bd..e8510e86f3 100644 --- a/src/common/util/options.schema.json +++ b/src/common/util/options.schema.json @@ -288,6 +288,12 @@ "type": "boolean", "description": "Indicates whether gnu89 semantic should be used for inline functions.", "default": false + }, + "addNestedScopeAttr": { + "title": "cil.addNestedScopeAttr", + "type": "boolean", + "description": "Indicates whether variables that CIL pulls out of their scope should be marked.", + "default": false } }, "additionalProperties": false diff --git a/src/goblint_lib.ml b/src/goblint_lib.ml index dadeb2cda1..a71a0c9684 100644 --- a/src/goblint_lib.ml +++ b/src/goblint_lib.ml @@ -23,6 +23,7 @@ module CfgTools = CfgTools module Analyses = Analyses module Constraints = Constraints module AnalysisState = AnalysisState +module AnalysisStateUtil = AnalysisStateUtil module ControlSpecC = ControlSpecC (** Master control program (MCP) is the analysis specification for the dynamic product of activated analyses. *) diff --git a/src/maingoblint.ml b/src/maingoblint.ml index e80ae12661..d187fc70f0 100644 --- a/src/maingoblint.ml +++ b/src/maingoblint.ml @@ -135,6 +135,7 @@ let check_arguments () = if get_bool "allfuns" && not (get_bool "exp.earlyglobs") then (set_bool "exp.earlyglobs" true; warn "allfuns enables exp.earlyglobs.\n"); if not @@ List.mem "escape" @@ get_string_list "ana.activated" then warn "Without thread escape analysis, every local variable whose address is taken is considered escaped, i.e., global!"; if List.mem "malloc_null" @@ get_string_list "ana.activated" && not @@ get_bool "sem.malloc.fail" then (set_bool "sem.malloc.fail" true; warn "The malloc_null analysis enables sem.malloc.fail."); + if List.mem "memOutOfBounds" @@ get_string_list "ana.activated" && not @@ get_bool "cil.addNestedScopeAttr" then (set_bool "cil.addNestedScopeAttr" true; warn "The memOutOfBounds analysis enables cil.addNestedScopeAttr."); if get_bool "ana.base.context.int" && not (get_bool "ana.base.context.non-ptr") then (set_bool "ana.base.context.int" false; warn "ana.base.context.int implicitly disabled by ana.base.context.non-ptr"); (* order matters: non-ptr=false, int=true -> int=false cascades to interval=false with warning *) if get_bool "ana.base.context.interval" && not (get_bool "ana.base.context.int") then (set_bool "ana.base.context.interval" false; warn "ana.base.context.interval implicitly disabled by ana.base.context.int"); @@ -190,6 +191,8 @@ let handle_options () = check_arguments (); AfterConfig.run (); Sys.set_signal (GobSys.signal_of_string (get_string "dbg.solver-signal")) Signal_ignore; (* Ignore solver-signal before solving (e.g. MyCFG), otherwise exceptions self-signal the default, which crashes instead of printing backtrace. *) + if AutoTune.isActivated "specification" && get_string "ana.specification" <> "" then + AutoTune.focusOnMemSafetySpecification (); Cilfacade.init_options (); handle_flags () diff --git a/src/util/analysisStateUtil.ml b/src/util/analysisStateUtil.ml new file mode 100644 index 0000000000..a34be33f18 --- /dev/null +++ b/src/util/analysisStateUtil.ml @@ -0,0 +1,13 @@ +type mem_safety_violation = + | InvalidFree + | InvalidDeref + | InvalidMemTrack + | InvalidMemcleanup + +let set_mem_safety_flag violation_type = + if !AnalysisState.postsolving then + match violation_type with + | InvalidFree -> AnalysisState.svcomp_may_invalid_free := true + | InvalidDeref -> AnalysisState.svcomp_may_invalid_deref := true + | InvalidMemTrack -> AnalysisState.svcomp_may_invalid_memtrack := true + | InvalidMemcleanup -> AnalysisState.svcomp_may_invalid_memcleanup := true \ No newline at end of file diff --git a/src/witness/svcomp.ml b/src/witness/svcomp.ml index 6c050aa5b1..2e29993e91 100644 --- a/src/witness/svcomp.ml +++ b/src/witness/svcomp.ml @@ -56,6 +56,8 @@ struct | ValidFree -> "valid-free" | ValidDeref -> "valid-deref" | ValidMemtrack -> "valid-memtrack" + | MemorySafety -> "memory-safety" (* TODO: Currently here only to complete the pattern match *) + | ValidMemcleanup -> "valid-memcleanup" in "false(" ^ result_spec ^ ")" | Unknown -> "unknown" diff --git a/src/witness/svcompSpec.ml b/src/witness/svcompSpec.ml index 39a42cebae..de2d7c5283 100644 --- a/src/witness/svcompSpec.ml +++ b/src/witness/svcompSpec.ml @@ -10,10 +10,13 @@ type t = | ValidFree | ValidDeref | ValidMemtrack + | MemorySafety (* Internal property for use in Goblint; serves as a summary for ValidFree, ValidDeref and ValidMemtrack *) + | ValidMemcleanup let of_string s = let s = String.strip s in - let regexp = Str.regexp "CHECK( init(main()), LTL(G \\(.*\\)) )" in + let regexp_multiple = Str.regexp "CHECK( init(main()), LTL(G \\(.*\\)) )\nCHECK( init(main()), LTL(G \\(.*\\)) )\nCHECK( init(main()), LTL(G \\(.*\\)) )" in + let regexp_single = Str.regexp "CHECK( init(main()), LTL(G \\(.*\\)) )" in let regexp_negated = Str.regexp "CHECK( init(main()), LTL(G ! \\(.*\\)) )" in if Str.string_match regexp_negated s 0 then let global_not = Str.matched_group 1 s in @@ -30,14 +33,19 @@ let of_string s = UnreachCall f else failwith "Svcomp.Specification.of_string: unknown global not expression" - else if Str.string_match regexp s 0 then + else if Str.string_match regexp_multiple s 0 then + let global1 = Str.matched_group 1 s in + let global2 = Str.matched_group 2 s in + let global3 = Str.matched_group 3 s in + let mem_safety_props = ["valid-free"; "valid-deref"; "valid-memtrack";] in + if (global1 <> global2 && global1 <> global3 && global2 <> global3) && List.for_all (fun x -> List.mem x mem_safety_props) [global1; global2; global3] then + MemorySafety + else + failwith "Svcomp.Specification.of_string: unknown global expression" + else if Str.string_match regexp_single s 0 then let global = Str.matched_group 1 s in - if global = "valid-free" then - ValidFree - else if global = "valid-deref" then - ValidDeref - else if global = "valid-memtrack" then - ValidMemtrack + if global = "valid-memcleanup" then + ValidMemcleanup else failwith "Svcomp.Specification.of_string: unknown global expression" else @@ -69,5 +77,7 @@ let to_string spec = | ValidDeref -> "valid-deref", false | ValidMemtrack -> "valid-memtrack", false | Termination -> "no-termination", true + | MemorySafety -> "memory-safety", false (* TODO: That's false, it's currently here just to complete the pattern match *) + | ValidMemcleanup -> "valid-memcleanup", false in print_output spec_str is_neg diff --git a/src/witness/witness.ml b/src/witness/witness.ml index 2031f266f4..08a796c307 100644 --- a/src/witness/witness.ml +++ b/src/witness/witness.ml @@ -535,14 +535,17 @@ struct in (module TaskResult:WitnessTaskResult) ) - | ValidFree -> + | ValidFree + | ValidDeref + | ValidMemtrack + | MemorySafety -> let module TrivialArg = struct include Arg let next _ = [] end in - if not !AnalysisState.svcomp_may_invalid_free then + if not !AnalysisState.svcomp_may_invalid_free && not !AnalysisState.svcomp_may_invalid_deref && not !AnalysisState.svcomp_may_invalid_memtrack then ( let module TaskResult = struct module Arg = Arg @@ -553,37 +556,7 @@ struct end in (module TaskResult:WitnessTaskResult) - else ( - let module TaskResult = - struct - module Arg = TrivialArg - let result = Result.Unknown - let invariant _ = Invariant.none - let is_violation _ = false - let is_sink _ = false - end - in - (module TaskResult:WitnessTaskResult) - ) - | ValidDeref -> - let module TrivialArg = - struct - include Arg - let next _ = [] - end - in - if not !AnalysisState.svcomp_may_invalid_deref then - let module TaskResult = - struct - module Arg = Arg - let result = Result.True - let invariant _ = Invariant.none - let is_violation _ = false - let is_sink _ = false - end - in - (module TaskResult:WitnessTaskResult) - else ( + ) else ( let module TaskResult = struct module Arg = TrivialArg @@ -595,14 +568,14 @@ struct in (module TaskResult:WitnessTaskResult) ) - | ValidMemtrack -> + | ValidMemcleanup -> let module TrivialArg = struct include Arg let next _ = [] end in - if not !AnalysisState.svcomp_may_invalid_memtrack then + if not !AnalysisState.svcomp_may_invalid_memcleanup then ( let module TaskResult = struct module Arg = Arg @@ -613,7 +586,7 @@ struct end in (module TaskResult:WitnessTaskResult) - else ( + ) else ( let module TaskResult = struct module Arg = TrivialArg diff --git a/tests/regression/77-mem-oob/01-oob-heap-simple.c b/tests/regression/74-invalid_deref/01-oob-heap-simple.c similarity index 100% rename from tests/regression/77-mem-oob/01-oob-heap-simple.c rename to tests/regression/74-invalid_deref/01-oob-heap-simple.c diff --git a/tests/regression/74-use_after_free/02-conditional-uaf.c b/tests/regression/74-invalid_deref/02-conditional-uaf.c similarity index 100% rename from tests/regression/74-use_after_free/02-conditional-uaf.c rename to tests/regression/74-invalid_deref/02-conditional-uaf.c diff --git a/tests/regression/74-use_after_free/03-nested-ptr-uaf.c b/tests/regression/74-invalid_deref/03-nested-ptr-uaf.c similarity index 100% rename from tests/regression/74-use_after_free/03-nested-ptr-uaf.c rename to tests/regression/74-invalid_deref/03-nested-ptr-uaf.c diff --git a/tests/regression/74-use_after_free/04-function-call-uaf.c b/tests/regression/74-invalid_deref/04-function-call-uaf.c similarity index 100% rename from tests/regression/74-use_after_free/04-function-call-uaf.c rename to tests/regression/74-invalid_deref/04-function-call-uaf.c diff --git a/tests/regression/77-mem-oob/05-oob-implicit-deref.c b/tests/regression/74-invalid_deref/05-oob-implicit-deref.c similarity index 100% rename from tests/regression/77-mem-oob/05-oob-implicit-deref.c rename to tests/regression/74-invalid_deref/05-oob-implicit-deref.c diff --git a/tests/regression/77-mem-oob/06-memset-oob.c b/tests/regression/74-invalid_deref/06-memset-oob.c similarity index 100% rename from tests/regression/77-mem-oob/06-memset-oob.c rename to tests/regression/74-invalid_deref/06-memset-oob.c diff --git a/tests/regression/77-mem-oob/07-memcpy-oob.c b/tests/regression/74-invalid_deref/07-memcpy-oob.c similarity index 94% rename from tests/regression/77-mem-oob/07-memcpy-oob.c rename to tests/regression/74-invalid_deref/07-memcpy-oob.c index 012f92996e..5605404a87 100644 --- a/tests/regression/77-mem-oob/07-memcpy-oob.c +++ b/tests/regression/74-invalid_deref/07-memcpy-oob.c @@ -31,13 +31,13 @@ int main(int argc, char const *argv[]) { memcpy(a, b, 40); //WARN memcpy(a, b, sizeof(a)); //WARN - memcpy(b, a, 60); //NOWARN + memcpy(b, a, 60); //WARN b += 1; memcpy(b, a, 60); //WARN s *s_ptr = malloc(sizeof(s)); - memcpy(s_ptr, a, sizeof(s)); //NOWARN + memcpy(s_ptr, a, sizeof(s)); //WARN memcpy(s_ptr->a, 0, sizeof(s)); //WARN memcpy(s_ptr->b, 0, sizeof(s)); //WARN diff --git a/tests/regression/77-mem-oob/08-memset-memcpy-array.c b/tests/regression/74-invalid_deref/08-memset-memcpy-array.c similarity index 96% rename from tests/regression/77-mem-oob/08-memset-memcpy-array.c rename to tests/regression/74-invalid_deref/08-memset-memcpy-array.c index f231ba2dc4..210a61d459 100644 --- a/tests/regression/77-mem-oob/08-memset-memcpy-array.c +++ b/tests/regression/74-invalid_deref/08-memset-memcpy-array.c @@ -6,13 +6,14 @@ int main(int argc, char const *argv[]) { int arr[42]; // Size should be 168 bytes (with 4 byte ints) int *b = arr; - + int random; + memset(b, 0, 168); //NOWARN memset(b, 0, sizeof(arr)); //NOWARN memset(b, 0, 169); //WARN memset(b, 0, sizeof(arr) + 1); //WARN - + int *c = malloc(sizeof(arr)); // Size should be 168 bytes (with 4 byte ints) memcpy(b, c, 168); //NOWARN memcpy(b, c, sizeof(arr)); //NOWARN @@ -26,7 +27,7 @@ int main(int argc, char const *argv[]) { memset(b, 0, 168); //WARN memcpy(b, c, 168); //WARN } else if (*(argv + 5)) { - int random = rand(); + random = rand(); b = &random; memset(b, 0, 168); //WARN memcpy(b, c, 168); //WARN diff --git a/tests/regression/74-use_after_free/09-juliet-uaf.c b/tests/regression/74-invalid_deref/09-juliet-uaf.c similarity index 100% rename from tests/regression/74-use_after_free/09-juliet-uaf.c rename to tests/regression/74-invalid_deref/09-juliet-uaf.c diff --git a/tests/regression/74-invalid_deref/10-oob-two-loops.c b/tests/regression/74-invalid_deref/10-oob-two-loops.c new file mode 100644 index 0000000000..303aac242e --- /dev/null +++ b/tests/regression/74-invalid_deref/10-oob-two-loops.c @@ -0,0 +1,22 @@ +// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval --disable warn.info --set sem.int.signed_overflow assume_none +#include + +int main() { + int *p = malloc(1048 * sizeof(int)); + + for (int i = 0; i < 1048; ++i) { + p[i] = rand(); //NOWARN + } + + int *q = p; + + while (*q >= 0 && q < p + 1048 * sizeof(int)) { //WARN + if (rand()) { + q++; + } else { + (*q)--; //WARN + } + } + free(p); + return 0; +} diff --git a/tests/regression/74-invalid_deref/11-address-offset-oob.c b/tests/regression/74-invalid_deref/11-address-offset-oob.c new file mode 100644 index 0000000000..ba01a12873 --- /dev/null +++ b/tests/regression/74-invalid_deref/11-address-offset-oob.c @@ -0,0 +1,16 @@ +// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval --disable warn.info --set sem.int.signed_overflow assume_none +int main() { + int *p = malloc(2 * sizeof(int)); + int *q = p; + int x; + + if (x) { + q++; + q++; + q++; + x = *q; //WARN + } + + x = *q; //WARN + return 0; +} diff --git a/tests/regression/74-invalid_deref/12-memcpy-oob-src.c b/tests/regression/74-invalid_deref/12-memcpy-oob-src.c new file mode 100644 index 0000000000..0f3a609fbe --- /dev/null +++ b/tests/regression/74-invalid_deref/12-memcpy-oob-src.c @@ -0,0 +1,43 @@ +// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval --disable warn.info +// TODO: The "--disable warn.info" part is a temporary fix and needs to be removed once the MacOS CI job is fixed +#include +#include + +struct A { + unsigned char a; + unsigned char b:2; + unsigned char c:2; + unsigned char d:5; + unsigned char e; +} __attribute__((packed)); + +struct A d; +int main(void) +{ + struct A *p; + p = malloc(5); + d.a = 1; + d.b = 2; + d.c = 3; + d.d = 4; + d.e = 5; + // It's an OOB error, because sizeof(d) == 4 + memcpy(p, &d, 5); //WARN + if (p->a != 1) { + free(p); + } + if (p->b != 2) { + free(p); + } + if (p->c != 3) { + free(p); + } + if (p->d != 4) { + free(p); + } + if (p->e != 5) { + free(p); + } + free(p); +} + diff --git a/tests/regression/74-invalid_deref/13-mem-oob-packed-struct.c b/tests/regression/74-invalid_deref/13-mem-oob-packed-struct.c new file mode 100644 index 0000000000..552cd1bb0b --- /dev/null +++ b/tests/regression/74-invalid_deref/13-mem-oob-packed-struct.c @@ -0,0 +1,33 @@ +// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval +#include + +struct A { + unsigned char a; + unsigned char b:2; + unsigned char c:2; + unsigned char d; +} __attribute__((packed)); + +int main(void) +{ + struct A *p; + p = malloc(2); + p->a = 1; + if (p->a != 1) { + free(p); + } + p->b = 2; + if (p->b != 2) { + free(p); + } + p->c = 3; + if (p->c != 3) { + free(p); + } + p->d = 4; //WARN + if (p->d != 4) {//WARN + free(p); + } + free(p); +} + diff --git a/tests/regression/74-use_after_free/14-alloca-uaf.c b/tests/regression/74-invalid_deref/14-alloca-uaf.c similarity index 100% rename from tests/regression/74-use_after_free/14-alloca-uaf.c rename to tests/regression/74-invalid_deref/14-alloca-uaf.c diff --git a/tests/regression/74-invalid_deref/15-juliet-uaf-global-var.c b/tests/regression/74-invalid_deref/15-juliet-uaf-global-var.c new file mode 100644 index 0000000000..cc9819950f --- /dev/null +++ b/tests/regression/74-invalid_deref/15-juliet-uaf-global-var.c @@ -0,0 +1,22 @@ +//PARAM: --set ana.activated[+] useAfterFree +#include + +int *global; + +void other(void) +{ + int *data = global; + free((void *)data); + return; +} + +int main(int argc, char **argv) +{ + int *data = (int *)malloc(400UL); + free((void *)data); + + global = data; + other(); + + return 0; +} \ No newline at end of file diff --git a/tests/regression/74-invalid_deref/16-uaf-packed-struct.c b/tests/regression/74-invalid_deref/16-uaf-packed-struct.c new file mode 100644 index 0000000000..e10aa28486 --- /dev/null +++ b/tests/regression/74-invalid_deref/16-uaf-packed-struct.c @@ -0,0 +1,40 @@ +// PARAM: --set ana.activated[+] useAfterFree +#include +#include + +struct A { + unsigned char a; + unsigned char b:2; + unsigned char c:2; + unsigned char pad1[2]; + unsigned int d; + unsigned char e; + unsigned char pad2[3]; +} __attribute__((packed)); + +struct A d; +int main(void) +{ + struct A *p; + p = malloc(12); + d.a = 1; + d.b = 2; + d.c = 3; + d.d = 4; + d.e = 5; + memcpy(p, &d, 4); + if (p->a != 1) { + free(p); + } + if (p->b != 2) {//WARN + free(p);//WARN + } + if (p->c != 3) {//WARN + free(p);//WARN + } + if (p->d != 4) { //WARN + free(p);//WARN + } + free(p);//WARN +} + diff --git a/tests/regression/74-invalid_deref/17-scopes-no-static.c b/tests/regression/74-invalid_deref/17-scopes-no-static.c new file mode 100644 index 0000000000..e0c4b47b73 --- /dev/null +++ b/tests/regression/74-invalid_deref/17-scopes-no-static.c @@ -0,0 +1,22 @@ +// PARAM: --set ana.activated[+] memOutOfBounds +// TODO: I haven't checked why, but we need memOutOfBounds for this case +extern int printf ( const char * format, ... ); + +int *foo2(void) +{ + int arr[1024]; + arr[194] = 13; + return arr + 1; +} + +int *foo(void) +{ + int arr[123]; + return foo2(); +} + +int main(void) { + int *a = foo(); + printf("%d\n", *a);//WARN + return 0; +} diff --git a/tests/regression/74-use_after_free/01-simple-uaf.c b/tests/regression/74-invalid_deref/18-simple-uaf.c similarity index 100% rename from tests/regression/74-use_after_free/01-simple-uaf.c rename to tests/regression/74-invalid_deref/18-simple-uaf.c diff --git a/tests/regression/77-mem-oob/02-oob-stack-simple.c b/tests/regression/74-invalid_deref/19-oob-stack-simple.c similarity index 100% rename from tests/regression/77-mem-oob/02-oob-stack-simple.c rename to tests/regression/74-invalid_deref/19-oob-stack-simple.c diff --git a/tests/regression/74-invalid_deref/20-scopes-global-var.c b/tests/regression/74-invalid_deref/20-scopes-global-var.c new file mode 100644 index 0000000000..9491e1c574 --- /dev/null +++ b/tests/regression/74-invalid_deref/20-scopes-global-var.c @@ -0,0 +1,29 @@ +int array[10]; + +// function returns array of numbers +int* getNumbers(void) { + for (int i = 0; i < 10; ++i) { + array[i] = i;//NOWARN + } + + return array; +} + +int* getNumbers2(void) { + int* numbers = getNumbers(); + // numbers2 is local + int numbers2[10]; + + for (int i = 0; i < 10; ++i) { + numbers2[i] = numbers[i];//NOWARN + } + + return numbers2; +} + +int main(void) { + int *numbers = getNumbers2(); + numbers[0] = 100;//WARN + + return 0; +} diff --git a/tests/regression/77-mem-oob/03-oob-loop.c b/tests/regression/74-invalid_deref/21-oob-loop.c similarity index 100% rename from tests/regression/77-mem-oob/03-oob-loop.c rename to tests/regression/74-invalid_deref/21-oob-loop.c diff --git a/tests/regression/74-invalid_deref/22-scopes-static.c b/tests/regression/74-invalid_deref/22-scopes-static.c new file mode 100644 index 0000000000..c13b665c84 --- /dev/null +++ b/tests/regression/74-invalid_deref/22-scopes-static.c @@ -0,0 +1,52 @@ +extern int printf (const char* format, ...); + +// function returns array of numbers +int* getNumbers() { + + static int array[10]; + + for (int i = 0; i < 10; ++i) { + array[i] = i;//NOWARN + } + + return array; +} + +int* getNumbers2() { + int* numbers = getNumbers(); + static int numbers2[10]; + for (int i = 0; i < 10; ++i) { + numbers2[i] = numbers[i];//NOWARN + } + return numbers2; +} + +int* getNumbers3() { + int* numbers = getNumbers2(); + int numbers3[10]; + for (int i = 0; i < 10; ++i) { + numbers3[i] = numbers[i];//NOWARN + } + + return numbers3; +} + +int* getNumbers4() { + int* numbers = getNumbers3(); + static int numbers4[10]; + for (int i = 0; i < 10; ++i) { + numbers4[i] = numbers[i];//WARN + } + return numbers4; +} + +int main (void) { + + int *numbers = getNumbers4(); + + for (int i = 0; i < 10; i++ ) { + printf( "%d\n", *(numbers + i));//NOWARN + } + + return 0; +} diff --git a/tests/regression/77-mem-oob/04-oob-deref-after-ptr-arith.c b/tests/regression/74-invalid_deref/23-oob-deref-after-ptr-arith.c similarity index 100% rename from tests/regression/77-mem-oob/04-oob-deref-after-ptr-arith.c rename to tests/regression/74-invalid_deref/23-oob-deref-after-ptr-arith.c diff --git a/tests/regression/74-use_after_free/05-uaf-free-in-wrapper-fun.c b/tests/regression/74-invalid_deref/24-uaf-free-in-wrapper-fun.c similarity index 100% rename from tests/regression/74-use_after_free/05-uaf-free-in-wrapper-fun.c rename to tests/regression/74-invalid_deref/24-uaf-free-in-wrapper-fun.c diff --git a/tests/regression/74-use_after_free/06-uaf-struct.c b/tests/regression/74-invalid_deref/25-uaf-struct.c similarity index 100% rename from tests/regression/74-use_after_free/06-uaf-struct.c rename to tests/regression/74-invalid_deref/25-uaf-struct.c diff --git a/tests/regression/77-mem-oob/09-memset-memcpy-addr-offs.c b/tests/regression/74-invalid_deref/26-memset-memcpy-addr-offs.c similarity index 100% rename from tests/regression/77-mem-oob/09-memset-memcpy-addr-offs.c rename to tests/regression/74-invalid_deref/26-memset-memcpy-addr-offs.c diff --git a/tests/regression/74-use_after_free/11-wrapper-funs-uaf.c b/tests/regression/74-invalid_deref/27-wrapper-funs-uaf.c similarity index 100% rename from tests/regression/74-use_after_free/11-wrapper-funs-uaf.c rename to tests/regression/74-invalid_deref/27-wrapper-funs-uaf.c diff --git a/tests/regression/74-use_after_free/12-multi-threaded-uaf.c b/tests/regression/74-invalid_deref/28-multi-threaded-uaf.c similarity index 100% rename from tests/regression/74-use_after_free/12-multi-threaded-uaf.c rename to tests/regression/74-invalid_deref/28-multi-threaded-uaf.c diff --git a/tests/regression/74-use_after_free/13-multi-threaded-uaf-with-joined-thread.c b/tests/regression/74-invalid_deref/29-multi-threaded-uaf-with-joined-thread.c similarity index 100% rename from tests/regression/74-use_after_free/13-multi-threaded-uaf-with-joined-thread.c rename to tests/regression/74-invalid_deref/29-multi-threaded-uaf-with-joined-thread.c diff --git a/tests/regression/75-invalid_dealloc/01-invalid-dealloc-simple.c b/tests/regression/75-invalid_free/01-invalid-dealloc-simple.c similarity index 100% rename from tests/regression/75-invalid_dealloc/01-invalid-dealloc-simple.c rename to tests/regression/75-invalid_free/01-invalid-dealloc-simple.c diff --git a/tests/regression/75-invalid_dealloc/02-invalid-dealloc-struct.c b/tests/regression/75-invalid_free/02-invalid-dealloc-struct.c similarity index 100% rename from tests/regression/75-invalid_dealloc/02-invalid-dealloc-struct.c rename to tests/regression/75-invalid_free/02-invalid-dealloc-struct.c diff --git a/tests/regression/75-invalid_dealloc/03-invalid-dealloc-array.c b/tests/regression/75-invalid_free/03-invalid-dealloc-array.c similarity index 100% rename from tests/regression/75-invalid_dealloc/03-invalid-dealloc-array.c rename to tests/regression/75-invalid_free/03-invalid-dealloc-array.c diff --git a/tests/regression/75-invalid_dealloc/04-invalid-realloc.c b/tests/regression/75-invalid_free/04-invalid-realloc.c similarity index 100% rename from tests/regression/75-invalid_dealloc/04-invalid-realloc.c rename to tests/regression/75-invalid_free/04-invalid-realloc.c diff --git a/tests/regression/75-invalid_dealloc/05-free-at-offset.c b/tests/regression/75-invalid_free/05-free-at-offset.c similarity index 100% rename from tests/regression/75-invalid_dealloc/05-free-at-offset.c rename to tests/regression/75-invalid_free/05-free-at-offset.c diff --git a/tests/regression/75-invalid_dealloc/06-realloc-at-offset.c b/tests/regression/75-invalid_free/06-realloc-at-offset.c similarity index 100% rename from tests/regression/75-invalid_dealloc/06-realloc-at-offset.c rename to tests/regression/75-invalid_free/06-realloc-at-offset.c diff --git a/tests/regression/75-invalid_dealloc/07-free-at-struct-offset.c b/tests/regression/75-invalid_free/07-free-at-struct-offset.c similarity index 100% rename from tests/regression/75-invalid_dealloc/07-free-at-struct-offset.c rename to tests/regression/75-invalid_free/07-free-at-struct-offset.c diff --git a/tests/regression/74-use_after_free/08-itc-no-double-free.c b/tests/regression/75-invalid_free/08-itc-no-double-free.c similarity index 100% rename from tests/regression/74-use_after_free/08-itc-no-double-free.c rename to tests/regression/75-invalid_free/08-itc-no-double-free.c diff --git a/tests/regression/75-invalid_dealloc/09-juliet-invalid-dealloc-alloca.c b/tests/regression/75-invalid_free/09-juliet-invalid-dealloc-alloca.c similarity index 100% rename from tests/regression/75-invalid_dealloc/09-juliet-invalid-dealloc-alloca.c rename to tests/regression/75-invalid_free/09-juliet-invalid-dealloc-alloca.c diff --git a/tests/regression/75-invalid_free/10-invalid-dealloc-union.c b/tests/regression/75-invalid_free/10-invalid-dealloc-union.c new file mode 100644 index 0000000000..be1eaa056d --- /dev/null +++ b/tests/regression/75-invalid_free/10-invalid-dealloc-union.c @@ -0,0 +1,42 @@ +extern void abort(void); +#include + +extern int __VERIFIER_nondet_int(void); + +int main() +{ + union { + void *p0; + + struct { + char c[2]; + int p1; + int p2; + } str; + + } data; + + // alloc 37B on heap + data.p0 = malloc(37U); + + // avoid introducing a memleak + void *ptr = data.p0; + + // this should be fine + if(__VERIFIER_nondet_int()) { + data.str.p2 = 20; + } else { + data.str.p2 = 30; + } + + if(25 > data.str.p2) { + // avoids memleak + data.str.c[1] = sizeof data.str.p1; + } + + // invalid free() + free(data.p0);//WARN + + free(ptr);//NOWARN + return 0; +} diff --git a/tests/regression/74-use_after_free/07-itc-double-free.c b/tests/regression/75-invalid_free/11-itc-double-free.c similarity index 100% rename from tests/regression/74-use_after_free/07-itc-double-free.c rename to tests/regression/75-invalid_free/11-itc-double-free.c diff --git a/tests/regression/75-invalid_dealloc/08-realloc-at-struct-offset.c b/tests/regression/75-invalid_free/12-realloc-at-struct-offset.c similarity index 100% rename from tests/regression/75-invalid_dealloc/08-realloc-at-struct-offset.c rename to tests/regression/75-invalid_free/12-realloc-at-struct-offset.c diff --git a/tests/regression/74-use_after_free/10-juliet-double-free.c b/tests/regression/75-invalid_free/13-juliet-double-free.c similarity index 100% rename from tests/regression/74-use_after_free/10-juliet-double-free.c rename to tests/regression/75-invalid_free/13-juliet-double-free.c diff --git a/tests/sv-comp/valid-memcleanup.prp b/tests/sv-comp/valid-memcleanup.prp new file mode 100644 index 0000000000..778c49e5dc --- /dev/null +++ b/tests/sv-comp/valid-memcleanup.prp @@ -0,0 +1,2 @@ +CHECK( init(main()), LTL(G valid-memcleanup) ) + From 58445527a7e46dfce695a36cd0d97e1e7840b9e3 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sat, 14 Oct 2023 11:37:03 +0200 Subject: [PATCH 0874/1312] Cleanup termination property --- src/witness/svcompSpec.ml | 46 +++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/src/witness/svcompSpec.ml b/src/witness/svcompSpec.ml index de2d7c5283..ceedfcc8a9 100644 --- a/src/witness/svcompSpec.ml +++ b/src/witness/svcompSpec.ml @@ -18,14 +18,13 @@ let of_string s = let regexp_multiple = Str.regexp "CHECK( init(main()), LTL(G \\(.*\\)) )\nCHECK( init(main()), LTL(G \\(.*\\)) )\nCHECK( init(main()), LTL(G \\(.*\\)) )" in let regexp_single = Str.regexp "CHECK( init(main()), LTL(G \\(.*\\)) )" in let regexp_negated = Str.regexp "CHECK( init(main()), LTL(G ! \\(.*\\)) )" in + let regexp_finally = Str.regexp "CHECK( init(main()), LTL(F \\(.*\\)) )" in if Str.string_match regexp_negated s 0 then let global_not = Str.matched_group 1 s in if global_not = "data-race" then NoDataRace else if global_not = "overflow" then NoOverflow - else if global_not = "no-termination" then - Termination else let call_regex = Str.regexp "call(\\(.*\\)())" in if Str.string_match call_regex global_not 0 then @@ -48,6 +47,12 @@ let of_string s = ValidMemcleanup else failwith "Svcomp.Specification.of_string: unknown global expression" + else if Str.string_match regexp_finally s 0 then + let finally = Str.matched_group 1 s in + if finally = "end" then + Termination + else + failwith "Svcomp.Specification.of_string: unknown finally expression" else failwith "Svcomp.Specification.of_string: unknown expression" @@ -63,21 +68,30 @@ let of_option () = of_string s let to_string spec = - let print_output spec_str is_neg = + let module Prop = struct + type prop = F | G + let string_of_prop = function + | F -> "F" + | G -> "G" + end + in + let open Prop in + let print_output prop spec_str is_neg = + let prop = string_of_prop prop in if is_neg then - Printf.sprintf "CHECK( init(main()), LTL(G ! %s) )" spec_str + Printf.sprintf "CHECK( init(main()), LTL(%s ! %s) )" prop spec_str else - Printf.sprintf "CHECK( init(main()), LTL(G %s) )" spec_str + Printf.sprintf "CHECK( init(main()), LTL(%s %s) )" prop spec_str in - let spec_str, is_neg = match spec with - | UnreachCall f -> "call(" ^ f ^ "())", true - | NoDataRace -> "data-race", true - | NoOverflow -> "overflow", true - | ValidFree -> "valid-free", false - | ValidDeref -> "valid-deref", false - | ValidMemtrack -> "valid-memtrack", false - | Termination -> "no-termination", true - | MemorySafety -> "memory-safety", false (* TODO: That's false, it's currently here just to complete the pattern match *) - | ValidMemcleanup -> "valid-memcleanup", false + let prop, spec_str, is_neg = match spec with + | UnreachCall f -> G, "call(" ^ f ^ "())", true + | NoDataRace -> G, "data-race", true + | NoOverflow -> G, "overflow", true + | ValidFree -> G, "valid-free", false + | ValidDeref -> G, "valid-deref", false + | ValidMemtrack -> G, "valid-memtrack", false + | MemorySafety -> G, "memory-safety", false (* TODO: That's false, it's currently here just to complete the pattern match *) + | ValidMemcleanup -> G, "valid-memcleanup", false + | Termination -> F, "termination", false in - print_output spec_str is_neg + print_output prop spec_str is_neg From 7cca2cf595a506e6ab9865633c192d304d8c76f1 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sat, 14 Oct 2023 11:39:26 +0200 Subject: [PATCH 0875/1312] LTL for termination is `F end` --- src/witness/svcompSpec.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/witness/svcompSpec.ml b/src/witness/svcompSpec.ml index ceedfcc8a9..e51e691154 100644 --- a/src/witness/svcompSpec.ml +++ b/src/witness/svcompSpec.ml @@ -92,6 +92,6 @@ let to_string spec = | ValidMemtrack -> G, "valid-memtrack", false | MemorySafety -> G, "memory-safety", false (* TODO: That's false, it's currently here just to complete the pattern match *) | ValidMemcleanup -> G, "valid-memcleanup", false - | Termination -> F, "termination", false + | Termination -> F, "end", false in print_output prop spec_str is_neg From a4261deb3f784720d0806cf92c1c9165d2cbe36e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 16 Oct 2023 17:36:23 +0300 Subject: [PATCH 0876/1312] Add final message for unknown ignored longjmp --- src/framework/constraints.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 21f3958a81..95a13ed516 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1663,7 +1663,8 @@ struct if M.tracing then Messages.tracel "longjmp" "Jumping to %a\n" JmpBufDomain.JmpBufSet.pretty targets; let handle_target target = match target with | JmpBufDomain.BufferEntryOrTop.AllTargets -> - M.warn ~category:Imprecise "Longjmp to potentially invalid target, as contents of buffer %a may be unknown! (imprecision due to heap?)" d_exp env + M.warn ~category:Imprecise "Longjmp to potentially invalid target, as contents of buffer %a may be unknown! (imprecision due to heap?)" d_exp env; + M.msg_final Error ~category:Unsound ~tags:[Category Imprecise; Category Call] "Longjmp to unknown target ignored" | Target (target_node, target_context) -> let target_fundec = Node.find_fundec target_node in if CilType.Fundec.equal target_fundec current_fundec && ControlSpecC.equal target_context (ctx.control_context ()) then ( From 5948ca4212df4c896ee20082a4eb6422c70bd06d Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 16 Oct 2023 17:37:45 +0300 Subject: [PATCH 0877/1312] Mark longjmp-top reachability test as TODO --- tests/regression/68-longjmp/56-longjmp-top.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression/68-longjmp/56-longjmp-top.c b/tests/regression/68-longjmp/56-longjmp-top.c index 4d57b42fd3..adb3a47476 100644 --- a/tests/regression/68-longjmp/56-longjmp-top.c +++ b/tests/regression/68-longjmp/56-longjmp-top.c @@ -15,7 +15,7 @@ int main() { longjmp(*buf_ptr, 1); // NO CRASH: problem?! } else { - __goblint_check(1); // reachable + __goblint_check(1); // TODO reachable: https://github.com/goblint/analyzer/pull/1210#discussion_r1350021903 } return 0; } From 32be7d50615878bb400b6d665d5bb589be28b79c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 20 Oct 2023 13:39:23 +0300 Subject: [PATCH 0878/1312] Improve names of some global constraint variables --- src/analyses/commonPriv.ml | 2 +- src/framework/constraints.ml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index db75455b40..793978980b 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -84,7 +84,7 @@ struct module V = struct (* TODO: Either3? *) - include Printable.Either (Printable.Either (VMutex) (VMutexInits)) (VGlobal) + include Printable.Either (struct include Printable.Either (VMutex) (VMutexInits) let name () = "mutex" end) (VGlobal) let name () = "MutexGlobals" let mutex x: t = `Left (`Left x) let mutex_inits: t = `Left (`Right ()) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 95a13ed516..812d056de4 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1467,6 +1467,7 @@ struct module V = struct include Printable.Either (S.V) (Printable.Either (Printable.Prod (Node) (C)) (Printable.Prod (CilType.Fundec) (C))) + let name () = "longjmp" let s x = `Left x let longjmpto x = `Right (`Left x) let longjmpret x = `Right (`Right x) From af904ac55762f3b9bb59bce54a13ed8e311894d1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Oct 2023 20:19:25 +0000 Subject: [PATCH 0879/1312] Bump actions/setup-node from 3 to 4 Bumps [actions/setup-node](https://github.com/actions/setup-node) from 3 to 4. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/setup-node dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/locked.yml | 2 +- .github/workflows/metadata.yml | 2 +- .github/workflows/options.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/locked.yml b/.github/workflows/locked.yml index 007ea34619..65dfbe7bac 100644 --- a/.github/workflows/locked.yml +++ b/.github/workflows/locked.yml @@ -153,7 +153,7 @@ jobs: ocaml-compiler: ${{ matrix.ocaml-compiler }} - name: Set up Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} diff --git a/.github/workflows/metadata.yml b/.github/workflows/metadata.yml index 1092606bc6..6c7360f9e3 100644 --- a/.github/workflows/metadata.yml +++ b/.github/workflows/metadata.yml @@ -39,7 +39,7 @@ jobs: uses: actions/checkout@v4 - name: Set up Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} diff --git a/.github/workflows/options.yml b/.github/workflows/options.yml index 40652791fa..94c49e4bf6 100644 --- a/.github/workflows/options.yml +++ b/.github/workflows/options.yml @@ -18,7 +18,7 @@ jobs: uses: actions/checkout@v4 - name: Set up Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} From 0cf9bc498741c24abda05007134c067f297a1b72 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 26 Oct 2023 10:28:06 +0300 Subject: [PATCH 0880/1312] Add smtprc-tid unsound case --- tests/regression/03-practical/32-smtprc-tid.c | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 tests/regression/03-practical/32-smtprc-tid.c diff --git a/tests/regression/03-practical/32-smtprc-tid.c b/tests/regression/03-practical/32-smtprc-tid.c new file mode 100644 index 0000000000..1d4810ee2e --- /dev/null +++ b/tests/regression/03-practical/32-smtprc-tid.c @@ -0,0 +1,38 @@ +#include +#include + +int threads_total = 4; +pthread_t *tids; + +void *cleaner(void *arg) { + while (1) { + for (int i = 0; i < threads_total; i++) { + if (tids[i]) { // RACE! + if (!pthread_join(tids[i], NULL)) // RACE! + tids[i] = 0; // RACE! + } + } + } + return NULL; +} + +void *thread(int i) { // wrong argument type is important + tids[i] = pthread_self(); // RACE! + return NULL; +} + +int main() { + pthread_t tid; + tids = malloc(threads_total * sizeof(pthread_t)); + + for(int i = 0; i < threads_total; i++) + tids[i] = 0; + + pthread_create(&tid, NULL, cleaner, NULL); + + for(int i = 0; i < threads_total; i++) { + pthread_create(&tid, NULL, thread, (int *)i); // cast is important + } + + return 0; +} From 2a958bd7e1a71c0216a0d49317796a45fd13ddf7 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 20 Sep 2023 17:47:35 +0300 Subject: [PATCH 0881/1312] Fix smtprc-tid unsoundness --- src/analyses/base.ml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index caa2a41533..6536a9c496 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -399,6 +399,8 @@ struct Int (if AD.is_bot (AD.meet p1 p2) then ID.of_int ik BI.one else match eq p1 p2 with Some x when x -> ID.of_int ik BI.zero | _ -> bool_top ik) | IndexPI when AD.to_string p2 = ["all_index"] -> addToAddrOp p1 (ID.top_of (Cilfacade.ptrdiff_ikind ())) + | IndexPI | PlusPI -> + addToAddrOp p1 (AD.to_int p2) (* sometimes index is AD for some reason... *) | _ -> VD.top () end (* For other values, we just give up! *) From 0e31b8d8d0b19679414d7621086c9ab408d8318c Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Thu, 26 Oct 2023 19:02:07 +0300 Subject: [PATCH 0882/1312] Add unknown thread ID --- src/analyses/useAfterFree.ml | 2 +- src/cdomains/mHP.ml | 2 +- src/cdomains/threadIdDomain.ml | 73 +++++++++++++++++++++++++++++++++- 3 files changed, 74 insertions(+), 3 deletions(-) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index ef63ab3e91..96a06a6cc1 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -76,7 +76,7 @@ struct end else if HeapVars.mem heap_var (snd ctx.local) then begin if is_double_free then set_mem_safety_flag InvalidFree else set_mem_safety_flag InvalidDeref; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "%s might occur in current unique thread %a for heap variable %a" bug_name ThreadIdDomain.FlagConfiguredTID.pretty current CilType.Varinfo.pretty heap_var + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "%s might occur in current unique thread %a for heap variable %a" bug_name ThreadIdDomain.Thread.pretty current CilType.Varinfo.pretty heap_var end end | `Top -> diff --git a/src/cdomains/mHP.ml b/src/cdomains/mHP.ml index 8037cfa21d..016a72a77e 100644 --- a/src/cdomains/mHP.ml +++ b/src/cdomains/mHP.ml @@ -4,7 +4,7 @@ include Printable.Std let name () = "mhp" -module TID = ThreadIdDomain.FlagConfiguredTID +module TID = ThreadIdDomain.Thread module Pretty = GoblintCil.Pretty type t = { diff --git a/src/cdomains/threadIdDomain.ml b/src/cdomains/threadIdDomain.ml index 7193552048..ff6edf8bda 100644 --- a/src/cdomains/threadIdDomain.ml +++ b/src/cdomains/threadIdDomain.ml @@ -279,6 +279,77 @@ struct let name () = "FlagConfiguredTID: " ^ if history_enabled () then H.name () else P.name () end -module Thread = FlagConfiguredTID +module Thread : Stateful = +struct + include Printable.Std + type t = + | Thread of FlagConfiguredTID.t + | UnknownThread + [@@deriving eq, ord, hash] + + let name () = "Thread id" + let pretty () t = + match t with + | Thread tid -> FlagConfiguredTID.pretty () tid + | UnknownThread -> Pretty.text "Unknown thread id" + + let show t = + match t with + | Thread tid -> FlagConfiguredTID.show tid + | UnknownThread -> "Unknown thread id" + + let printXml f t = + match t with + | Thread tid -> FlagConfiguredTID.printXml f tid + | UnknownThread -> BatPrintf.fprintf f "\n\nUnknown thread id\n\n\n" + + let to_yojson t = + match t with + | Thread tid -> FlagConfiguredTID.to_yojson tid + | UnknownThread -> `String "Unknown thread id" + + let relift t = + match t with + | Thread tid -> Thread (FlagConfiguredTID.relift tid) + | UnknownThread -> UnknownThread + + let lift t = Thread t + + let threadinit v ~multiple = Thread (FlagConfiguredTID.threadinit v ~multiple) + + let is_main t = + match t with + | Thread tid -> FlagConfiguredTID.is_main tid + | UnknownThread -> false + + let is_unique t = + match t with + | Thread tid -> FlagConfiguredTID.is_unique tid + | UnknownThread -> false + + let may_create t1 t2 = + match t1, t2 with + | Thread tid1, Thread tid2 -> FlagConfiguredTID.may_create tid1 tid2 + | _, _ -> true + + let is_must_parent t1 t2 = + match t1, t2 with + | Thread tid1, Thread tid2 -> FlagConfiguredTID.is_must_parent tid1 tid2 + | _, _ -> false + + module D = FlagConfiguredTID.D + + let threadenter (t, d) node i v = + match t with + | Thread tid -> List.map lift (FlagConfiguredTID.threadenter (tid, d) node i v) + | UnknownThread -> assert false + + let threadspawn = FlagConfiguredTID.threadspawn + + let created t d = + match t with + | Thread tid -> Option.map (List.map lift) (FlagConfiguredTID.created tid d) + | UnknownThread -> None +end module ThreadLifted = Lift (Thread) From 192108b69ced96430d69148ad360cff22c4e0bf5 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Thu, 26 Oct 2023 19:34:30 +0300 Subject: [PATCH 0883/1312] Use set instead of toppedSet for ThreadSet --- src/cdomains/concDomain.ml | 21 ++++++++++++++++++++- src/cdomains/threadIdDomain.ml | 12 +++++++----- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/cdomains/concDomain.ml b/src/cdomains/concDomain.ml index b16cdf1d9f..5f609a31d8 100644 --- a/src/cdomains/concDomain.ml +++ b/src/cdomains/concDomain.ml @@ -1,6 +1,25 @@ (** Domains for thread sets and their uniqueness. *) -module ThreadSet = SetDomain.ToppedSet (ThreadIdDomain.Thread) (struct let topname = "All Threads" end) +module ThreadSet = +struct + include SetDomain.Make (ThreadIdDomain.Thread) + + let is_top = mem UnknownThread + + let top () = singleton UnknownThread + + let merge uop cop x y = + match is_top x, is_top y with + | true, true -> uop x y + | false, true -> x + | true, false -> y + | false, false -> cop x y + + let meet x y = merge join meet x y + + let narrow x y = merge (fun x y -> widen x (join x y)) narrow x y + +end module MustThreadSet = SetDomain.Reverse(ThreadSet) module CreatedThreadSet = ThreadSet diff --git a/src/cdomains/threadIdDomain.ml b/src/cdomains/threadIdDomain.ml index ff6edf8bda..c0a8f2390f 100644 --- a/src/cdomains/threadIdDomain.ml +++ b/src/cdomains/threadIdDomain.ml @@ -279,13 +279,15 @@ struct let name () = "FlagConfiguredTID: " ^ if history_enabled () then H.name () else P.name () end -module Thread : Stateful = +type thread = + | Thread of FlagConfiguredTID.t + | UnknownThread +[@@deriving eq, ord, hash] + +module Thread : Stateful with type t = thread = struct include Printable.Std - type t = - | Thread of FlagConfiguredTID.t - | UnknownThread - [@@deriving eq, ord, hash] + type t = thread [@@deriving eq, ord, hash] let name () = "Thread id" let pretty () t = From 1221860befda16cbdf5c3ca3bc3e1d6a775dca46 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Fri, 27 Oct 2023 12:03:34 +0300 Subject: [PATCH 0884/1312] Add test for unknown thread id --- .../51-threadjoins/07-trivial-unknowntid.c | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 tests/regression/51-threadjoins/07-trivial-unknowntid.c diff --git a/tests/regression/51-threadjoins/07-trivial-unknowntid.c b/tests/regression/51-threadjoins/07-trivial-unknowntid.c new file mode 100644 index 0000000000..2797291ee3 --- /dev/null +++ b/tests/regression/51-threadjoins/07-trivial-unknowntid.c @@ -0,0 +1,34 @@ +//PARAM: --set ana.activated[+] threadJoins +#include + +int g = 10; +int h = 10; +pthread_mutex_t A = PTHREAD_MUTEX_INITIALIZER; + +void *t_fun(void *arg) { + g++; // RACE! + return NULL; +} + +void *t_benign(void *arg) { + h++; // NORACE + pthread_t id2; + pthread_create(&id2, NULL, t_fun, NULL); + foo(&id2); + pthread_join(id2, NULL); + return NULL; +} + +int main(void) { + int t; + + pthread_t id2; + pthread_create(&id2, NULL, t_benign, NULL); + pthread_join(id2, NULL); + // t_benign and t_fun should be in here + + g++; // RACE! + h++; // NORACE + + return 0; +} From 2df78822dc866fbb9bd26dbb6ccba893c280f114 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Fri, 27 Oct 2023 12:04:31 +0300 Subject: [PATCH 0885/1312] Fix unsoundness on unknown function call with tid as argument --- src/cdomains/valueDomain.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cdomains/valueDomain.ml b/src/cdomains/valueDomain.ml index cba4b04c18..d3c8bc6989 100644 --- a/src/cdomains/valueDomain.ml +++ b/src/cdomains/valueDomain.ml @@ -708,7 +708,7 @@ struct let v = invalidate_value ask voidType (CArrays.get ask n (array_idx_top)) in Array (CArrays.set ask n (array_idx_top) v) | t , Blob n -> Blob (Blobs.invalidate_value ask t n) - | _ , Thread _ -> state (* TODO: no top thread ID set! *) + | _ , Thread tid -> Thread (Threads.join (Threads.top ()) tid) | _ , JmpBuf _ -> state (* TODO: no top jmpbuf *) | _, Bot -> Bot (* Leave uninitialized value (from malloc) alone in free to avoid trashing everything. TODO: sound? *) | t , _ -> top_value t From 6899d444f27c1f434ade07565ab5d23a631ea753 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Fri, 27 Oct 2023 15:10:48 +0300 Subject: [PATCH 0886/1312] Add --enable ana.sv-comp.functions to 20-race-2_1-container_of.c --- tests/regression/10-synch/20-race-2_1-container_of.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/regression/10-synch/20-race-2_1-container_of.c b/tests/regression/10-synch/20-race-2_1-container_of.c index 6083cf4ca0..940649c43b 100644 --- a/tests/regression/10-synch/20-race-2_1-container_of.c +++ b/tests/regression/10-synch/20-race-2_1-container_of.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.activated[+] thread --set ana.path_sens[+] threadflag +// PARAM: --set ana.activated[+] thread --set ana.path_sens[+] threadflag --enable ana.sv-comp.functions #include #include #include @@ -60,7 +60,7 @@ int my_drv_probe(struct my_data *data) { ldv_assert(data->shared.a==0); // NORACE ldv_assert(data->shared.b==0); // NORACE - int res = __VERIFIER_nondet_int(); + int res = magic(); if(res) goto exit; //register callback From a401a68ee26c9d40ee7f2ec0e0a467c93211240a Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Fri, 27 Oct 2023 23:25:49 +0300 Subject: [PATCH 0887/1312] Replace exception handling with top checks --- src/analyses/apron/relationPriv.apron.ml | 22 +++++++++++----------- src/analyses/basePriv.ml | 22 +++++++++++----------- src/analyses/threadAnalysis.ml | 7 ++++--- src/analyses/threadJoins.ml | 4 ++-- 4 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/analyses/apron/relationPriv.apron.ml b/src/analyses/apron/relationPriv.apron.ml index b386af162b..3adfa272bb 100644 --- a/src/analyses/apron/relationPriv.apron.ml +++ b/src/analyses/apron/relationPriv.apron.ml @@ -1011,17 +1011,17 @@ struct ) ) else ( - match ConcDomain.ThreadSet.elements tids with - | [tid] -> - let lmust',l' = G.thread (getg (V.thread tid)) in - {st with priv = (w, LMust.union lmust' lmust, L.join l l')} - | _ -> - (* To match the paper more closely, one would have to join in the non-definite case too *) - (* Given how we handle lmust (for initialization), doing this might actually be beneficial given that it grows lmust *) - st - | exception SetDomain.Unsupported _ -> - (* elements throws if the thread set is top *) - st + if ConcDomain.ThreadSet.is_top tids + then st + else + match ConcDomain.ThreadSet.elements tids with + | [tid] -> + let lmust',l' = G.thread (getg (V.thread tid)) in + {st with priv = (w, LMust.union lmust' lmust, L.join l l')} + | _ -> + (* To match the paper more closely, one would have to join in the non-definite case too *) + (* Given how we handle lmust (for initialization), doing this might actually be beneficial given that it grows lmust *) + st ) let thread_return ask getg sideg tid (st: relation_components_t) = diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index 0154924a1c..ed6439a847 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -544,17 +544,17 @@ struct ) ) else ( - match ConcDomain.ThreadSet.elements tids with - | [tid] -> - let lmust',l' = G.thread (getg (V.thread tid)) in - {st with priv = (w, LMust.union lmust' lmust, L.join l l')} - | _ -> - (* To match the paper more closely, one would have to join in the non-definite case too *) - (* Given how we handle lmust (for initialization), doing this might actually be beneficial given that it grows lmust *) - st - | exception SetDomain.Unsupported _ -> - (* elements throws if the thread set is top *) - st + if (ConcDomain.ThreadSet.is_top tids) + then st + else + match ConcDomain.ThreadSet.elements tids with + | [tid] -> + let lmust',l' = G.thread (getg (V.thread tid)) in + {st with priv = (w, LMust.union lmust' lmust, L.join l l')} + | _ -> + (* To match the paper more closely, one would have to join in the non-definite case too *) + (* Given how we handle lmust (for initialization), doing this might actually be beneficial given that it grows lmust *) + st ) let thread_return ask getg sideg tid (st: BaseComponents (D).t) = diff --git a/src/analyses/threadAnalysis.ml b/src/analyses/threadAnalysis.ml index 1e679a4707..acc53d9dee 100644 --- a/src/analyses/threadAnalysis.ml +++ b/src/analyses/threadAnalysis.ml @@ -54,15 +54,16 @@ struct | ThreadJoin { thread = id; ret_var } -> (* TODO: generalize ThreadJoin like ThreadCreate *) (let has_clean_exit tid = not (BatTuple.Tuple3.third (ctx.global tid)) in + let tids = ctx.ask (Queries.EvalThread id) in let join_thread s tid = if has_clean_exit tid && not (is_not_unique ctx tid) then D.remove tid s else s in - match TS.elements (ctx.ask (Queries.EvalThread id)) with - | threads -> List.fold_left join_thread ctx.local threads - | exception SetDomain.Unsupported _ -> ctx.local) + if TS.is_top tids + then ctx.local + else List.fold_left join_thread ctx.local (TS.elements tids)) | _ -> ctx.local let query ctx (type a) (q: a Queries.t): a Queries.result = diff --git a/src/analyses/threadJoins.ml b/src/analyses/threadJoins.ml index f2cd36619f..2977ed9082 100644 --- a/src/analyses/threadJoins.ml +++ b/src/analyses/threadJoins.ml @@ -52,7 +52,7 @@ struct if TIDs.is_top threads then ctx.local else ( - (* elements throws if the thread set is top *) + (* all elements are known *) let threads = TIDs.elements threads in match threads with | [tid] when TID.is_unique tid-> @@ -70,7 +70,7 @@ struct (MustTIDs.bot(), true) (* consider everything joined, MustTIDs is reversed so bot is All threads *) ) else ( - (* elements throws if the thread set is top *) + (* all elements are known *) let threads = TIDs.elements threads in if List.compare_length_with threads 1 > 0 then M.info ~category:Unsound "Ambiguous thread ID assume-joined, assuming all of those threads must-joined."; From 2c0a08f0e356d7e92fff2db874d081e38831e272 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Sun, 29 Oct 2023 20:14:11 +0200 Subject: [PATCH 0888/1312] Fix accident in 20 10 test --- tests/regression/10-synch/20-race-2_1-container_of.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression/10-synch/20-race-2_1-container_of.c b/tests/regression/10-synch/20-race-2_1-container_of.c index 940649c43b..04d5facbb7 100644 --- a/tests/regression/10-synch/20-race-2_1-container_of.c +++ b/tests/regression/10-synch/20-race-2_1-container_of.c @@ -60,7 +60,7 @@ int my_drv_probe(struct my_data *data) { ldv_assert(data->shared.a==0); // NORACE ldv_assert(data->shared.b==0); // NORACE - int res = magic(); + int res = __VERIFIER_nondet_int(); if(res) goto exit; //register callback From 4cb8c97c0d35b69dd6cf18452dff49e7453a2666 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Sun, 29 Oct 2023 20:15:50 +0200 Subject: [PATCH 0889/1312] Join threads with top when joining with int or address --- src/cdomains/valueDomain.ml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/cdomains/valueDomain.ml b/src/cdomains/valueDomain.ml index d3c8bc6989..f5e9c45845 100644 --- a/src/cdomains/valueDomain.ml +++ b/src/cdomains/valueDomain.ml @@ -552,11 +552,9 @@ struct | y, Blob (x,s,o) -> Blob (join (x:t) y, s, o) | (Thread x, Thread y) -> Thread (Threads.join x y) | (Int x, Thread y) - | (Thread y, Int x) -> - Thread y (* TODO: ignores int! *) + | (Thread y, Int x) -> Thread (Threads.join y (Threads.top ())) | (Address x, Thread y) - | (Thread y, Address x) -> - Thread y (* TODO: ignores address! *) + | (Thread y, Address x) -> Thread (Threads.join y (Threads.top ())) | (JmpBuf x, JmpBuf y) -> JmpBuf (JmpBufs.join x y) | (Mutex, Mutex) -> Mutex | (MutexAttr x, MutexAttr y) -> MutexAttr (MutexAttr.join x y) From ae7a4061ffa1b120c20e3f641a637c197494cc12 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Sun, 29 Oct 2023 20:20:32 +0200 Subject: [PATCH 0890/1312] Implement widen for threads with int and address similarly to the Address and Int case --- src/cdomains/valueDomain.ml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/cdomains/valueDomain.ml b/src/cdomains/valueDomain.ml index f5e9c45845..c8b3ac928e 100644 --- a/src/cdomains/valueDomain.ml +++ b/src/cdomains/valueDomain.ml @@ -583,11 +583,9 @@ struct | (Blob x, Blob y) -> Blob (Blobs.widen x y) (* TODO: why no blob special cases like in join? *) | (Thread x, Thread y) -> Thread (Threads.widen x y) | (Int x, Thread y) - | (Thread y, Int x) -> - Thread y (* TODO: ignores int! *) + | (Thread y, Int x) -> Thread (Threads.widen y (Threads.join y (Threads.top ()))) | (Address x, Thread y) - | (Thread y, Address x) -> - Thread y (* TODO: ignores address! *) + | (Thread y, Address x) -> Thread (Threads.widen y (Threads.join y (Threads.top ()))) | (Mutex, Mutex) -> Mutex | (JmpBuf x, JmpBuf y) -> JmpBuf (JmpBufs.widen x y) | (MutexAttr x, MutexAttr y) -> MutexAttr (MutexAttr.widen x y) From 894e6189dfa5a27dbb0872d5feeae23e35568888 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Sun, 29 Oct 2023 21:20:47 +0200 Subject: [PATCH 0891/1312] Handle top thread when handling thread joins in base --- src/analyses/base.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 6536a9c496..58ab2dc219 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2372,6 +2372,7 @@ struct | Int n when GobOption.exists (BI.equal BI.zero) (ID.to_int n) -> st | Address ret_a -> begin match eval_rv (Analyses.ask_of_ctx ctx) gs st id with + | Thread a when ValueDomain.Threads.is_top a -> invalidate ~ctx (Analyses.ask_of_ctx ctx) gs st [ret_var] | Thread a -> let v = List.fold VD.join (VD.bot ()) (List.map (fun x -> G.thread (ctx.global (V.thread x))) (ValueDomain.Threads.elements a)) in (* TODO: is this type right? *) From 7ad12249c111c7dbdb906be7f57311c49a3a15be Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 30 Oct 2023 16:52:50 +0200 Subject: [PATCH 0892/1312] Move GraphML witness options into witness.graphml (issue #1217) --- conf/ldv-races.json | 6 +- conf/svcomp-yaml.json | 4 +- conf/svcomp.json | 6 +- conf/svcomp21.json | 4 +- ...vcomp22-intervals-novareq-affeq-apron.json | 6 +- ...comp22-intervals-novareq-affeq-native.json | 6 +- ...omp22-intervals-novareq-octagon-apron.json | 6 +- ...p22-intervals-novareq-polyhedra-apron.json | 6 +- conf/svcomp22.json | 6 +- conf/svcomp23.json | 6 +- src/common/util/options.schema.json | 93 ++++++++++--------- src/framework/control.ml | 2 +- src/witness/myARG.ml | 4 +- src/witness/witness.ml | 14 +-- sv-comp/sv-comp-run-no-overflow.py | 2 +- sv-comp/sv-comp-run.py | 2 +- .../observer/path_nofun_true-unreach-call.c | 2 +- 17 files changed, 101 insertions(+), 74 deletions(-) diff --git a/conf/ldv-races.json b/conf/ldv-races.json index 01c60efc8d..2840bb368c 100644 --- a/conf/ldv-races.json +++ b/conf/ldv-races.json @@ -53,8 +53,10 @@ } }, "witness": { - "id": "enumerate", - "unknown": false + "graphml": { + "id": "enumerate", + "unknown": false + } }, "solver": "td3", "sem": { diff --git a/conf/svcomp-yaml.json b/conf/svcomp-yaml.json index e09d1c80d7..10a977ff47 100644 --- a/conf/svcomp-yaml.json +++ b/conf/svcomp-yaml.json @@ -76,7 +76,9 @@ "region-offsets": true }, "witness": { - "enabled": false, + "graphml": { + "enabled": false + }, "yaml": { "enabled": true }, diff --git a/conf/svcomp.json b/conf/svcomp.json index 913d43784b..87fef277c3 100644 --- a/conf/svcomp.json +++ b/conf/svcomp.json @@ -90,8 +90,10 @@ } }, "witness": { - "id": "enumerate", - "unknown": false + "graphml": { + "id": "enumerate", + "unknown": false + } }, "pre": { "enabled": false diff --git a/conf/svcomp21.json b/conf/svcomp21.json index a19bfdb9d0..eda5cdfb88 100644 --- a/conf/svcomp21.json +++ b/conf/svcomp21.json @@ -64,6 +64,8 @@ } }, "witness": { - "id": "enumerate" + "graphml": { + "id": "enumerate" + } } } diff --git a/conf/svcomp22-intervals-novareq-affeq-apron.json b/conf/svcomp22-intervals-novareq-affeq-apron.json index 7f72f5d0d8..1dafe0a76d 100644 --- a/conf/svcomp22-intervals-novareq-affeq-apron.json +++ b/conf/svcomp22-intervals-novareq-affeq-apron.json @@ -68,7 +68,9 @@ } }, "witness": { - "id": "enumerate", - "unknown": false + "graphml": { + "id": "enumerate", + "unknown": false + } } } \ No newline at end of file diff --git a/conf/svcomp22-intervals-novareq-affeq-native.json b/conf/svcomp22-intervals-novareq-affeq-native.json index 3ae1b19788..47b5cbbd8f 100644 --- a/conf/svcomp22-intervals-novareq-affeq-native.json +++ b/conf/svcomp22-intervals-novareq-affeq-native.json @@ -65,7 +65,9 @@ } }, "witness": { - "id": "enumerate", - "unknown": false + "graphml": { + "id": "enumerate", + "unknown": false + } } } diff --git a/conf/svcomp22-intervals-novareq-octagon-apron.json b/conf/svcomp22-intervals-novareq-octagon-apron.json index 3bf149800e..c6c7144cf6 100644 --- a/conf/svcomp22-intervals-novareq-octagon-apron.json +++ b/conf/svcomp22-intervals-novareq-octagon-apron.json @@ -68,7 +68,9 @@ } }, "witness": { - "id": "enumerate", - "unknown": false + "graphml": { + "id": "enumerate", + "unknown": false + } } } diff --git a/conf/svcomp22-intervals-novareq-polyhedra-apron.json b/conf/svcomp22-intervals-novareq-polyhedra-apron.json index e4e513415a..e636b6fcdf 100644 --- a/conf/svcomp22-intervals-novareq-polyhedra-apron.json +++ b/conf/svcomp22-intervals-novareq-polyhedra-apron.json @@ -68,7 +68,9 @@ } }, "witness": { - "id": "enumerate", - "unknown": false + "graphml": { + "id": "enumerate", + "unknown": false + } } } diff --git a/conf/svcomp22.json b/conf/svcomp22.json index 85ea693375..09113a38c9 100644 --- a/conf/svcomp22.json +++ b/conf/svcomp22.json @@ -67,7 +67,9 @@ } }, "witness": { - "id": "enumerate", - "unknown": false + "graphml": { + "id": "enumerate", + "unknown": false + } } } diff --git a/conf/svcomp23.json b/conf/svcomp23.json index 56474fbe2b..6f404060ba 100644 --- a/conf/svcomp23.json +++ b/conf/svcomp23.json @@ -90,7 +90,9 @@ } }, "witness": { - "id": "enumerate", - "unknown": false + "graphml": { + "id": "enumerate", + "unknown": false + } } } diff --git a/src/common/util/options.schema.json b/src/common/util/options.schema.json index 400dde06dc..5f2081e8d6 100644 --- a/src/common/util/options.schema.json +++ b/src/common/util/options.schema.json @@ -2279,24 +2279,56 @@ "title": "witness", "type": "object", "properties": { - "enabled": { - "title": "witness.enabled", - "description": "Output witness", - "type": "boolean", - "default": true - }, - "path": { - "title": "witness.path", - "description": "Witness output path", - "type": "string", - "default": "witness.graphml" - }, - "id": { - "title": "witness.id", - "description": "Which witness node IDs to use? node/enumerate", - "type": "string", - "enum": ["node", "enumerate"], - "default": "node" + "graphml": { + "title": "witness.graphml", + "type": "object", + "properties": { + "enabled": { + "title": "witness.graphml.enabled", + "description": "Output GraphML witness", + "type": "boolean", + "default": true + }, + "path": { + "title": "witness.graphml.path", + "description": "GraphML witness output path", + "type": "string", + "default": "witness.graphml" + }, + "id": { + "title": "witness.graphml.id", + "description": "Which witness node IDs to use? node/enumerate", + "type": "string", + "enum": ["node", "enumerate"], + "default": "node" + }, + "minimize": { + "title": "witness.graphml.minimize", + "description": "Try to minimize the witness", + "type": "boolean", + "default": false + }, + "uncil": { + "title": "witness.graphml.uncil", + "description": + "Try to undo CIL control flow transformations in witness", + "type": "boolean", + "default": false + }, + "stack": { + "title": "witness.graphml.stack", + "description": "Construct stacktrace-based witness nodes", + "type": "boolean", + "default": true + }, + "unknown": { + "title": "witness.graphml.unknown", + "description": "Output witness for unknown result", + "type": "boolean", + "default": true + } + }, + "additionalProperties": false }, "invariant": { "title": "witness.invariant", @@ -2376,31 +2408,6 @@ }, "additionalProperties": false }, - "minimize": { - "title": "witness.minimize", - "description": "Try to minimize the witness", - "type": "boolean", - "default": false - }, - "uncil": { - "title": "witness.uncil", - "description": - "Try to undo CIL control flow transformations in witness", - "type": "boolean", - "default": false - }, - "stack": { - "title": "witness.stack", - "description": "Construct stacktrace-based witness nodes", - "type": "boolean", - "default": true - }, - "unknown": { - "title": "witness.unknown", - "description": "Output witness for unknown result", - "type": "boolean", - "default": true - }, "yaml": { "title": "witness.yaml", "type": "object", diff --git a/src/framework/control.ml b/src/framework/control.ml index 9baa2dd1ca..fe43deb45f 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -14,7 +14,7 @@ module type S2S = functor (X : Spec) -> Spec (* spec is lazy, so HConsed table in Hashcons lifters is preserved between analyses in server mode *) let spec_module: (module Spec) Lazy.t = lazy ( GobConfig.building_spec := true; - let arg_enabled = (get_bool "ana.sv-comp.enabled" && get_bool "witness.enabled") || get_bool "exp.arg" in + let arg_enabled = (get_bool "ana.sv-comp.enabled" && get_bool "witness.graphml.enabled") || get_bool "exp.arg" in let open Batteries in (* apply functor F on module X if opt is true *) let lift opt (module F : S2S) (module X : Spec) = (module (val if opt then (module F (X)) else (module X) : Spec) : Spec) in diff --git a/src/witness/myARG.ml b/src/witness/myARG.ml index 62c705f5b1..068aed7a22 100644 --- a/src/witness/myARG.ml +++ b/src/witness/myARG.ml @@ -320,7 +320,7 @@ struct let rec next_opt' n = match n with - | Statement {sid; skind=If (_, _, _, loc, eloc); _} when GobConfig.get_bool "witness.uncil" -> (* TODO: use elocs instead? *) + | Statement {sid; skind=If (_, _, _, loc, eloc); _} when GobConfig.get_bool "witness.graphml.uncil" -> (* TODO: use elocs instead? *) let (e, if_true_next_n, if_false_next_n) = partition_if_next (Arg.next n) in (* avoid infinite recursion with sid <> sid2 in if_nondet_var *) (* TODO: why physical comparison if_false_next_n != n doesn't work? *) @@ -373,7 +373,7 @@ struct Question(e_cond, e_true, e_false, Cilfacade.typeOf e_false) let next_opt' n = match n with - | Statement {skind=If (_, _, _, loc, eloc); _} when GobConfig.get_bool "witness.uncil" -> (* TODO: use eloc instead? *) + | Statement {skind=If (_, _, _, loc, eloc); _} when GobConfig.get_bool "witness.graphml.uncil" -> (* TODO: use eloc instead? *) let (e_cond, if_true_next_n, if_false_next_n) = partition_if_next (Arg.next n) in if Node.location if_true_next_n = loc && Node.location if_false_next_n = loc then match Arg.next if_true_next_n, Arg.next if_false_next_n with diff --git a/src/witness/witness.ml b/src/witness/witness.ml index 9f5a3c1801..2d94f4a18d 100644 --- a/src/witness/witness.ml +++ b/src/witness/witness.ml @@ -13,7 +13,7 @@ let write_file filename (module Task:Task) (module TaskResult:WitnessTaskResult) let module Invariant = WitnessUtil.Invariant (Task) in let module TaskResult = - (val if get_bool "witness.stack" then + (val if get_bool "witness.graphml.stack" then (module StackTaskResult (Task.Cfg) (TaskResult) : WitnessTaskResult) else (module TaskResult) @@ -24,7 +24,7 @@ let write_file filename (module Task:Task) (module TaskResult:WitnessTaskResult) struct (* type node = N.t type edge = TaskResult.Arg.Edge.t *) - let minwitness = get_bool "witness.minimize" + let minwitness = get_bool "witness.graphml.minimize" let is_interesting_real from_node edge to_node = (* TODO: don't duplicate this logic with write_node, write_edge *) (* startlines aren't currently interesting because broken, see below *) @@ -58,12 +58,12 @@ let write_file filename (module Task:Task) (module TaskResult:WitnessTaskResult) let module N = Arg.Node in let module GML = XmlGraphMlWriter in let module GML = - (val match get_string "witness.id" with + (val match get_string "witness.graphml.id" with | "node" -> (module ArgNodeGraphMlWriter (N) (GML) : GraphMlWriter with type node = N.t) | "enumerate" -> (module EnumerateNodeGraphMlWriter (N) (GML)) - | _ -> failwith "witness.id: illegal value" + | _ -> failwith "witness.graphml.id: illegal value" ) in let module GML = DeDupGraphMlWriter (N) (GML) in @@ -305,7 +305,7 @@ struct let determine_result entrystates (module Task:Task): (module WitnessTaskResult) = let module Arg: BiArgInvariant = - (val if GobConfig.get_bool "witness.enabled" then ( + (val if GobConfig.get_bool "witness.graphml.enabled" then ( let module Arg = (val ArgTool.create entrystates) in let module Arg = struct @@ -576,8 +576,8 @@ struct print_task_result (module TaskResult); - if get_bool "witness.enabled" && (TaskResult.result <> Result.Unknown || get_bool "witness.unknown") then ( - let witness_path = get_string "witness.path" in + if get_bool "witness.graphml.enabled" && (TaskResult.result <> Result.Unknown || get_bool "witness.graphml.unknown") then ( + let witness_path = get_string "witness.graphml.path" in Timing.wrap "write" (write_file witness_path (module Task)) (module TaskResult) ) diff --git a/sv-comp/sv-comp-run-no-overflow.py b/sv-comp/sv-comp-run-no-overflow.py index a3461b1a64..88ee2c0e53 100755 --- a/sv-comp/sv-comp-run-no-overflow.py +++ b/sv-comp/sv-comp-run-no-overflow.py @@ -13,7 +13,7 @@ OVERVIEW = False # with True Goblint isn't executed # TODO: don't hard-code specification -GOBLINT_COMMAND = "./goblint --conf conf/svcomp21.json --set ana.specification ./tests/sv-comp/no-overflow.prp --set witness.path {witness_filename} {code_filename} -v" +GOBLINT_COMMAND = "./goblint --conf conf/svcomp21.json --set ana.specification ./tests/sv-comp/no-overflow.prp --set witness.graphml.path {witness_filename} {code_filename} -v" TIMEOUT = 10 # with some int that's Goblint timeout for single execution START = 1 EXIT_ON_ERROR = True diff --git a/sv-comp/sv-comp-run.py b/sv-comp/sv-comp-run.py index af7cada051..977aa69ab6 100755 --- a/sv-comp/sv-comp-run.py +++ b/sv-comp/sv-comp-run.py @@ -13,7 +13,7 @@ OVERVIEW = False # with True Goblint isn't executed # TODO: don't hard-code specification -GOBLINT_COMMAND = "./goblint --conf conf/svcomp21.json --set ana.specification ./tests/sv-comp/unreach-call-__VERIFIER_error.prp --set witness.path {witness_filename} {code_filename}" +GOBLINT_COMMAND = "./goblint --conf conf/svcomp21.json --set ana.specification ./tests/sv-comp/unreach-call-__VERIFIER_error.prp --set witness.graphml.path {witness_filename} {code_filename}" TIMEOUT = 30 # with some int that's Goblint timeout for single execution START = 1 EXIT_ON_ERROR = True diff --git a/tests/sv-comp/observer/path_nofun_true-unreach-call.c b/tests/sv-comp/observer/path_nofun_true-unreach-call.c index 0cb70d23e9..cf1191e9fd 100644 --- a/tests/sv-comp/observer/path_nofun_true-unreach-call.c +++ b/tests/sv-comp/observer/path_nofun_true-unreach-call.c @@ -21,4 +21,4 @@ int main() return 0; } -// ./goblint --enable ana.sv-comp --enable ana.wp --enable witness.uncil --disable ana.int.def_exc --enable ana.int.interval --set ana.activated '["base"]' --html tests/sv-comp/observer/path_nofun_true-unreach-call.c +// ./goblint --enable ana.sv-comp --enable ana.wp --enable witness.graphml.uncil --disable ana.int.def_exc --enable ana.int.interval --set ana.activated '["base"]' --html tests/sv-comp/observer/path_nofun_true-unreach-call.c From 8399258dc31e0f12bbeea04be749f730bac080e0 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 30 Oct 2023 17:01:12 +0200 Subject: [PATCH 0893/1312] Disable witness.graphml.enabled by default --- conf/ldv-races.json | 1 + conf/svcomp.json | 1 + conf/svcomp21.json | 1 + conf/svcomp22-intervals-novareq-affeq-apron.json | 1 + conf/svcomp22-intervals-novareq-affeq-native.json | 1 + conf/svcomp22-intervals-novareq-octagon-apron.json | 1 + conf/svcomp22-intervals-novareq-polyhedra-apron.json | 1 + conf/svcomp22.json | 1 + conf/svcomp23.json | 1 + src/common/util/options.schema.json | 2 +- 10 files changed, 10 insertions(+), 1 deletion(-) diff --git a/conf/ldv-races.json b/conf/ldv-races.json index 2840bb368c..8db800d74c 100644 --- a/conf/ldv-races.json +++ b/conf/ldv-races.json @@ -54,6 +54,7 @@ }, "witness": { "graphml": { + "enabled": true, "id": "enumerate", "unknown": false } diff --git a/conf/svcomp.json b/conf/svcomp.json index 87fef277c3..2c310c076d 100644 --- a/conf/svcomp.json +++ b/conf/svcomp.json @@ -91,6 +91,7 @@ }, "witness": { "graphml": { + "enabled": true, "id": "enumerate", "unknown": false } diff --git a/conf/svcomp21.json b/conf/svcomp21.json index eda5cdfb88..2e36e61d0c 100644 --- a/conf/svcomp21.json +++ b/conf/svcomp21.json @@ -65,6 +65,7 @@ }, "witness": { "graphml": { + "enabled": true, "id": "enumerate" } } diff --git a/conf/svcomp22-intervals-novareq-affeq-apron.json b/conf/svcomp22-intervals-novareq-affeq-apron.json index 1dafe0a76d..f7f7662b6a 100644 --- a/conf/svcomp22-intervals-novareq-affeq-apron.json +++ b/conf/svcomp22-intervals-novareq-affeq-apron.json @@ -69,6 +69,7 @@ }, "witness": { "graphml": { + "enabled": true, "id": "enumerate", "unknown": false } diff --git a/conf/svcomp22-intervals-novareq-affeq-native.json b/conf/svcomp22-intervals-novareq-affeq-native.json index 47b5cbbd8f..00db00f30f 100644 --- a/conf/svcomp22-intervals-novareq-affeq-native.json +++ b/conf/svcomp22-intervals-novareq-affeq-native.json @@ -66,6 +66,7 @@ }, "witness": { "graphml": { + "enabled": true, "id": "enumerate", "unknown": false } diff --git a/conf/svcomp22-intervals-novareq-octagon-apron.json b/conf/svcomp22-intervals-novareq-octagon-apron.json index c6c7144cf6..a0c09e8937 100644 --- a/conf/svcomp22-intervals-novareq-octagon-apron.json +++ b/conf/svcomp22-intervals-novareq-octagon-apron.json @@ -69,6 +69,7 @@ }, "witness": { "graphml": { + "enabled": true, "id": "enumerate", "unknown": false } diff --git a/conf/svcomp22-intervals-novareq-polyhedra-apron.json b/conf/svcomp22-intervals-novareq-polyhedra-apron.json index e636b6fcdf..3a478bf687 100644 --- a/conf/svcomp22-intervals-novareq-polyhedra-apron.json +++ b/conf/svcomp22-intervals-novareq-polyhedra-apron.json @@ -69,6 +69,7 @@ }, "witness": { "graphml": { + "enabled": true, "id": "enumerate", "unknown": false } diff --git a/conf/svcomp22.json b/conf/svcomp22.json index 09113a38c9..316c3c5534 100644 --- a/conf/svcomp22.json +++ b/conf/svcomp22.json @@ -68,6 +68,7 @@ }, "witness": { "graphml": { + "enabled": true, "id": "enumerate", "unknown": false } diff --git a/conf/svcomp23.json b/conf/svcomp23.json index 6f404060ba..af584f1593 100644 --- a/conf/svcomp23.json +++ b/conf/svcomp23.json @@ -91,6 +91,7 @@ }, "witness": { "graphml": { + "enabled": true, "id": "enumerate", "unknown": false } diff --git a/src/common/util/options.schema.json b/src/common/util/options.schema.json index 5f2081e8d6..8255be2b48 100644 --- a/src/common/util/options.schema.json +++ b/src/common/util/options.schema.json @@ -2287,7 +2287,7 @@ "title": "witness.graphml.enabled", "description": "Output GraphML witness", "type": "boolean", - "default": true + "default": false }, "path": { "title": "witness.graphml.path", From a2a4fa2be47c1f2fc2f3eff167c9e57db9e346ee Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 30 Oct 2023 17:11:28 +0200 Subject: [PATCH 0894/1312] Don't output trivial congruence invariant (closes #1218) --- src/cdomains/intDomain.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cdomains/intDomain.ml b/src/cdomains/intDomain.ml index 748df62300..3bc84ae676 100644 --- a/src/cdomains/intDomain.ml +++ b/src/cdomains/intDomain.ml @@ -3274,6 +3274,7 @@ struct let invariant_ikind e ik x = match x with + | x when is_top x -> Invariant.top () | Some (c, m) when m =: Ints_t.zero -> if get_bool "witness.invariant.exact" then let c = Ints_t.to_bigint c in From 783442572bdc03837dd1378c71994e4f53bae360 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 30 Oct 2023 17:27:09 +0200 Subject: [PATCH 0895/1312] Forbid witness.graphml.enabled outside of SV-COMP mode --- src/framework/control.ml | 2 +- src/maingoblint.ml | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/framework/control.ml b/src/framework/control.ml index fe43deb45f..948dce6075 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -14,7 +14,7 @@ module type S2S = functor (X : Spec) -> Spec (* spec is lazy, so HConsed table in Hashcons lifters is preserved between analyses in server mode *) let spec_module: (module Spec) Lazy.t = lazy ( GobConfig.building_spec := true; - let arg_enabled = (get_bool "ana.sv-comp.enabled" && get_bool "witness.graphml.enabled") || get_bool "exp.arg" in + let arg_enabled = get_bool "witness.graphml.enabled" || get_bool "exp.arg" in let open Batteries in (* apply functor F on module X if opt is true *) let lift opt (module F : S2S) (module X : Spec) = (module (val if opt then (module F (X)) else (module X) : Spec) : Spec) in diff --git a/src/maingoblint.ml b/src/maingoblint.ml index b5998df2d1..6c55f43ba1 100644 --- a/src/maingoblint.ml +++ b/src/maingoblint.ml @@ -160,7 +160,8 @@ let check_arguments () = ^ String.concat " and " @@ List.map (fun s -> "'" ^ s ^ "'") imprecise_options) ); if get_bool "solvers.td3.space" && get_bool "solvers.td3.remove-wpoint" then fail "solvers.td3.space is incompatible with solvers.td3.remove-wpoint"; - if get_bool "solvers.td3.space" && get_string "solvers.td3.side_widen" = "sides-local" then fail "solvers.td3.space is incompatible with solvers.td3.side_widen = 'sides-local'" + if get_bool "solvers.td3.space" && get_string "solvers.td3.side_widen" = "sides-local" then fail "solvers.td3.space is incompatible with solvers.td3.side_widen = 'sides-local'"; + if not (get_bool "ana.sv-comp.enabled") && get_bool "witness.graphml.enabled" then fail "witness.graphml.enabled: cannot generate GraphML witness without SV-COMP mode (ana.sv-comp.enabled)" (** Initialize some globals in other modules. *) let handle_flags () = From e01caccff051316ecb3ff26ba176656f06a9fb76 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 30 Oct 2023 17:31:30 +0200 Subject: [PATCH 0896/1312] Disable witness.invariant.accessed by default Makes access analysis more expensive --- src/common/util/options.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/util/options.schema.json b/src/common/util/options.schema.json index 8255be2b48..188e5a77e8 100644 --- a/src/common/util/options.schema.json +++ b/src/common/util/options.schema.json @@ -2365,7 +2365,7 @@ "title": "witness.invariant.accessed", "description": "Only emit invariants for locally accessed variables", "type": "boolean", - "default": true + "default": false }, "full": { "title": "witness.invariant.full", From 6cd62e5163aaaa5deb6ac046d6ab7995e358d1a1 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 30 Oct 2023 17:39:48 +0200 Subject: [PATCH 0897/1312] Update witness timings --- src/witness/witness.ml | 7 ++----- src/witness/yamlWitness.ml | 3 +++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/witness/witness.ml b/src/witness/witness.ml index 2d94f4a18d..af2e1c03ec 100644 --- a/src/witness/witness.ml +++ b/src/witness/witness.ml @@ -572,13 +572,13 @@ struct let write entrystates = let module Task = (val (BatOption.get !task)) in - let module TaskResult = (val (Timing.wrap "determine" (determine_result entrystates) (module Task))) in + let module TaskResult = (val (Timing.wrap "sv-comp result" (determine_result entrystates) (module Task))) in print_task_result (module TaskResult); if get_bool "witness.graphml.enabled" && (TaskResult.result <> Result.Unknown || get_bool "witness.graphml.unknown") then ( let witness_path = get_string "witness.graphml.path" in - Timing.wrap "write" (write_file witness_path (module Task)) (module TaskResult) + Timing.wrap "graphml witness" (write_file witness_path (module Task)) (module TaskResult) ) let write entrystates = @@ -595,7 +595,4 @@ struct ) else write entrystates - - let write entrystates = - Timing.wrap "witness" write entrystates end diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index 72ff21f6bd..9e8ebeff51 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -392,6 +392,9 @@ struct ]; yaml_entries_to_file yaml_entries (Fpath.v (GobConfig.get_string "witness.yaml.path")) + + let write () = + Timing.wrap "yaml witness" write () end From 1cf3942190226126befdac561159c15758153a52 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 31 Oct 2023 16:07:47 +0200 Subject: [PATCH 0898/1312] Make memsafety autotuner enable ana.arrayoob (PR #1201) https://github.com/goblint/analyzer/pull/1201#issuecomment-1787126733 --- src/autoTune.ml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/autoTune.ml b/src/autoTune.ml index 186d930189..b96848c841 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -218,6 +218,7 @@ let focusOnMemSafetySpecification () = enableAnalyses uafAna | ValidDeref -> (* Enable the memOutOfBounds analysis *) let memOobAna = ["memOutOfBounds"] in + set_bool "ana.arrayoob" true; print_endline "Setting \"cil.addNestedScopeAttr\" to true"; set_bool "cil.addNestedScopeAttr" true; print_endline @@ "Specification: ValidDeref -> enabling memOutOfBounds analysis \"" ^ (String.concat ", " memOobAna) ^ "\""; @@ -232,6 +233,7 @@ let focusOnMemSafetySpecification () = print_endline @@ "Specification: ValidMemtrack and ValidMemcleanup -> enabling memLeak analysis \"" ^ (String.concat ", " memLeakAna) ^ "\""; enableAnalyses memLeakAna | MemorySafety -> (* TODO: This is a temporary solution for the memory safety category *) + set_bool "ana.arrayoob" true; (print_endline "Setting \"cil.addNestedScopeAttr\" to true"; set_bool "cil.addNestedScopeAttr" true; if (get_int "ana.malloc.unique_address_count") < 1 then ( From 6131273c1c95bf762749aa7a6a857fec09603def Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 31 Oct 2023 16:10:14 +0200 Subject: [PATCH 0899/1312] Separate memsafetySpecification autotuner and enable in svcomp conf Normal specification autotuner does other things we didn't want in SV-COMP. --- conf/svcomp.json | 3 ++- src/common/util/options.schema.json | 2 +- src/maingoblint.ml | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/conf/svcomp.json b/conf/svcomp.json index 913d43784b..342ac6f298 100644 --- a/conf/svcomp.json +++ b/conf/svcomp.json @@ -70,7 +70,8 @@ "congruence", "octagon", "wideningThresholds", - "loopUnrollHeuristic" + "loopUnrollHeuristic", + "memsafetySpecification" ] } }, diff --git a/src/common/util/options.schema.json b/src/common/util/options.schema.json index 400dde06dc..4d1b012701 100644 --- a/src/common/util/options.schema.json +++ b/src/common/util/options.schema.json @@ -544,7 +544,7 @@ "type": "array", "items": { "type": "string" }, "default": [ - "congruence", "singleThreaded", "specification", "mallocWrappers", "noRecursiveIntervals", "enums", "loopUnrollHeuristic", "arrayDomain", "octagon", "wideningThresholds" + "congruence", "singleThreaded", "specification", "mallocWrappers", "noRecursiveIntervals", "enums", "loopUnrollHeuristic", "arrayDomain", "octagon", "wideningThresholds", "memsafetySpecification" ] } }, diff --git a/src/maingoblint.ml b/src/maingoblint.ml index b5998df2d1..9e0b41fe3c 100644 --- a/src/maingoblint.ml +++ b/src/maingoblint.ml @@ -186,7 +186,7 @@ let handle_options () = check_arguments (); AfterConfig.run (); Sys.set_signal (GobSys.signal_of_string (get_string "dbg.solver-signal")) Signal_ignore; (* Ignore solver-signal before solving (e.g. MyCFG), otherwise exceptions self-signal the default, which crashes instead of printing backtrace. *) - if AutoTune.isActivated "specification" && get_string "ana.specification" <> "" then + if AutoTune.isActivated "memsafetySpecification" && get_string "ana.specification" <> "" then AutoTune.focusOnMemSafetySpecification (); Cilfacade.init_options (); handle_flags () From 4d6b570d6d3c20850fcbe0e95f86638e8595c78b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 31 Oct 2023 16:29:10 +0200 Subject: [PATCH 0900/1312] Add type for SV-COMP multiproperty --- src/autoTune.ml | 21 ++++++++++++++------- src/util/loopUnrolling.ml | 7 ++++--- src/witness/svcomp.ml | 16 +++++++++------- src/witness/svcompSpec.ml | 9 +++++++++ src/witness/witness.ml | 10 +++++++--- 5 files changed, 43 insertions(+), 20 deletions(-) diff --git a/src/autoTune.ml b/src/autoTune.ml index b96848c841..c00564bce7 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -210,8 +210,8 @@ let activateLongjmpAnalysesWhenRequired () = enableAnalyses longjmpAnalyses; ) -let focusOnMemSafetySpecification () = - match Svcomp.Specification.of_option () with +let focusOnMemSafetySpecification (spec: Svcomp.Specification.t) = + match spec with | ValidFree -> (* Enable the useAfterFree analysis *) let uafAna = ["useAfterFree"] in print_endline @@ "Specification: ValidFree -> enabling useAfterFree analysis \"" ^ (String.concat ", " uafAna) ^ "\""; @@ -244,8 +244,11 @@ let focusOnMemSafetySpecification () = enableAnalyses memSafetyAnas) | _ -> () -let focusOnSpecification () = - match Svcomp.Specification.of_option () with +let focusOnMemSafetySpecification () = + List.iter focusOnMemSafetySpecification (Svcomp.Specification.of_option ()) + +let focusOnSpecification (spec: Svcomp.Specification.t) = + match spec with | UnreachCall s -> () | NoDataRace -> (*enable all thread analyses*) print_endline @@ "Specification: NoDataRace -> enabling thread analyses \"" ^ (String.concat ", " notNeccessaryThreadAnalyses) ^ "\""; @@ -255,6 +258,9 @@ let focusOnSpecification () = set_bool "ana.int.interval" true | _ -> () +let focusOnSpecification () = + List.iter focusOnSpecification (Svcomp.Specification.of_option ()) + (*Detect enumerations and enable the "ana.int.enums" option*) exception EnumFound class enumVisitor = object @@ -411,9 +417,10 @@ let congruenceOption factors file = let apronOctagonOption factors file = let locals = if List.mem "specification" (get_string_list "ana.autotune.activated" ) && get_string "ana.specification" <> "" then - match Svcomp.Specification.of_option () with - | NoOverflow -> 12 - | _ -> 8 + if List.mem Svcomp.Specification.NoOverflow (Svcomp.Specification.of_option ()) then + 12 + else + 8 else 8 in let globals = 2 in let selectedLocals = diff --git a/src/util/loopUnrolling.ml b/src/util/loopUnrolling.ml index 62d0f662f3..9a2f6c7b29 100644 --- a/src/util/loopUnrolling.ml +++ b/src/util/loopUnrolling.ml @@ -324,9 +324,10 @@ class loopUnrollingCallVisitor = object raise Found; | _ -> if List.mem "specification" @@ get_string_list "ana.autotune.activated" && get_string "ana.specification" <> "" then ( - match SvcompSpec.of_option () with - | UnreachCall s -> if info.vname = s then raise Found - | _ -> () + List.iter (function + | SvcompSpec.UnreachCall s -> if info.vname = s then raise Found + | _ -> () + ) (SvcompSpec.of_option ()) ); DoChildren ) diff --git a/src/witness/svcomp.ml b/src/witness/svcomp.ml index 22543d48a9..6d773f666b 100644 --- a/src/witness/svcomp.ml +++ b/src/witness/svcomp.ml @@ -8,7 +8,7 @@ module Specification = SvcompSpec module type Task = sig val file: Cil.file - val specification: Specification.t + val specification: Specification.multi module Cfg: MyCFG.CfgBidir end @@ -18,9 +18,10 @@ let task: (module Task) option ref = ref None let is_error_function f = let module Task = (val (Option.get !task)) in - match Task.specification with - | UnreachCall f_spec -> f.vname = f_spec - | _ -> false + List.exists (function + | Specification.UnreachCall f_spec -> f.vname = f_spec + | _ -> false + ) Task.specification (* TODO: unused, but should be used? *) let is_special_function f = @@ -30,9 +31,10 @@ let is_special_function f = | fname when String.starts_with fname "__VERIFIER" -> true | fname -> let module Task = (val (Option.get !task)) in - match Task.specification with - | UnreachCall f_spec -> fname = f_spec - | _ -> false + List.exists (function + | Specification.UnreachCall f_spec -> fname = f_spec + | _ -> false + ) Task.specification in is_svcomp && is_verifier diff --git a/src/witness/svcompSpec.ml b/src/witness/svcompSpec.ml index 4a3da23d9b..185f1fbf67 100644 --- a/src/witness/svcompSpec.ml +++ b/src/witness/svcompSpec.ml @@ -12,6 +12,8 @@ type t = | MemorySafety (* Internal property for use in Goblint; serves as a summary for ValidFree, ValidDeref and ValidMemtrack *) | ValidMemcleanup +type multi = t list + let of_string s = let s = String.strip s in let regexp_multiple = Str.regexp "CHECK( init(main()), LTL(G \\(.*\\)) )\nCHECK( init(main()), LTL(G \\(.*\\)) )\nCHECK( init(main()), LTL(G \\(.*\\)) )" in @@ -48,6 +50,8 @@ let of_string s = else failwith "Svcomp.Specification.of_string: unknown expression" +let of_string s: multi = [of_string s] + let of_file path = let s = BatFile.with_file_in path BatIO.read_all in of_string s @@ -77,3 +81,8 @@ let to_string spec = | ValidMemcleanup -> "valid-memcleanup", false in print_output spec_str is_neg + +let to_string spec = + match spec with + | [spec] -> to_string spec + | _ -> assert false (* TODO: aggregate *) diff --git a/src/witness/witness.ml b/src/witness/witness.ml index 9f5a3c1801..310717b9c3 100644 --- a/src/witness/witness.ml +++ b/src/witness/witness.ml @@ -303,7 +303,7 @@ struct val find_invariant: Node.t -> Invariant.t end - let determine_result entrystates (module Task:Task): (module WitnessTaskResult) = + let determine_result entrystates (module Task:Task) (spec: Svcomp.Specification.t): (module WitnessTaskResult) = let module Arg: BiArgInvariant = (val if GobConfig.get_bool "witness.enabled" then ( let module Arg = (val ArgTool.create entrystates) in @@ -338,7 +338,7 @@ struct ) in - match Task.specification with + match spec with | UnreachCall _ -> (* error function name is globally known through Svcomp.task *) let is_unreach_call = @@ -410,7 +410,7 @@ struct let module TaskResult = struct module Arg = PathArg - let result = Result.False (Some Task.specification) + let result = Result.False (Some spec) let invariant _ = Invariant.none let is_violation = is_violation let is_sink _ = false @@ -569,6 +569,10 @@ struct (module TaskResult:WitnessTaskResult) ) + let determine_result entrystates (module Task:Task): (module WitnessTaskResult) = + match Task.specification with + | [spec] -> determine_result entrystates (module Task) spec + | _ -> assert false (* TODO: aggregate *) let write entrystates = let module Task = (val (BatOption.get !task)) in From 3747556e90acdced4e01fc21e9c83107d10f93fc Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 31 Oct 2023 17:23:40 +0200 Subject: [PATCH 0901/1312] Remove special MemorySafety SV-COMP property, add full multiproperty handling --- src/autoTune.ml | 10 ---------- src/witness/svcomp.ml | 2 +- src/witness/svcompSpec.ml | 34 +++++++++++++++++----------------- src/witness/witness.ml | 27 ++++++++++++++++++++++----- 4 files changed, 40 insertions(+), 33 deletions(-) diff --git a/src/autoTune.ml b/src/autoTune.ml index c00564bce7..06347f3190 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -232,16 +232,6 @@ let focusOnMemSafetySpecification (spec: Svcomp.Specification.t) = ); print_endline @@ "Specification: ValidMemtrack and ValidMemcleanup -> enabling memLeak analysis \"" ^ (String.concat ", " memLeakAna) ^ "\""; enableAnalyses memLeakAna - | MemorySafety -> (* TODO: This is a temporary solution for the memory safety category *) - set_bool "ana.arrayoob" true; - (print_endline "Setting \"cil.addNestedScopeAttr\" to true"; - set_bool "cil.addNestedScopeAttr" true; - if (get_int "ana.malloc.unique_address_count") < 1 then ( - print_endline "Setting \"ana.malloc.unique_address_count\" to 1"; - set_int "ana.malloc.unique_address_count" 1; - ); - let memSafetyAnas = ["memOutOfBounds"; "memLeak"; "useAfterFree";] in - enableAnalyses memSafetyAnas) | _ -> () let focusOnMemSafetySpecification () = diff --git a/src/witness/svcomp.ml b/src/witness/svcomp.ml index 6d773f666b..736de0efae 100644 --- a/src/witness/svcomp.ml +++ b/src/witness/svcomp.ml @@ -45,6 +45,7 @@ struct | True | False of Specification.t option | Unknown + [@@deriving ord] let to_string = function | True -> "true" @@ -57,7 +58,6 @@ struct | ValidFree -> "valid-free" | ValidDeref -> "valid-deref" | ValidMemtrack -> "valid-memtrack" - | MemorySafety -> "memory-safety" (* TODO: Currently here only to complete the pattern match *) | ValidMemcleanup -> "valid-memcleanup" in "false(" ^ result_spec ^ ")" diff --git a/src/witness/svcompSpec.ml b/src/witness/svcompSpec.ml index 185f1fbf67..9bd5a35e3e 100644 --- a/src/witness/svcompSpec.ml +++ b/src/witness/svcompSpec.ml @@ -9,14 +9,13 @@ type t = | ValidFree | ValidDeref | ValidMemtrack - | MemorySafety (* Internal property for use in Goblint; serves as a summary for ValidFree, ValidDeref and ValidMemtrack *) | ValidMemcleanup +[@@deriving ord] type multi = t list let of_string s = let s = String.strip s in - let regexp_multiple = Str.regexp "CHECK( init(main()), LTL(G \\(.*\\)) )\nCHECK( init(main()), LTL(G \\(.*\\)) )\nCHECK( init(main()), LTL(G \\(.*\\)) )" in let regexp_single = Str.regexp "CHECK( init(main()), LTL(G \\(.*\\)) )" in let regexp_negated = Str.regexp "CHECK( init(main()), LTL(G ! \\(.*\\)) )" in if Str.string_match regexp_negated s 0 then @@ -32,25 +31,29 @@ let of_string s = UnreachCall f else failwith "Svcomp.Specification.of_string: unknown global not expression" - else if Str.string_match regexp_multiple s 0 then - let global1 = Str.matched_group 1 s in - let global2 = Str.matched_group 2 s in - let global3 = Str.matched_group 3 s in - let mem_safety_props = ["valid-free"; "valid-deref"; "valid-memtrack";] in - if (global1 <> global2 && global1 <> global3 && global2 <> global3) && List.for_all (fun x -> List.mem x mem_safety_props) [global1; global2; global3] then - MemorySafety - else - failwith "Svcomp.Specification.of_string: unknown global expression" else if Str.string_match regexp_single s 0 then let global = Str.matched_group 1 s in - if global = "valid-memcleanup" then + if global = "valid-free" then + ValidFree + else if global = "valid-deref" then + ValidDeref + else if global = "valid-memtrack" then + ValidMemtrack + else if global = "valid-memcleanup" then ValidMemcleanup else failwith "Svcomp.Specification.of_string: unknown global expression" else failwith "Svcomp.Specification.of_string: unknown expression" -let of_string s: multi = [of_string s] +let of_string s: multi = + List.filter_map (fun line -> + let line = String.strip line in + if line = "" then + None + else + Some (of_string line) + ) (String.split_on_char '\n' s) let of_file path = let s = BatFile.with_file_in path BatIO.read_all in @@ -77,12 +80,9 @@ let to_string spec = | ValidFree -> "valid-free", false | ValidDeref -> "valid-deref", false | ValidMemtrack -> "valid-memtrack", false - | MemorySafety -> "memory-safety", false (* TODO: That's false, it's currently here just to complete the pattern match *) | ValidMemcleanup -> "valid-memcleanup", false in print_output spec_str is_neg let to_string spec = - match spec with - | [spec] -> to_string spec - | _ -> assert false (* TODO: aggregate *) + String.concat "\n" (List.map to_string spec) diff --git a/src/witness/witness.ml b/src/witness/witness.ml index 310717b9c3..419185400c 100644 --- a/src/witness/witness.ml +++ b/src/witness/witness.ml @@ -507,8 +507,7 @@ struct ) | ValidFree | ValidDeref - | ValidMemtrack - | MemorySafety -> + | ValidMemtrack -> let module TrivialArg = struct include Arg @@ -570,9 +569,27 @@ struct ) let determine_result entrystates (module Task:Task): (module WitnessTaskResult) = - match Task.specification with - | [spec] -> determine_result entrystates (module Task) spec - | _ -> assert false (* TODO: aggregate *) + Task.specification + |> List.fold_left (fun acc spec -> + let module TaskResult = (val determine_result entrystates (module Task) spec) in + match acc with + | None -> Some (module TaskResult: WitnessTaskResult) + | Some (module Acc: WitnessTaskResult) -> + match Acc.result, TaskResult.result with + (* keep old violation/unknown *) + | False _, True + | False _, Unknown + | Unknown, True -> Some (module Acc: WitnessTaskResult) + (* use new violation/unknown *) + | True, False _ + | Unknown, False _ + | True, Unknown -> Some (module TaskResult: WitnessTaskResult) + (* both same, arbitrarily keep old *) + | True, True -> Some (module Acc: WitnessTaskResult) + | Unknown, Unknown -> Some (module Acc: WitnessTaskResult) + | False _, False _ -> failwith "multiple violations" + ) None + |> Option.get let write entrystates = let module Task = (val (BatOption.get !task)) in From 5093b5dd90a6ec7aca4c541e007c7d4f3025b707 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 31 Oct 2023 17:25:11 +0200 Subject: [PATCH 0902/1312] Fix witness determine_result for memsafety --- src/witness/witness.ml | 64 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 3 deletions(-) diff --git a/src/witness/witness.ml b/src/witness/witness.ml index 419185400c..dd829dd9e2 100644 --- a/src/witness/witness.ml +++ b/src/witness/witness.ml @@ -505,8 +505,66 @@ struct in (module TaskResult:WitnessTaskResult) ) - | ValidFree - | ValidDeref + | ValidFree -> + let module TrivialArg = + struct + include Arg + let next _ = [] + end + in + if not !AnalysisState.svcomp_may_invalid_free then ( + let module TaskResult = + struct + module Arg = Arg + let result = Result.True + let invariant _ = Invariant.none + let is_violation _ = false + let is_sink _ = false + end + in + (module TaskResult:WitnessTaskResult) + ) else ( + let module TaskResult = + struct + module Arg = TrivialArg + let result = Result.Unknown + let invariant _ = Invariant.none + let is_violation _ = false + let is_sink _ = false + end + in + (module TaskResult:WitnessTaskResult) + ) + | ValidDeref -> + let module TrivialArg = + struct + include Arg + let next _ = [] + end + in + if not !AnalysisState.svcomp_may_invalid_deref then ( + let module TaskResult = + struct + module Arg = Arg + let result = Result.True + let invariant _ = Invariant.none + let is_violation _ = false + let is_sink _ = false + end + in + (module TaskResult:WitnessTaskResult) + ) else ( + let module TaskResult = + struct + module Arg = TrivialArg + let result = Result.Unknown + let invariant _ = Invariant.none + let is_violation _ = false + let is_sink _ = false + end + in + (module TaskResult:WitnessTaskResult) + ) | ValidMemtrack -> let module TrivialArg = struct @@ -514,7 +572,7 @@ struct let next _ = [] end in - if not !AnalysisState.svcomp_may_invalid_free && not !AnalysisState.svcomp_may_invalid_deref && not !AnalysisState.svcomp_may_invalid_memtrack then ( + if not !AnalysisState.svcomp_may_invalid_memtrack then ( let module TaskResult = struct module Arg = Arg From bb163a55ba1e06afcd267a02e521a4907f694db2 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 31 Oct 2023 19:13:04 +0200 Subject: [PATCH 0903/1312] Deduplicate Svcomp.is_error_function --- src/util/loopUnrolling.ml | 6 ++---- src/witness/svcomp.ml | 15 +++++++-------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/util/loopUnrolling.ml b/src/util/loopUnrolling.ml index 9a2f6c7b29..4ce8fc06b4 100644 --- a/src/util/loopUnrolling.ml +++ b/src/util/loopUnrolling.ml @@ -324,10 +324,8 @@ class loopUnrollingCallVisitor = object raise Found; | _ -> if List.mem "specification" @@ get_string_list "ana.autotune.activated" && get_string "ana.specification" <> "" then ( - List.iter (function - | SvcompSpec.UnreachCall s -> if info.vname = s then raise Found - | _ -> () - ) (SvcompSpec.of_option ()) + if Svcomp.is_error_function' info (SvcompSpec.of_option ()) then + raise Found ); DoChildren ) diff --git a/src/witness/svcomp.ml b/src/witness/svcomp.ml index 736de0efae..218f0716ae 100644 --- a/src/witness/svcomp.ml +++ b/src/witness/svcomp.ml @@ -16,12 +16,16 @@ end let task: (module Task) option ref = ref None -let is_error_function f = +let is_error_function' f spec = let module Task = (val (Option.get !task)) in List.exists (function | Specification.UnreachCall f_spec -> f.vname = f_spec | _ -> false - ) Task.specification + ) spec + +let is_error_function f = + let module Task = (val (Option.get !task)) in + is_error_function' f Task.specification (* TODO: unused, but should be used? *) let is_special_function f = @@ -29,12 +33,7 @@ let is_special_function f = let is_svcomp = String.ends_with loc.file "sv-comp.c" in (* only includes/sv-comp.c functions, not __VERIFIER_assert in benchmark *) let is_verifier = match f.vname with | fname when String.starts_with fname "__VERIFIER" -> true - | fname -> - let module Task = (val (Option.get !task)) in - List.exists (function - | Specification.UnreachCall f_spec -> fname = f_spec - | _ -> false - ) Task.specification + | fname -> is_error_function f in is_svcomp && is_verifier From 35f9323854d7e358a63b374c0bad3ced5cc1d4ed Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 1 Nov 2023 12:10:56 +0200 Subject: [PATCH 0904/1312] Use threadflag path-sensitivity instead of threadid in svcomp conf This is enough for ldv-races/race-2_1-container_of, etc, but cheaper. --- conf/svcomp.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/svcomp.json b/conf/svcomp.json index df624e4b83..d17e1a5f1e 100644 --- a/conf/svcomp.json +++ b/conf/svcomp.json @@ -38,7 +38,7 @@ "uninit", "expsplit", "activeSetjmp", - "threadid" + "threadflag" ], "context": { "widen": false From f4b37100d7a9edd544518b434aa39950ef1edb88 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 1 Nov 2023 12:22:46 +0200 Subject: [PATCH 0905/1312] Copy svcomp conf to svcomp24 --- conf/svcomp24.json | 116 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 conf/svcomp24.json diff --git a/conf/svcomp24.json b/conf/svcomp24.json new file mode 100644 index 0000000000..178035eb8a --- /dev/null +++ b/conf/svcomp24.json @@ -0,0 +1,116 @@ +{ + "ana": { + "sv-comp": { + "enabled": true, + "functions": true + }, + "int": { + "def_exc": true, + "enums": false, + "interval": true + }, + "float": { + "interval": true + }, + "activated": [ + "base", + "threadid", + "threadflag", + "threadreturn", + "mallocWrapper", + "mutexEvents", + "mutex", + "access", + "race", + "escape", + "expRelation", + "mhp", + "assert", + "var_eq", + "symb_locks", + "region", + "thread", + "threadJoins" + ], + "path_sens": [ + "mutex", + "malloc_null", + "uninit", + "expsplit", + "activeSetjmp", + "threadflag" + ], + "context": { + "widen": false + }, + "malloc": { + "wrappers": [ + "kmalloc", + "__kmalloc", + "usb_alloc_urb", + "__builtin_alloca", + "kzalloc", + + "ldv_malloc", + + "kzalloc_node", + "ldv_zalloc", + "kmalloc_array", + "kcalloc", + + "ldv_xmalloc", + "ldv_xzalloc", + "ldv_calloc", + "ldv_kzalloc" + ] + }, + "base": { + "arrays": { + "domain": "partitioned" + } + }, + "race": { + "free": false, + "call": false + }, + "autotune": { + "enabled": true, + "activated": [ + "singleThreaded", + "mallocWrappers", + "noRecursiveIntervals", + "enums", + "congruence", + "octagon", + "wideningThresholds", + "loopUnrollHeuristic", + "memsafetySpecification" + ] + } + }, + "exp": { + "region-offsets": true + }, + "solver": "td3", + "sem": { + "unknown_function": { + "spawn": false + }, + "int": { + "signed_overflow": "assume_none" + }, + "null-pointer": { + "dereference": "assume_none" + } + }, + "witness": { + "graphml": { + "enabled": true, + "id": "enumerate", + "unknown": false + } + }, + "pre": { + "enabled": false + } +} From 529a415a03f50dcd69ca4869b5dddf4194535638 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 1 Nov 2023 12:56:11 +0200 Subject: [PATCH 0906/1312] Add YAML witness generation to svcomp confs --- conf/svcomp.json | 23 +++++++++++++++++++++++ conf/svcomp24.json | 23 +++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/conf/svcomp.json b/conf/svcomp.json index 178035eb8a..77f519a568 100644 --- a/conf/svcomp.json +++ b/conf/svcomp.json @@ -108,6 +108,29 @@ "enabled": true, "id": "enumerate", "unknown": false + }, + "yaml": { + "enabled": true, + "entry-types": [ + "loop_invariant" + ] + }, + "invariant": { + "loop-head": true, + "after-lock": false, + "other": false, + "accessed": false, + "exact": true, + "exclude-vars": [ + "tmp\\(___[0-9]+\\)?", + "cond", + "RETURN", + "__\\(cil_\\)?tmp_?[0-9]*\\(_[0-9]+\\)?", + ".*____CPAchecker_TMP_[0-9]+", + "__VERIFIER_assert__cond", + "__ksymtab_.*", + "\\(ldv_state_variable\\|ldv_timer_state\\|ldv_timer_list\\|ldv_irq_\\(line_\\|data_\\)?[0-9]+\\|ldv_retval\\)_[0-9]+" + ] } }, "pre": { diff --git a/conf/svcomp24.json b/conf/svcomp24.json index 178035eb8a..77f519a568 100644 --- a/conf/svcomp24.json +++ b/conf/svcomp24.json @@ -108,6 +108,29 @@ "enabled": true, "id": "enumerate", "unknown": false + }, + "yaml": { + "enabled": true, + "entry-types": [ + "loop_invariant" + ] + }, + "invariant": { + "loop-head": true, + "after-lock": false, + "other": false, + "accessed": false, + "exact": true, + "exclude-vars": [ + "tmp\\(___[0-9]+\\)?", + "cond", + "RETURN", + "__\\(cil_\\)?tmp_?[0-9]*\\(_[0-9]+\\)?", + ".*____CPAchecker_TMP_[0-9]+", + "__VERIFIER_assert__cond", + "__ksymtab_.*", + "\\(ldv_state_variable\\|ldv_timer_state\\|ldv_timer_list\\|ldv_irq_\\(line_\\|data_\\)?[0-9]+\\|ldv_retval\\)_[0-9]+" + ] } }, "pre": { From ab9eacc13ba44deea33edd146615914cdf2d544e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 1 Nov 2023 12:56:33 +0200 Subject: [PATCH 0907/1312] Copy svcomp24 conf to svcomp24-validate --- conf/svcomp24-validate.json | 139 ++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 conf/svcomp24-validate.json diff --git a/conf/svcomp24-validate.json b/conf/svcomp24-validate.json new file mode 100644 index 0000000000..77f519a568 --- /dev/null +++ b/conf/svcomp24-validate.json @@ -0,0 +1,139 @@ +{ + "ana": { + "sv-comp": { + "enabled": true, + "functions": true + }, + "int": { + "def_exc": true, + "enums": false, + "interval": true + }, + "float": { + "interval": true + }, + "activated": [ + "base", + "threadid", + "threadflag", + "threadreturn", + "mallocWrapper", + "mutexEvents", + "mutex", + "access", + "race", + "escape", + "expRelation", + "mhp", + "assert", + "var_eq", + "symb_locks", + "region", + "thread", + "threadJoins" + ], + "path_sens": [ + "mutex", + "malloc_null", + "uninit", + "expsplit", + "activeSetjmp", + "threadflag" + ], + "context": { + "widen": false + }, + "malloc": { + "wrappers": [ + "kmalloc", + "__kmalloc", + "usb_alloc_urb", + "__builtin_alloca", + "kzalloc", + + "ldv_malloc", + + "kzalloc_node", + "ldv_zalloc", + "kmalloc_array", + "kcalloc", + + "ldv_xmalloc", + "ldv_xzalloc", + "ldv_calloc", + "ldv_kzalloc" + ] + }, + "base": { + "arrays": { + "domain": "partitioned" + } + }, + "race": { + "free": false, + "call": false + }, + "autotune": { + "enabled": true, + "activated": [ + "singleThreaded", + "mallocWrappers", + "noRecursiveIntervals", + "enums", + "congruence", + "octagon", + "wideningThresholds", + "loopUnrollHeuristic", + "memsafetySpecification" + ] + } + }, + "exp": { + "region-offsets": true + }, + "solver": "td3", + "sem": { + "unknown_function": { + "spawn": false + }, + "int": { + "signed_overflow": "assume_none" + }, + "null-pointer": { + "dereference": "assume_none" + } + }, + "witness": { + "graphml": { + "enabled": true, + "id": "enumerate", + "unknown": false + }, + "yaml": { + "enabled": true, + "entry-types": [ + "loop_invariant" + ] + }, + "invariant": { + "loop-head": true, + "after-lock": false, + "other": false, + "accessed": false, + "exact": true, + "exclude-vars": [ + "tmp\\(___[0-9]+\\)?", + "cond", + "RETURN", + "__\\(cil_\\)?tmp_?[0-9]*\\(_[0-9]+\\)?", + ".*____CPAchecker_TMP_[0-9]+", + "__VERIFIER_assert__cond", + "__ksymtab_.*", + "\\(ldv_state_variable\\|ldv_timer_state\\|ldv_timer_list\\|ldv_irq_\\(line_\\|data_\\)?[0-9]+\\|ldv_retval\\)_[0-9]+" + ] + } + }, + "pre": { + "enabled": false + } +} From 95ee32e6ea1bcb0a7075e9cea1f439d45f0d4965 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 1 Nov 2023 13:08:21 +0200 Subject: [PATCH 0908/1312] Add YAML witness validation to svcomp24-validate conf --- conf/svcomp24-validate.json | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/conf/svcomp24-validate.json b/conf/svcomp24-validate.json index 77f519a568..3727c3e9f8 100644 --- a/conf/svcomp24-validate.json +++ b/conf/svcomp24-validate.json @@ -12,6 +12,10 @@ "float": { "interval": true }, + "apron": { + "domain": "polyhedra", + "strengthening": true + }, "activated": [ "base", "threadid", @@ -30,7 +34,9 @@ "symb_locks", "region", "thread", - "threadJoins" + "threadJoins", + "unassume", + "apron" ], "path_sens": [ "mutex", @@ -86,6 +92,9 @@ "loopUnrollHeuristic", "memsafetySpecification" ] + }, + "widen": { + "tokens": true } }, "exp": { @@ -105,32 +114,19 @@ }, "witness": { "graphml": { - "enabled": true, - "id": "enumerate", - "unknown": false + "enabled": false }, "yaml": { - "enabled": true, + "enabled": false, "entry-types": [ + "location_invariant", "loop_invariant" ] }, "invariant": { "loop-head": true, - "after-lock": false, - "other": false, - "accessed": false, - "exact": true, - "exclude-vars": [ - "tmp\\(___[0-9]+\\)?", - "cond", - "RETURN", - "__\\(cil_\\)?tmp_?[0-9]*\\(_[0-9]+\\)?", - ".*____CPAchecker_TMP_[0-9]+", - "__VERIFIER_assert__cond", - "__ksymtab_.*", - "\\(ldv_state_variable\\|ldv_timer_state\\|ldv_timer_list\\|ldv_irq_\\(line_\\|data_\\)?[0-9]+\\|ldv_retval\\)_[0-9]+" - ] + "after-lock": true, + "other": true } }, "pre": { From ce917e639d88c0777cd929de635c030fe060cf2b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 1 Nov 2023 13:13:52 +0200 Subject: [PATCH 0909/1312] Update sv-comp/archive.sh for 2024 --- sv-comp/archive.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sv-comp/archive.sh b/sv-comp/archive.sh index 9bab49f70d..87dcd75eb9 100755 --- a/sv-comp/archive.sh +++ b/sv-comp/archive.sh @@ -4,7 +4,7 @@ make clean -git tag -m "SV-COMP 2023" svcomp23 +git tag -m "SV-COMP 2024" svcomp24 dune build --profile=release src/goblint.exe rm -f goblint @@ -32,7 +32,8 @@ zip goblint/sv-comp/goblint.zip \ goblint/lib/libboxD.so \ goblint/lib/libpolkaMPQ.so \ goblint/lib/LICENSE.APRON \ - goblint/conf/svcomp23.json \ + goblint/conf/svcomp24.json \ + goblint/conf/svcomp24-validate.json \ goblint/lib/libc/stub/include/assert.h \ goblint/lib/goblint/runtime/include/goblint.h \ goblint/lib/libc/stub/src/stdlib.c \ From 6bad00c305c18578af314b3f1b83ee76aa23f8a6 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 1 Nov 2023 13:22:09 +0200 Subject: [PATCH 0910/1312] Fix Apron license for unpinned package for SV-COMP --- sv-comp/archive.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sv-comp/archive.sh b/sv-comp/archive.sh index 87dcd75eb9..5d8605dc70 100755 --- a/sv-comp/archive.sh +++ b/sv-comp/archive.sh @@ -18,7 +18,7 @@ cp _opam/share/apron/lib/libapron.so lib/ cp _opam/share/apron/lib/liboctD.so lib/ cp _opam/share/apron/lib/libboxD.so lib/ cp _opam/share/apron/lib/libpolkaMPQ.so lib/ -cp _opam/.opam-switch/sources/apron/COPYING lib/LICENSE.APRON +wget -O lib/LICENSE.APRON https://raw.githubusercontent.com/antoinemine/apron/master/COPYING # done outside to ensure archive contains goblint/ directory cd .. From 0f1389808ff3a848a9d6e6484df3f381860c7ddc Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Wed, 1 Nov 2023 17:14:03 +0200 Subject: [PATCH 0911/1312] Fix indentation --- src/analyses/apron/relationPriv.apron.ml | 22 +++++++++++----------- src/analyses/basePriv.ml | 22 +++++++++++----------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/analyses/apron/relationPriv.apron.ml b/src/analyses/apron/relationPriv.apron.ml index 3adfa272bb..2baf4cdca8 100644 --- a/src/analyses/apron/relationPriv.apron.ml +++ b/src/analyses/apron/relationPriv.apron.ml @@ -1011,17 +1011,17 @@ struct ) ) else ( - if ConcDomain.ThreadSet.is_top tids - then st - else - match ConcDomain.ThreadSet.elements tids with - | [tid] -> - let lmust',l' = G.thread (getg (V.thread tid)) in - {st with priv = (w, LMust.union lmust' lmust, L.join l l')} - | _ -> - (* To match the paper more closely, one would have to join in the non-definite case too *) - (* Given how we handle lmust (for initialization), doing this might actually be beneficial given that it grows lmust *) - st + if ConcDomain.ThreadSet.is_top tids then + st + else + match ConcDomain.ThreadSet.elements tids with + | [tid] -> + let lmust',l' = G.thread (getg (V.thread tid)) in + {st with priv = (w, LMust.union lmust' lmust, L.join l l')} + | _ -> + (* To match the paper more closely, one would have to join in the non-definite case too *) + (* Given how we handle lmust (for initialization), doing this might actually be beneficial given that it grows lmust *) + st ) let thread_return ask getg sideg tid (st: relation_components_t) = diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index ed6439a847..013a48a2d6 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -544,17 +544,17 @@ struct ) ) else ( - if (ConcDomain.ThreadSet.is_top tids) - then st - else - match ConcDomain.ThreadSet.elements tids with - | [tid] -> - let lmust',l' = G.thread (getg (V.thread tid)) in - {st with priv = (w, LMust.union lmust' lmust, L.join l l')} - | _ -> - (* To match the paper more closely, one would have to join in the non-definite case too *) - (* Given how we handle lmust (for initialization), doing this might actually be beneficial given that it grows lmust *) - st + if ConcDomain.ThreadSet.is_top tids then + st + else + match ConcDomain.ThreadSet.elements tids with + | [tid] -> + let lmust',l' = G.thread (getg (V.thread tid)) in + {st with priv = (w, LMust.union lmust' lmust, L.join l l')} + | _ -> + (* To match the paper more closely, one would have to join in the non-definite case too *) + (* Given how we handle lmust (for initialization), doing this might actually be beneficial given that it grows lmust *) + st ) let thread_return ask getg sideg tid (st: BaseComponents (D).t) = From 031dde1e9538335a0691462a946b571866c1674b Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 1 Nov 2023 16:58:38 +0100 Subject: [PATCH 0912/1312] Add test for joinign thread array --- tests/regression/10-synch/28-join-array.c | 25 +++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 tests/regression/10-synch/28-join-array.c diff --git a/tests/regression/10-synch/28-join-array.c b/tests/regression/10-synch/28-join-array.c new file mode 100644 index 0000000000..99813b9810 --- /dev/null +++ b/tests/regression/10-synch/28-join-array.c @@ -0,0 +1,25 @@ +// PARAM: --set ana.activated[+] thread +#include + +int data = 0; +pthread_mutex_t data_mutex; + +void *thread(void *arg) { + pthread_mutex_lock(&data_mutex); + data = 3; // RACE! + pthread_mutex_unlock(&data_mutex); + return NULL; +} + +int main() { + pthread_t tids[2]; + + pthread_create(&tids[0], NULL, &thread, NULL); + pthread_create(&tids[1], NULL, &thread, NULL); + + pthread_join(tids[0], NULL); + + data = 1; //RACE! + + return 1; +} From 1d55756147f3dba8cc5f42a996ae3ffaf7c6dbce Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 1 Nov 2023 16:59:08 +0100 Subject: [PATCH 0913/1312] threadAnalysis: Only add definite tids to set of mustJoined thread --- src/analyses/threadAnalysis.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/analyses/threadAnalysis.ml b/src/analyses/threadAnalysis.ml index 1e679a4707..1f6e9fabb3 100644 --- a/src/analyses/threadAnalysis.ml +++ b/src/analyses/threadAnalysis.ml @@ -61,7 +61,8 @@ struct s in match TS.elements (ctx.ask (Queries.EvalThread id)) with - | threads -> List.fold_left join_thread ctx.local threads + | [t] -> join_thread ctx.local t (* single thread *) + | _ -> ctx.local (* if several possible threads are may-joined, none are must-joined *) | exception SetDomain.Unsupported _ -> ctx.local) | _ -> ctx.local From a7d02c42ddcdb21c5042b28d0af7d39a0058a6cf Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 1 Nov 2023 18:25:55 +0100 Subject: [PATCH 0914/1312] Simplify warning --- src/analyses/loopTermination.ml | 8 ++------ src/framework/constraints.ml | 14 +++----------- src/framework/control.ml | 7 +------ 3 files changed, 6 insertions(+), 23 deletions(-) diff --git a/src/analyses/loopTermination.ml b/src/analyses/loopTermination.ml index 61034a57c0..077615ad10 100644 --- a/src/analyses/loopTermination.ml +++ b/src/analyses/loopTermination.ml @@ -64,12 +64,8 @@ struct ctx.sideg () (G.add (`Lifted loop_statement) is_bounded (ctx.global ())); (* In case the loop is not bounded, a warning is created. *) if not (is_bounded) then ( - let msgs = - [(Pretty.dprintf - "The program might not terminate! (Loop analysis)", - Some (M.Location.CilLocation (Cilfacade.get_stmtLoc loop_statement)) - );] in - M.msg_group Warning ~category:NonTerminating "Possibly non terminating loops" msgs); + M.warn ~loc:(M.Location.CilLocation (Cilfacade.get_stmtLoc loop_statement)) ~category:NonTerminating "The program might not terminate! (Loop analysis)" + ); () with Not_found -> failwith "Encountered a call to __goblint_bounded with an unknown loop counter variable.") diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 963e6e4996..307f6d0260 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1688,12 +1688,7 @@ struct List.iter handle_path (S.paths_as_set conv_ctx); if !AnalysisState.should_warn && List.mem "termination" @@ get_string_list "ana.activated" then ( AnalysisState.svcomp_may_not_terminate := true; - let msgs = - [(Pretty.dprintf - "The program might not terminate! (Longjmp)", - None - );] in - M.msg_group Warning ~category:NonTerminating "Possibly non terminating loops" msgs + M.warn ~category:NonTerminating "The program might not terminate! (Longjmp)" ); S.D.bot () | _ -> S.special conv_ctx lv f args @@ -1777,11 +1772,8 @@ struct if LS.mem call path_visited_calls then ( AnalysisState.svcomp_may_not_terminate := true; (*set the indicator for a non-terminating program for the sv comp*) (*Cycle found*) - let msgs = - [ - (Pretty.dprintf "The program might not terminate! (Fundec %a is contained in a call graph cycle)" CilType.Fundec.pretty fundec, Some (M.Location.CilLocation fundec.svar.vdecl)); - ] in - M.msg_group Warning ~category:NonTerminating "Recursion cycle" msgs) (* output a warning for non-termination*) + let loc = M.Location.CilLocation fundec.svar.vdecl in + M.warn ~loc ~category:NonTerminating "The program might not terminate! (Fundec %a is contained in a call graph cycle)" CilType.Fundec.pretty fundec) (* output a warning for non-termination*) else if not (LH.mem global_visited_calls call) then begin LH.replace global_visited_calls call (); let new_path_visited_calls = LS.add call path_visited_calls in diff --git a/src/framework/control.ml b/src/framework/control.ml index 65eda50f03..8768fa00c9 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -146,12 +146,7 @@ struct let fundec_live = live fi fname in if ( not (BatISet.is_empty fundec_live)) then ( AnalysisState.svcomp_may_not_terminate := true; - let msgs = - [(Pretty.dprintf - "The program might not terminate! (Upjumping Goto)", - Some (M.Location.CilLocation l) - );] in - M.msg_group Warning ~category:NonTerminating "Possibly non terminating loops" msgs); + M.warn ~loc:(M.Location.CilLocation l) ~category:NonTerminating "The program might not terminate! (Upjumping Goto)"); ) (!live_lines)) (!Cilfacade.upjumping_gotos); From cf22550ceb4e9e0a70ec193f0d64294b0ce8d340 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 1 Nov 2023 18:36:03 +0100 Subject: [PATCH 0915/1312] Delete old termination analysis --- src/analyses/termination.ml | 239 ------------------------------------ 1 file changed, 239 deletions(-) delete mode 100644 src/analyses/termination.ml diff --git a/src/analyses/termination.ml b/src/analyses/termination.ml deleted file mode 100644 index 6da9225d3f..0000000000 --- a/src/analyses/termination.ml +++ /dev/null @@ -1,239 +0,0 @@ -(** Termination analysis of loops using counter variables ([term]). *) - -open Batteries -open GoblintCil -open Analyses - -module M = Messages -let (||?) a b = match a,b with Some x,_ | _, Some x -> Some x | _ -> None - -module TermDomain = struct - include SetDomain.ToppedSet (Basetype.Variables) (struct let topname = "All Variables" end) -end - -(* some kind of location string suitable for variable names? *) -let show_location_id l = - string_of_int l.line ^ "_" ^ string_of_int l.column - -class loopCounterVisitor (fd : fundec) = object(self) - inherit nopCilVisitor - method! vstmt s = - let action s = match s.skind with - | Loop (b, loc, eloc, _, _) -> - (* insert loop counter variable *) - let name = "term"^show_location_id loc in - let typ = intType in (* TODO the type should be the same as the one of the original loop counter *) - let v = Cilfacade.create_var (makeLocalVar fd name ~init:(SingleInit zero) typ) in - (* make an init stmt since the init above is apparently ignored *) - let init_stmt = mkStmtOneInstr @@ Set (var v, zero, loc, eloc) in - (* increment it every iteration *) - let inc_stmt = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in - b.bstmts <- inc_stmt :: b.bstmts; - let nb = mkBlock [init_stmt; mkStmt s.skind] in - s.skind <- Block nb; - s - | _ -> s - in ChangeDoChildrenPost (s, action) -end - -let loopBreaks : (int, location) Hashtbl.t = Hashtbl.create 13 (* break stmt sid -> corresponding loop *) -class loopBreaksVisitor (fd : fundec) = object(self) - inherit nopCilVisitor - method! vstmt s = - (match s.skind with - | Loop (b, loc, eloc, Some continue, Some break) -> Hashtbl.add loopBreaks break.sid loc (* TODO: use eloc? *) - | Loop _ -> failwith "Termination.preprocess: every loop should have a break and continue stmt after prepareCFG" - | _ -> ()); - DoChildren -end - -(* if the given block contains a goto while_break.* we have the termination condition for a loop *) -let exits = function - | { bstmts = [{ skind = Goto (stmt, loc); _ }]; _ } -> Hashtbl.find_option loopBreaks !stmt.sid - | _ -> None (* TODO handle return (need to find out what loop we are in) *) - -let lvals_of_expr = - let rec f a = function - | Const _ | SizeOf _ | SizeOfStr _ | AlignOf _ | AddrOfLabel _ -> a - | Lval l | AddrOf l | StartOf l -> l :: a - | SizeOfE e | AlignOfE e | UnOp (_,e,_) | CastE (_,e) | Imag e | Real e -> f a e - | BinOp (_,e1,e2,_) -> f a e1 @ f a e2 - | Question (c,t,e,_) -> f a c @ f a t @ f a e - in f [] - -let loopVars : (location, lval) Hashtbl.t = Hashtbl.create 13 (* loop location -> lval used for exit *) -class loopVarsVisitor (fd : fundec) = object - inherit nopCilVisitor - method! vstmt s = - let add_exit_cond e loc = - match lvals_of_expr e with - | [lval] when Cilfacade.typeOf e |> isArithmeticType -> Hashtbl.add loopVars loc lval - | _ -> () - in - (match s.skind with - | If (e, tb, fb, loc, eloc) -> Option.map_default (add_exit_cond e) () (exits tb ||? exits fb) - | _ -> ()); - DoChildren -end - -let stripCastsDeep e = - let v = object - inherit nopCilVisitor - method! vexpr e = ChangeTo (stripCasts e) - end - in visitCilExpr v e - -(* keep the enclosing loop for statements *) -let cur_loop = ref None (* current loop *) -let cur_loop' = ref None (* for nested loops *) -let makeVar fd loc name = - let id = name ^ "__" ^ show_location_id loc in - try List.find (fun v -> v.vname = id) fd.slocals - with Not_found -> - let typ = intType in (* TODO the type should be the same as the one of the original loop counter *) - Cilfacade.create_var (makeLocalVar fd id ~init:(SingleInit zero) typ) -let f_assume = Lval (var (emptyFunction "__goblint_assume").svar) -let f_check = Lval (var (emptyFunction "__goblint_check").svar) -class loopInstrVisitor (fd : fundec) = object(self) - inherit nopCilVisitor - method! vstmt s = - (* TODO: use Loop eloc? *) - (match s.skind with - | Loop (_, loc, eloc, _, _) -> - cur_loop' := !cur_loop; - cur_loop := Some loc - | _ -> ()); - let action s = - (* first, restore old cur_loop *) - (match s.skind with - | Loop (_, loc, eloc, _, _) -> - cur_loop := !cur_loop'; - | _ -> ()); - let in_loop () = Option.is_some !cur_loop && Hashtbl.mem loopVars (Option.get !cur_loop) in - match s.skind with - | Loop (b, loc, eloc, Some continue, Some break) when Hashtbl.mem loopVars loc -> - (* find loop var for current loop *) - let x = Hashtbl.find loopVars loc in - (* insert loop counter and diff to loop var *) - let t = var @@ makeVar fd loc "t" in - let d1 = var @@ makeVar fd loc "d1" in - let d2 = var @@ makeVar fd loc "d2" in - (* make init stmts *) - let t_init = mkStmtOneInstr @@ Set (t, zero, loc, eloc) in - let d1_init = mkStmtOneInstr @@ Set (d1, Lval x, loc, eloc) in - let d2_init = mkStmtOneInstr @@ Set (d2, Lval x, loc, eloc) in - (* increment/decrement in every iteration *) - let t_inc = mkStmtOneInstr @@ Set (t, increm (Lval t) 1, loc, eloc) in - let d1_inc = mkStmtOneInstr @@ Set (d1, increm (Lval d1) (-1), loc, eloc) in - let d2_inc = mkStmtOneInstr @@ Set (d2, increm (Lval d2) 1 , loc, eloc) in - let typ = intType in - let e1 = BinOp (Eq, Lval t, BinOp (MinusA, Lval x, Lval d1, typ), typ) in - let e2 = BinOp (Eq, Lval t, BinOp (MinusA, Lval d2, Lval x, typ), typ) in - let inv1 = mkStmtOneInstr @@ Call (None, f_assume, [e1], loc, eloc) in - let inv2 = mkStmtOneInstr @@ Call (None, f_assume, [e2], loc, eloc) in - (match b.bstmts with - | cont :: cond :: ss -> - (* changing succs/preds directly doesn't work -> need to replace whole stmts *) - b.bstmts <- cont :: cond :: inv1 :: inv2 :: d1_inc :: d2_inc :: t_inc :: ss; - let nb = mkBlock [t_init; d1_init; d2_init; mkStmt s.skind] in - s.skind <- Block nb; - | _ -> ()); - s - | Loop (b, loc, eloc, Some continue, Some break) -> - print_endline @@ "WARN: Could not determine loop variable for loop at " ^ CilType.Location.show loc; - s - | _ when Hashtbl.mem loopBreaks s.sid -> (* after a loop, we check that t is bounded/positive (no overflow happened) *) - let loc = Hashtbl.find loopBreaks s.sid in - let t = var @@ makeVar fd loc "t" in - let e3 = BinOp (Ge, Lval t, zero, intType) in - let inv3 = mkStmtOneInstr @@ Call (None, f_check, [e3], loc, locUnknown) in - let nb = mkBlock [mkStmt s.skind; inv3] in - s.skind <- Block nb; - s - | Instr [Set (lval, e, loc, eloc)] when in_loop () -> - (* find loop var for current loop *) - let cur_loop = Option.get !cur_loop in - let x = Hashtbl.find loopVars cur_loop in - if x <> lval then - s - else (* we only care about the loop var *) - let d1 = makeVar fd cur_loop "d1" in - let d2 = makeVar fd cur_loop "d2" in - (match stripCastsDeep e with - | BinOp (op, Lval x', e2, typ) when (op = PlusA || op = MinusA) && x' = x && isArithmeticType typ -> (* TODO x = 1 + x, MinusA! *) - (* increase diff by same expr *) - let d1_inc = mkStmtOneInstr @@ Set (var d1, BinOp (PlusA, Lval (var d1), e2, typ), loc, eloc) in - let d2_inc = mkStmtOneInstr @@ Set (var d2, BinOp (PlusA, Lval (var d2), e2, typ), loc, eloc) in - let nb = mkBlock [d1_inc; d2_inc; mkStmt s.skind] in - s.skind <- Block nb; - s - | _ -> - (* otherwise diff is e - counter *) - let t = makeVar fd cur_loop "t" in - let te = Cilfacade.typeOf e in - let dt1 = mkStmtOneInstr @@ Set (var d1, BinOp (MinusA, Lval x, Lval (var t), te), loc, eloc) in - let dt2 = mkStmtOneInstr @@ Set (var d2, BinOp (MinusA, Lval x, Lval (var t), te), loc, eloc) in - let nb = mkBlock [mkStmt s.skind; dt1; dt2] in - s.skind <- Block nb; - s - ) - | _ -> s - in - ChangeDoChildrenPost (s, action) -end - - -module Spec = -struct - include Analyses.IdentitySpec - - let name () = "term" - module D = TermDomain - module C = TermDomain - - (* queries *) - (*let query ctx (q:Queries.t) : Queries.Result.t =*) - (*match q with*) - (*| Queries.MustTerm loc -> `Bool (D.mem v ctx.local)*) - (*| _ -> Queries.Result.top ()*) - - (* transfer functions *) - - let branch ctx (exp:exp) (tv:bool) : D.t = - ctx.local - (* if the then-block contains a goto while_break.* we have the termination condition for a loop *) - (* match !MyCFG.current_node with *) - (* | Some (MyCFG.Statement({ skind = If (e, tb, fb, loc) })) -> *) - (* let str_exit b = match exits b with Some loc -> string_of_int loc.line | None -> "None" in *) - (* M.debug @@ *) - (* "\nCil-exp: " ^ sprint d_exp e *) - (* (*^ "; Goblint-exp: " ^ sprint d_exp exp*) *) - (* ^ "; Goblint: " ^ sprint Queries.Result.pretty (ctx.ask (Queries.EvalInt exp)) *) - (* ^ "\nCurrent block: " ^ (if tv then "Then" else "Else") *) - (* ^ "\nThen block (exits " ^ str_exit tb ^ "): " ^ sprint d_block tb *) - (* ^ "\nElse block (exits " ^ str_exit fb ^ "): " ^ sprint d_block fb *) - (* ; *) - (* ctx.local *) - (* | _ -> ctx.local *) - - let startstate v = D.bot () - let threadenter ctx lval f args = [D.bot ()] - let exitstate v = D.bot () -end - -class recomputeVisitor (fd : fundec) = object(self) - inherit nopCilVisitor - method! vfunc fd = - computeCFGInfo fd true; - SkipChildren -end - -let _ = - (* Cilfacade.register_preprocess Spec.name (new loopCounterVisitor); *) - Cilfacade.register_preprocess (Spec.name ()) (new loopBreaksVisitor); - Cilfacade.register_preprocess (Spec.name ()) (new loopVarsVisitor); - Cilfacade.register_preprocess (Spec.name ()) (new loopInstrVisitor); - Cilfacade.register_preprocess (Spec.name ()) (new recomputeVisitor); - Hashtbl.clear loopBreaks; (* because the sids are now different *) - Cilfacade.register_preprocess (Spec.name ()) (new loopBreaksVisitor); - MCP.register_analysis (module Spec : MCPSpec) From 67cc037057a69e446bc35e906e256a5c63621138 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 1 Nov 2023 18:36:59 +0100 Subject: [PATCH 0916/1312] Rm old Termination also from `goblint_lib.ml` --- src/goblint_lib.ml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/goblint_lib.ml b/src/goblint_lib.ml index a71a0c9684..e1087f8fc9 100644 --- a/src/goblint_lib.ml +++ b/src/goblint_lib.ml @@ -149,7 +149,6 @@ module UnitAnalysis = UnitAnalysis module Assert = Assert module FileUse = FileUse module Uninit = Uninit -module Termination = Termination module Expsplit = Expsplit module StackTrace = StackTrace module Spec = Spec From dff7be0446336b3e9ac8810fce326004f767a293 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 1 Nov 2023 18:54:43 +0100 Subject: [PATCH 0917/1312] Make `do_preprocess_cil` more efficient --- src/common/util/cilfacade.ml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/common/util/cilfacade.ml b/src/common/util/cilfacade.ml index 2075cda890..47cf6d6210 100644 --- a/src/common/util/cilfacade.ml +++ b/src/common/util/cilfacade.ml @@ -93,12 +93,13 @@ let register_preprocess_cil name visitor_fun = visitors_cil := !visitors_cil @ [name, visitor_fun] let do_preprocess_cil ast = - let f fd (name, visitor_fun) = - (* this has to be done here, since the settings aren't available when register_preprocess is called *) - if List.mem name (get_string_list "ana.activated") then - ignore @@ visitCilFunction (visitor_fun fd) fd - in - iterGlobals ast (function GFun (fd,_) -> List.iter (f fd) !visitors_cil | _ -> ()) + (* this has to be done here, since the settings aren't available when register_preprocess is called *) + let active_visitors = List.filter_map (fun (name, visitor_fun) -> if List.mem name (get_string_list "ana.activated") then Some visitor_fun else None) !visitors_cil in + let f fd visitor_fun = ignore @@ visitCilFunction (visitor_fun fd) fd in + if active_visitors <> [] then + iterGlobals ast (function GFun (fd,_) -> List.iter (f fd) active_visitors | _ -> ()) + else + () (** @raise GoblintCil.FrontC.ParseError @raise GoblintCil.Errormsg.Error *) From 2d8fe096da4f54d585ce65748a28cf06de18d124 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 1 Nov 2023 18:59:03 +0100 Subject: [PATCH 0918/1312] Remove outdated TODO --- src/witness/witness.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/witness/witness.ml b/src/witness/witness.ml index 08a796c307..1612667a30 100644 --- a/src/witness/witness.ml +++ b/src/witness/witness.ml @@ -475,7 +475,7 @@ struct in (module TaskResult:WitnessTaskResult) ) - | Termination -> (* TODO: implement this properly*) + | Termination -> let module TrivialArg = struct include Arg From f147d9bdd67fda7d77907631b643678eebe4284a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 2 Nov 2023 10:05:22 +0200 Subject: [PATCH 0919/1312] Remove unused [@@deriving ord] on SV-COMP spec --- src/witness/svcomp.ml | 1 - src/witness/svcompSpec.ml | 1 - 2 files changed, 2 deletions(-) diff --git a/src/witness/svcomp.ml b/src/witness/svcomp.ml index 218f0716ae..eae97b1199 100644 --- a/src/witness/svcomp.ml +++ b/src/witness/svcomp.ml @@ -44,7 +44,6 @@ struct | True | False of Specification.t option | Unknown - [@@deriving ord] let to_string = function | True -> "true" diff --git a/src/witness/svcompSpec.ml b/src/witness/svcompSpec.ml index 9bd5a35e3e..66b3b83ac8 100644 --- a/src/witness/svcompSpec.ml +++ b/src/witness/svcompSpec.ml @@ -10,7 +10,6 @@ type t = | ValidDeref | ValidMemtrack | ValidMemcleanup -[@@deriving ord] type multi = t list From c42ec6b1e19eca1f1b899387a023d6505e285b7d Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 26 Sep 2023 18:09:51 +0300 Subject: [PATCH 0920/1312] Refactor Access.may_race with match --- src/domains/access.ml | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/domains/access.ml b/src/domains/access.ml index 8907ccbc32..3ba7aaee74 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -438,16 +438,13 @@ struct end -(* Check if two accesses may race and if yes with which confidence *) +(** Check if two accesses may race. *) let may_race A.{kind; acc; _} A.{kind=kind2; acc=acc2; _} = - if kind = Read && kind2 = Read then - false (* two read/read accesses do not race *) - else if not (get_bool "ana.race.free") && (kind = Free || kind2 = Free) then - false - else if not (MCPAccess.A.may_race acc acc2) then - false (* analysis-specific information excludes race *) - else - true + match kind, kind2 with + | Read, Read -> false (* two read/read accesses do not race *) + | Free, _ + | _, Free when not (get_bool "ana.race.free") -> false + | _, _ -> MCPAccess.A.may_race acc acc2 (* analysis-specific information excludes race *) (** Access sets for race detection and warnings. *) module WarnAccs = From e690d9ad81291ee34360e72fd2aceb33742c5d2d Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 2 Nov 2023 16:11:18 +0100 Subject: [PATCH 0921/1312] Prioritze termination warnings in `update_suite.rb` --- scripts/update_suite.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/update_suite.rb b/scripts/update_suite.rb index 28d6ddb52d..a7d60b5c21 100755 --- a/scripts/update_suite.rb +++ b/scripts/update_suite.rb @@ -160,13 +160,13 @@ def collect_warnings when /invariant confirmed/ then "success" when /invariant unconfirmed/ then "unknown" when /invariant refuted/ then "fail" + when /(Upjumping Goto)/ then "goto" + when /(Fundec \w+ is contained in a call graph cycle)/ then "fundec" + when /(Loop analysis)/ then "loop" when /^\[Warning\]/ then "warn" when /^\[Error\]/ then "warn" when /^\[Info\]/ then "warn" when /^\[Success\]/ then "success" - when /(Upjumping Goto)/ then "goto" - when /(Fundec \w+ is contained in a call graph cycle)/ then "fundec" - when /(Loop analysis)/ then "loop" when /\[Debug\]/ then next # debug "warnings" shouldn't count as other warnings (against NOWARN) when /^ on line \d+ $/ then next # dead line warnings shouldn't count (used for unreachability with NOWARN) when /^ on lines \d+..\d+ $/ then next # dead line warnings shouldn't count (used for unreachability with NOWARN) From 04516adf08e43517eb13bcfcf3fb06dda310c164 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 3 Nov 2023 12:58:46 +0100 Subject: [PATCH 0922/1312] Make memLeak path- & ctx-sensitive --- src/analyses/memLeak.ml | 7 ++++--- src/common/util/options.schema.json | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index dbaa2d69fc..62b6bbe3a7 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -14,9 +14,10 @@ struct let name () = "memLeak" module D = ToppedVarInfoSet - module C = Lattice.Unit + module C = D + module P = IdentityP (D) - let context _ _ = () + let context _ d = d (* HELPER FUNCTIONS *) let warn_for_multi_threaded ctx = @@ -95,4 +96,4 @@ struct end let _ = - MCP.register_analysis (module Spec : MCPSpec) \ No newline at end of file + MCP.register_analysis (module Spec : MCPSpec) diff --git a/src/common/util/options.schema.json b/src/common/util/options.schema.json index 40669ff729..4e282b19a4 100644 --- a/src/common/util/options.schema.json +++ b/src/common/util/options.schema.json @@ -352,7 +352,7 @@ "description": "List of path-sensitive analyses", "type": "array", "items": { "type": "string" }, - "default": [ "mutex", "malloc_null", "uninit", "expsplit","activeSetjmp" ] + "default": [ "mutex", "malloc_null", "uninit", "expsplit","activeSetjmp","memLeak" ] }, "ctx_insens": { "title": "ana.ctx_insens", From 27cf7f58c44e50d45bf5e98ef8ad097d0ed126b9 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 3 Nov 2023 14:05:30 +0100 Subject: [PATCH 0923/1312] Set unique address count to 5 --- src/autoTune.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/autoTune.ml b/src/autoTune.ml index 06347f3190..7ddc1aee43 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -227,8 +227,8 @@ let focusOnMemSafetySpecification (spec: Svcomp.Specification.t) = | ValidMemcleanup -> (* Enable the memLeak analysis *) let memLeakAna = ["memLeak"] in if (get_int "ana.malloc.unique_address_count") < 1 then ( - print_endline "Setting \"ana.malloc.unique_address_count\" to 1"; - set_int "ana.malloc.unique_address_count" 1; + print_endline "Setting \"ana.malloc.unique_address_count\" to 5"; + set_int "ana.malloc.unique_address_count" 5; ); print_endline @@ "Specification: ValidMemtrack and ValidMemcleanup -> enabling memLeak analysis \"" ^ (String.concat ", " memLeakAna) ^ "\""; enableAnalyses memLeakAna From 2174f108cf0c179d027d94684bee149547c3bf77 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 6 Nov 2023 14:06:35 +0200 Subject: [PATCH 0924/1312] Make memLeak path- & ctx-sensitive Co-authored-by: Michael Schwarz --- src/analyses/memLeak.ml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index dbaa2d69fc..9c09c05cf6 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -14,9 +14,10 @@ struct let name () = "memLeak" module D = ToppedVarInfoSet - module C = Lattice.Unit + module C = D + module P = IdentityP (D) - let context _ _ = () + let context _ d = d (* HELPER FUNCTIONS *) let warn_for_multi_threaded ctx = From 4279417b24a5c2e3c0bb46dd6cb0c2c6c29a0d03 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 6 Nov 2023 14:07:57 +0200 Subject: [PATCH 0925/1312] Add test from #1235 --- .../56-witness/52-witness-lifter-ps2.c | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 tests/regression/56-witness/52-witness-lifter-ps2.c diff --git a/tests/regression/56-witness/52-witness-lifter-ps2.c b/tests/regression/56-witness/52-witness-lifter-ps2.c new file mode 100644 index 0000000000..bcb7c1410c --- /dev/null +++ b/tests/regression/56-witness/52-witness-lifter-ps2.c @@ -0,0 +1,35 @@ +// PARAM: --enable ana.sv-comp.enabled --enable ana.sv-comp.functions --enable witness.graphml.enabled --set ana.specification 'CHECK( init(main()), LTL(G valid-memtrack) )' --set ana.activated[+] memLeak --set ana.path_sens[+] memLeak --set ana.malloc.unique_address_count 1 +struct _twoIntsStruct { + int intOne ; + int intTwo ; +}; + +typedef struct _twoIntsStruct twoIntsStruct; + +void printStructLine(twoIntsStruct const *structTwoIntsStruct) +{ + return; +} + + +int main(int argc, char **argv) +{ + twoIntsStruct *data; + int tmp_1; + + + if (tmp_1 != 0) { + twoIntsStruct *dataBuffer = malloc(800UL); + data = dataBuffer; + } + else { + + twoIntsStruct *dataBuffer_0 = malloc(800UL); + data = dataBuffer_0; + } + + printStructLine((twoIntsStruct const *)data); + free((void *)data); + + return; +} From 5c8a37773067c55709c6f0cc7a3e20da6bdb08c7 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 6 Nov 2023 14:08:27 +0200 Subject: [PATCH 0926/1312] Use inline_edge in more witness interfaces --- src/witness/myARG.ml | 2 +- src/witness/svcomp.ml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/witness/myARG.ml b/src/witness/myARG.ml index 068aed7a22..0dd42bc910 100644 --- a/src/witness/myARG.ml +++ b/src/witness/myARG.ml @@ -141,7 +141,7 @@ struct let equal_node_context _ _ = failwith "StackNode: equal_node_context" end -module Stack (Cfg:CfgForward) (Arg: S): +module Stack (Cfg:CfgForward) (Arg: S with module Edge = InlineEdge): S with module Node = StackNode (Arg.Node) and module Edge = Arg.Edge = struct module Node = StackNode (Arg.Node) diff --git a/src/witness/svcomp.ml b/src/witness/svcomp.ml index eae97b1199..9aab121196 100644 --- a/src/witness/svcomp.ml +++ b/src/witness/svcomp.ml @@ -76,7 +76,7 @@ sig val is_sink: Arg.Node.t -> bool end -module StackTaskResult (Cfg:MyCFG.CfgForward) (TaskResult: TaskResult) = +module StackTaskResult (Cfg:MyCFG.CfgForward) (TaskResult: TaskResult with module Arg.Edge = MyARG.InlineEdge) = struct module Arg = MyARG.Stack (Cfg) (TaskResult.Arg) From 23771e41e1afac7268761b8ceb1de0d033b01370 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 6 Nov 2023 14:09:36 +0200 Subject: [PATCH 0927/1312] Remove CFG-based function return node from MyARG.Stack (closes #1235) --- src/witness/myARG.ml | 44 ++++++++++++++++++-------------------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/src/witness/myARG.ml b/src/witness/myARG.ml index 0dd42bc910..a11ba44178 100644 --- a/src/witness/myARG.ml +++ b/src/witness/myARG.ml @@ -161,38 +161,30 @@ struct (* | [] -> failwith "StackArg.next: return stack empty" *) | [] -> [] (* main return *) | call_n :: call_stack -> - let call_cfgnode = Arg.Node.cfgnode call_n in let call_next = - Cfg.next call_cfgnode + Arg.next call_n (* filter because infinite loops starting with function call will have another Neg(1) edge from the head *) - |> List.filter (fun (locedges, to_node) -> - List.exists (function - | (_, Proc _) -> true - | (_, _) -> false - ) locedges + |> List.filter (fun (edge, to_n) -> + match edge with + | InlinedEdge _ -> true + | _ -> false ) in begin match call_next with - | [] -> failwith "StackArg.next: call next empty" - | [(_, return_node)] -> - begin match Arg.Node.move_opt call_n return_node with - (* TODO: Is it possible to have a calling node without a returning node? *) - (* | None -> [] *) - | None -> failwith "StackArg.next: no return node" - | Some return_n -> - (* TODO: Instead of next & filter, construct unique return_n directly. Currently edge missing. *) - Arg.next n - |> List.filter (fun (edge, to_n) -> - (* let to_cfgnode = Arg.Node.cfgnode to_n in - MyCFG.Node.equal to_cfgnode return_node *) - Arg.Node.equal_node_context to_n return_n - ) - |> List.map (fun (edge, to_n) -> - let to_n' = to_n :: call_stack in - (edge, to_n') - ) - end + | [] -> failwith "StackArg.next: call next empty" (* TODO: Is it possible to have a calling node without a returning node? *) + | [(_, return_n)] -> + (* TODO: Instead of next & filter, construct unique return_n directly. Currently edge missing. *) + Arg.next n + |> List.filter (fun (edge, to_n) -> + (* let to_cfgnode = Arg.Node.cfgnode to_n in + MyCFG.Node.equal to_cfgnode return_node *) + Arg.Node.equal_node_context to_n return_n + ) + |> List.map (fun (edge, to_n) -> + let to_n' = to_n :: call_stack in + (edge, to_n') + ) | _ :: _ :: _ -> failwith "StackArg.next: call next ambiguous" end end From 3299b75380b60073f075efcc3d46e97513f92ab3 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 6 Nov 2023 14:15:26 +0200 Subject: [PATCH 0928/1312] Remove now-unused Cfg argument from stack ARG functor --- src/witness/myARG.ml | 4 ++-- src/witness/svcomp.ml | 4 ++-- src/witness/witness.ml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/witness/myARG.ml b/src/witness/myARG.ml index a11ba44178..ae8b5f6772 100644 --- a/src/witness/myARG.ml +++ b/src/witness/myARG.ml @@ -141,7 +141,7 @@ struct let equal_node_context _ _ = failwith "StackNode: equal_node_context" end -module Stack (Cfg:CfgForward) (Arg: S with module Edge = InlineEdge): +module Stack (Arg: S with module Edge = InlineEdge): S with module Node = StackNode (Arg.Node) and module Edge = Arg.Edge = struct module Node = StackNode (Arg.Node) @@ -156,7 +156,7 @@ struct | n :: stack -> let cfgnode = Arg.Node.cfgnode n in match cfgnode with - | Function _ -> (* TODO: can this be done without Cfg? *) + | Function _ -> (* TODO: can this be done without cfgnode? *) begin match stack with (* | [] -> failwith "StackArg.next: return stack empty" *) | [] -> [] (* main return *) diff --git a/src/witness/svcomp.ml b/src/witness/svcomp.ml index 9aab121196..89487ea8d4 100644 --- a/src/witness/svcomp.ml +++ b/src/witness/svcomp.ml @@ -76,9 +76,9 @@ sig val is_sink: Arg.Node.t -> bool end -module StackTaskResult (Cfg:MyCFG.CfgForward) (TaskResult: TaskResult with module Arg.Edge = MyARG.InlineEdge) = +module StackTaskResult (TaskResult: TaskResult with module Arg.Edge = MyARG.InlineEdge) = struct - module Arg = MyARG.Stack (Cfg) (TaskResult.Arg) + module Arg = MyARG.Stack (TaskResult.Arg) let result = TaskResult.result diff --git a/src/witness/witness.ml b/src/witness/witness.ml index d94960d542..235461c348 100644 --- a/src/witness/witness.ml +++ b/src/witness/witness.ml @@ -14,7 +14,7 @@ let write_file filename (module Task:Task) (module TaskResult:WitnessTaskResult) let module TaskResult = (val if get_bool "witness.graphml.stack" then - (module StackTaskResult (Task.Cfg) (TaskResult) : WitnessTaskResult) + (module StackTaskResult (TaskResult) : WitnessTaskResult) else (module TaskResult) ) From 38e82eb7620200427bbc13040c5758471e862fa3 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 6 Nov 2023 14:22:08 +0200 Subject: [PATCH 0929/1312] Add 56-witness/53-witness-lifter-ps2 version with functon introducing path-sensitivity --- .../56-witness/53-witness-lifter-ps3.c | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 tests/regression/56-witness/53-witness-lifter-ps3.c diff --git a/tests/regression/56-witness/53-witness-lifter-ps3.c b/tests/regression/56-witness/53-witness-lifter-ps3.c new file mode 100644 index 0000000000..06b73b3888 --- /dev/null +++ b/tests/regression/56-witness/53-witness-lifter-ps3.c @@ -0,0 +1,39 @@ +// PARAM: --enable ana.sv-comp.enabled --enable ana.sv-comp.functions --enable witness.graphml.enabled --set ana.specification 'CHECK( init(main()), LTL(G valid-memtrack) )' --set ana.activated[+] memLeak --set ana.path_sens[+] memLeak --set ana.malloc.unique_address_count 1 +struct _twoIntsStruct { + int intOne ; + int intTwo ; +}; + +typedef struct _twoIntsStruct twoIntsStruct; + +void printStructLine(twoIntsStruct const *structTwoIntsStruct) +{ + return; +} + +twoIntsStruct *foo() { + twoIntsStruct *data; + int tmp_1; + + if (tmp_1 != 0) { + twoIntsStruct *dataBuffer = malloc(800UL); + data = dataBuffer; + } + else { + + twoIntsStruct *dataBuffer_0 = malloc(800UL); + data = dataBuffer_0; + } + return data; +} + +int main(int argc, char **argv) +{ + twoIntsStruct *data; + data = foo(); + + printStructLine((twoIntsStruct const *)data); + free((void *)data); + + return; +} From cb32b12a377ba255b02735890e4eb50afab16cca Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 6 Nov 2023 14:44:30 +0200 Subject: [PATCH 0930/1312] Fix 56-witness/53-witness-lifter-ps3 --- src/witness/myARG.ml | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/src/witness/myARG.ml b/src/witness/myARG.ml index ae8b5f6772..373a66d3d6 100644 --- a/src/witness/myARG.ml +++ b/src/witness/myARG.ml @@ -165,28 +165,21 @@ struct Arg.next call_n (* filter because infinite loops starting with function call will have another Neg(1) edge from the head *) - |> List.filter (fun (edge, to_n) -> + |> List.filter_map (fun (edge, to_n) -> match edge with - | InlinedEdge _ -> true - | _ -> false + | InlinedEdge _ -> Some to_n + | _ -> None ) in - begin match call_next with - | [] -> failwith "StackArg.next: call next empty" (* TODO: Is it possible to have a calling node without a returning node? *) - | [(_, return_n)] -> - (* TODO: Instead of next & filter, construct unique return_n directly. Currently edge missing. *) - Arg.next n - |> List.filter (fun (edge, to_n) -> - (* let to_cfgnode = Arg.Node.cfgnode to_n in - MyCFG.Node.equal to_cfgnode return_node *) - Arg.Node.equal_node_context to_n return_n - ) - |> List.map (fun (edge, to_n) -> - let to_n' = to_n :: call_stack in - (edge, to_n') - ) - | _ :: _ :: _ -> failwith "StackArg.next: call next ambiguous" - end + Arg.next n + |> List.filter_map (fun (edge, to_n) -> + if BatList.mem_cmp Arg.Node.compare to_n call_next then ( + let to_n' = to_n :: call_stack in + Some (edge, to_n') + ) + else + None + ) end | _ -> let+ (edge, to_n) = Arg.next n in From 476795e22774535cfb38820011cd261b1df58218 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 7 Nov 2023 14:07:27 +0200 Subject: [PATCH 0931/1312] Add YAML invariant_set entry type (issue #1238) --- src/witness/yamlWitnessType.ml | 100 +++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/src/witness/yamlWitnessType.ml b/src/witness/yamlWitnessType.ml index 3390c1e3ab..c68b1a45c9 100644 --- a/src/witness/yamlWitnessType.ml +++ b/src/witness/yamlWitnessType.ml @@ -242,6 +242,100 @@ struct {location; loop_invariant; precondition} end +module InvariantSet = +struct + module LoopInvariant = + struct + type t = { + location: Location.t; + value: string; + format: string; + } + + let invariant_type = "loop_invariant" + + let to_yaml' {location; value; format} = + [ + ("location", Location.to_yaml location); + ("value", `String value); + ("format", `String format); + ] + + let of_yaml y = + let open GobYaml in + let+ location = y |> find "location" >>= Location.of_yaml + and+ value = y |> find "value" >>= to_string + and+ format = y |> find "format" >>= to_string in + {location; value; format} + end + + module LocationInvariant = + struct + include LoopInvariant + + let invariant_type = "location_invariant" + end + + (* TODO: could maybe use GADT, but adds ugly existential layer to entry type pattern matching *) + module InvariantType = + struct + type t = + | LocationInvariant of LocationInvariant.t + | LoopInvariant of LoopInvariant.t + + let invariant_type = function + | LocationInvariant _ -> LocationInvariant.invariant_type + | LoopInvariant _ -> LoopInvariant.invariant_type + + let to_yaml' = function + | LocationInvariant x -> LocationInvariant.to_yaml' x + | LoopInvariant x -> LoopInvariant.to_yaml' x + + let of_yaml y = + let open GobYaml in + let* invariant_type = y |> find "type" >>= to_string in + if invariant_type = LocationInvariant.invariant_type then + let+ x = y |> LocationInvariant.of_yaml in + LocationInvariant x + else if invariant_type = LoopInvariant.invariant_type then + let+ x = y |> LoopInvariant.of_yaml in + LoopInvariant x + else + Error (`Msg "type") + end + + module Invariant = + struct + type t = { + invariant_type: InvariantType.t; + } + + let to_yaml {invariant_type} = + `O ([ + ("type", `String (InvariantType.invariant_type invariant_type)); + ] @ InvariantType.to_yaml' invariant_type) + + let of_yaml y = + let open GobYaml in + let+ invariant_type = y |> InvariantType.of_yaml in + {invariant_type} + end + + type t = { + content: Invariant.t list; + } + + let entry_type = "invariant_set" + + let to_yaml' {content} = + [("content", `A (List.map Invariant.to_yaml content))] + + let of_yaml y = + let open GobYaml in + let+ content = y |> list >>= list_map Invariant.of_yaml in + {content} +end + module Target = struct type t = { @@ -326,6 +420,7 @@ struct | PreconditionLoopInvariant of PreconditionLoopInvariant.t | LoopInvariantCertificate of LoopInvariantCertificate.t | PreconditionLoopInvariantCertificate of PreconditionLoopInvariantCertificate.t + | InvariantSet of InvariantSet.t let entry_type = function | LocationInvariant _ -> LocationInvariant.entry_type @@ -334,6 +429,7 @@ struct | PreconditionLoopInvariant _ -> PreconditionLoopInvariant.entry_type | LoopInvariantCertificate _ -> LoopInvariantCertificate.entry_type | PreconditionLoopInvariantCertificate _ -> PreconditionLoopInvariantCertificate.entry_type + | InvariantSet _ -> InvariantSet.entry_type let to_yaml' = function | LocationInvariant x -> LocationInvariant.to_yaml' x @@ -342,6 +438,7 @@ struct | PreconditionLoopInvariant x -> PreconditionLoopInvariant.to_yaml' x | LoopInvariantCertificate x -> LoopInvariantCertificate.to_yaml' x | PreconditionLoopInvariantCertificate x -> PreconditionLoopInvariantCertificate.to_yaml' x + | InvariantSet x -> InvariantSet.to_yaml' x let of_yaml y = let open GobYaml in @@ -364,6 +461,9 @@ struct else if entry_type = PreconditionLoopInvariantCertificate.entry_type then let+ x = y |> PreconditionLoopInvariantCertificate.of_yaml in PreconditionLoopInvariantCertificate x + else if entry_type = InvariantSet.entry_type then + let+ x = y |> InvariantSet.of_yaml in + InvariantSet x else Error (`Msg "entry_type") end From f25051c5e6b55220e572d9a536f8b2e1ccba450a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 7 Nov 2023 14:31:04 +0200 Subject: [PATCH 0932/1312] Add options for YAML invariant_set --- src/common/util/options.schema.json | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/common/util/options.schema.json b/src/common/util/options.schema.json index 40669ff729..a51c5c59cf 100644 --- a/src/common/util/options.schema.json +++ b/src/common/util/options.schema.json @@ -2430,7 +2430,8 @@ "flow_insensitive_invariant", "precondition_loop_invariant", "loop_invariant_certificate", - "precondition_loop_invariant_certificate" + "precondition_loop_invariant_certificate", + "invariant_set" ] }, "default": [ @@ -2438,7 +2439,24 @@ "loop_invariant", "flow_insensitive_invariant", "loop_invariant_certificate", - "precondition_loop_invariant_certificate" + "precondition_loop_invariant_certificate", + "invariant_set" + ] + }, + "invariant-types": { + "title": "witness.yaml.invariant-types", + "description": "YAML witness invariant types to output/input.", + "type": "array", + "items": { + "type": "string", + "enum": [ + "location_invariant", + "loop_invariant" + ] + }, + "default": [ + "location_invariant", + "loop_invariant" ] }, "path": { From 4a329b0373462cc3bc266d21d4f693ccc424abfb Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 7 Nov 2023 14:31:33 +0200 Subject: [PATCH 0933/1312] Add YAML invariant_set generation --- src/witness/yamlWitness.ml | 93 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index 9e8ebeff51..c83d5b48be 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -91,6 +91,29 @@ struct metadata = metadata ~task (); } + let location_invariant' ~location ~(invariant): InvariantSet.Invariant.t = { + invariant_type = LocationInvariant { + location; + value = invariant; + format = "c_expression"; + }; + } + + let loop_invariant' ~location ~(invariant): InvariantSet.Invariant.t = { + invariant_type = LoopInvariant { + location; + value = invariant; + format = "c_expression"; + }; + } + + let invariant_set ~task ~(invariants): Entry.t = { + entry_type = InvariantSet { + content = invariants; + }; + metadata = metadata ~task (); + } + let target ~uuid ~type_ ~(file_name): Target.t = { uuid; type_; @@ -134,6 +157,9 @@ let yaml_entries_to_file yaml_entries file = let entry_type_enabled entry_type = List.mem entry_type (GobConfig.get_string_list "witness.yaml.entry-types") +let invariant_type_enabled invariant_type = + List.mem invariant_type (GobConfig.get_string_list "witness.yaml.invariant-types") + module Make (R: ResultQuery.SpecSysSol2) = struct open R @@ -385,6 +411,73 @@ struct entries in + (* Generate invariant set *) + let entries = + if entry_type_enabled YamlWitnessType.InvariantSet.entry_type then ( + let invariants = [] in + + (* Generate location invariants *) + let invariants = + if invariant_type_enabled YamlWitnessType.InvariantSet.LocationInvariant.invariant_type then ( + NH.fold (fun n local acc -> + let loc = Node.location n in + if is_invariant_node n then ( + let lvals = local_lvals n local in + match R.ask_local_node n ~local (Invariant {Invariant.default_context with lvals}) with + | `Lifted inv -> + let invs = WitnessUtil.InvariantExp.process_exp inv in + List.fold_left (fun acc inv -> + let location_function = (Node.find_fundec n).svar.vname in + let location = Entry.location ~location:loc ~location_function in + let invariant = CilType.Exp.show inv in + let invariant = Entry.location_invariant' ~location ~invariant in + invariant :: acc + ) acc invs + | `Bot | `Top -> (* TODO: 0 for bot (dead code)? *) + acc + ) + else + acc + ) (Lazy.force nh) invariants + ) + else + invariants + in + + (* Generate loop invariants *) + let invariants = + if entry_type_enabled YamlWitnessType.InvariantSet.LoopInvariant.invariant_type then ( + NH.fold (fun n local acc -> + let loc = Node.location n in + if WitnessInvariant.emit_loop_head && WitnessUtil.NH.mem WitnessInvariant.loop_heads n then ( + match R.ask_local_node n ~local (Invariant Invariant.default_context) with + | `Lifted inv -> + let invs = WitnessUtil.InvariantExp.process_exp inv in + List.fold_left (fun acc inv -> + let location_function = (Node.find_fundec n).svar.vname in + let location = Entry.location ~location:loc ~location_function in + let invariant = CilType.Exp.show inv in + let invariant = Entry.loop_invariant' ~location ~invariant in + invariant :: acc + ) acc invs + | `Bot | `Top -> (* TODO: 0 for bot (dead code)? *) + acc + ) + else + acc + ) (Lazy.force nh) invariants + ) + else + invariants + in + + let entry = Entry.invariant_set ~task ~invariants in + entry :: entries + ) + else + entries + in + let yaml_entries = List.rev_map YamlWitnessType.Entry.to_yaml entries in (* reverse to make entries in file in the same order as generation messages *) M.msg_group Info ~category:Witness "witness generation summary" [ From fcd18652628e74bcaec601614f0852d13be4ea13 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 7 Nov 2023 14:58:07 +0200 Subject: [PATCH 0934/1312] Add YAML invariant_set validation --- src/witness/yamlWitness.ml | 46 +++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index c83d5b48be..c66938961b 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -732,6 +732,48 @@ struct None in + let validate_invariant_set (invariant_set: YamlWitnessType.InvariantSet.t) = + + let validate_location_invariant (location_invariant: YamlWitnessType.InvariantSet.LocationInvariant.t) = + let loc = loc_of_location location_invariant.location in + let inv = location_invariant.value in + + match Locator.find_opt locator loc with + | Some lvars -> + ignore (validate_lvars_invariant ~entry_certificate:None ~loc ~lvars inv) + | None -> + incr cnt_error; + M.warn ~category:Witness ~loc:(CilLocation loc) "couldn't locate invariant: %s" inv; + in + + let validate_loop_invariant (loop_invariant: YamlWitnessType.InvariantSet.LoopInvariant.t) = + let loc = loc_of_location loop_invariant.location in + let inv = loop_invariant.value in + + match Locator.find_opt loop_locator loc with + | Some lvars -> + ignore (validate_lvars_invariant ~entry_certificate:None ~loc ~lvars inv) + | None -> + incr cnt_error; + M.warn ~category:Witness ~loc:(CilLocation loc) "couldn't locate invariant: %s" inv; + in + + let validate_invariant (invariant: YamlWitnessType.InvariantSet.Invariant.t) = + let target_type = YamlWitnessType.InvariantSet.InvariantType.invariant_type invariant.invariant_type in + match invariant_type_enabled target_type, invariant.invariant_type with + | true, LocationInvariant x -> + validate_location_invariant x + | true, LoopInvariant x -> + validate_loop_invariant x + | false, (LocationInvariant _ | LoopInvariant _) -> + incr cnt_disabled; + M.info_noloc ~category:Witness "disabled invariant of type %s" target_type; + in + + List.iter validate_invariant invariant_set.content; + None + in + match entry_type_enabled target_type, entry.entry_type with | true, LocationInvariant x -> validate_location_invariant x @@ -739,7 +781,9 @@ struct validate_loop_invariant x | true, PreconditionLoopInvariant x -> validate_precondition_loop_invariant x - | false, (LocationInvariant _ | LoopInvariant _ | PreconditionLoopInvariant _) -> + | true, InvariantSet x -> + validate_invariant_set x + | false, (LocationInvariant _ | LoopInvariant _ | PreconditionLoopInvariant _ | InvariantSet _) -> incr cnt_disabled; M.info_noloc ~category:Witness "disabled entry of type %s" target_type; None From e13bb5c3131f066fc1b793269a15c04c56cc9dbd Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 7 Nov 2023 15:02:39 +0200 Subject: [PATCH 0935/1312] Add YAML invariant_set unassume --- src/analyses/unassumeAnalysis.ml | 44 +++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/src/analyses/unassumeAnalysis.ml b/src/analyses/unassumeAnalysis.ml index 43707acd1e..5895f242c9 100644 --- a/src/analyses/unassumeAnalysis.ml +++ b/src/analyses/unassumeAnalysis.ml @@ -200,6 +200,46 @@ struct M.warn ~category:Witness ~loc:msgLoc "couldn't locate invariant: %s" inv in + let unassume_invariant_set (invariant_set: YamlWitnessType.InvariantSet.t) = + + let unassume_location_invariant (location_invariant: YamlWitnessType.InvariantSet.LocationInvariant.t) = + let loc = loc_of_location location_invariant.location in + let inv = location_invariant.value in + let msgLoc: M.Location.t = CilLocation loc in + + match Locator.find_opt locator loc with + | Some nodes -> + unassume_nodes_invariant ~loc ~nodes inv + | None -> + M.warn ~category:Witness ~loc:msgLoc "couldn't locate invariant: %s" inv + in + + let unassume_loop_invariant (loop_invariant: YamlWitnessType.InvariantSet.LoopInvariant.t) = + let loc = loc_of_location loop_invariant.location in + let inv = loop_invariant.value in + let msgLoc: M.Location.t = CilLocation loc in + + match Locator.find_opt loop_locator loc with + | Some nodes -> + unassume_nodes_invariant ~loc ~nodes inv + | None -> + M.warn ~category:Witness ~loc:msgLoc "couldn't locate invariant: %s" inv + in + + let validate_invariant (invariant: YamlWitnessType.InvariantSet.Invariant.t) = + let target_type = YamlWitnessType.InvariantSet.InvariantType.invariant_type invariant.invariant_type in + match YamlWitness.invariant_type_enabled target_type, invariant.invariant_type with + | true, LocationInvariant x -> + unassume_location_invariant x + | true, LoopInvariant x -> + unassume_loop_invariant x + | false, (LocationInvariant _ | LoopInvariant _) -> + M.info_noloc ~category:Witness "disabled invariant of type %s" target_type + in + + List.iter validate_invariant invariant_set.content + in + match YamlWitness.entry_type_enabled target_type, entry.entry_type with | true, LocationInvariant x -> unassume_location_invariant x @@ -207,7 +247,9 @@ struct unassume_loop_invariant x | true, PreconditionLoopInvariant x -> unassume_precondition_loop_invariant x - | false, (LocationInvariant _ | LoopInvariant _ | PreconditionLoopInvariant _) -> + | true, InvariantSet x -> + unassume_invariant_set x + | false, (LocationInvariant _ | LoopInvariant _ | PreconditionLoopInvariant _ | InvariantSet _) -> M.info_noloc ~category:Witness "disabled entry of type %s" target_type | _ -> M.info_noloc ~category:Witness "cannot unassume entry of type %s" target_type From 90e962f53ebf4eb92ef88c5fcf8c6401f118299c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 7 Nov 2023 15:15:10 +0200 Subject: [PATCH 0936/1312] Reverse YAML invariant_set content --- src/witness/yamlWitness.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index c66938961b..a580b319db 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -471,6 +471,7 @@ struct invariants in + let invariants = List.rev invariants in let entry = Entry.invariant_set ~task ~invariants in entry :: entries ) From ca84014548d7d585162b0afc9842dc78c2f72cef Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 7 Nov 2023 15:15:22 +0200 Subject: [PATCH 0937/1312] Fix YAML invariant_set parsing --- src/witness/yamlWitnessType.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/witness/yamlWitnessType.ml b/src/witness/yamlWitnessType.ml index c68b1a45c9..f9bcf3235f 100644 --- a/src/witness/yamlWitnessType.ml +++ b/src/witness/yamlWitnessType.ml @@ -332,7 +332,7 @@ struct let of_yaml y = let open GobYaml in - let+ content = y |> list >>= list_map Invariant.of_yaml in + let+ content = y |> find "content" >>= list >>= list_map Invariant.of_yaml in {content} end From 421abcd41fa5084c3f07854b88a26a1382d253e1 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 7 Nov 2023 21:44:51 +0100 Subject: [PATCH 0938/1312] Warn whenever there's allocated heap memory that's unreachable from globals at program exit --- src/analyses/memLeak.ml | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index dbaa2d69fc..f7f555a70a 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -19,6 +19,22 @@ struct let context _ _ = () (* HELPER FUNCTIONS *) + let get_global_vars () = + (* Filtering by GVar seems to account for declarations, as well as definitions of global vars *) + List.filter_map (function GVar (v, _, _) -> Some v | _ -> None) !Cilfacade.current_file.globals + + let get_reachable_mem_from_globals (global_vars:varinfo list) ctx = + global_vars + |> List.map (fun v -> Lval (Var v, NoOffset)) + |> List.filter_map (fun exp -> + match ctx.ask (Queries.MayPointTo exp) with + | a when not (Queries.AD.is_top a) && Queries.AD.cardinal a = 1 -> + begin match List.hd @@ Queries.AD.elements a with + | Queries.AD.Addr.Addr (v, _) when (ctx.ask (Queries.IsHeapVar v)) && not (ctx.ask (Queries.IsMultiple v)) -> Some v + | _ -> None + end + | _ -> None) + let warn_for_multi_threaded ctx = if not (ctx.ask (Queries.MustBeSingleThreaded { since_start = true })) then ( set_mem_safety_flag InvalidMemTrack; @@ -27,17 +43,25 @@ struct ) let check_for_mem_leak ?(assert_exp_imprecise = false) ?(exp = None) ctx = - let state = ctx.local in - if not @@ D.is_empty state then + let allocated_mem = ctx.local in + if not (D.is_empty allocated_mem) then + let reachable_mem = D.of_list (get_reachable_mem_from_globals (get_global_vars ()) ctx) in + (* Check and warn if there's unreachable allocated memory at program exit *) + let allocated_and_unreachable_mem = D.diff allocated_mem reachable_mem in + if not (D.is_empty allocated_and_unreachable_mem) then ( + set_mem_safety_flag InvalidMemTrack; + M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "There is unreachable allocated heap memory at program exit. A memory leak might occur for the alloc vars %a\n" (Pretty.d_list ", " CilType.Varinfo.pretty) (D.elements allocated_and_unreachable_mem) + ); + (* Check and warn if some of the allocated memory is not deallocated at program exit *) match assert_exp_imprecise, exp with | true, Some exp -> set_mem_safety_flag InvalidMemTrack; set_mem_safety_flag InvalidMemcleanup; - M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "assert expression %a is unknown. Memory leak might possibly occur for heap variables: %a" d_exp exp D.pretty state + M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Assert expression %a is unknown. Memory leak might possibly occur for heap variables: %a" d_exp exp D.pretty allocated_mem | _ -> set_mem_safety_flag InvalidMemTrack; set_mem_safety_flag InvalidMemcleanup; - M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory leak detected for heap variables: %a" D.pretty state + M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory leak detected for heap variables: %a" D.pretty allocated_mem (* TRANSFER FUNCTIONS *) let return ctx (exp:exp option) (f:fundec) : D.t = From 9f7224ed8d6ffeaeda226b62a71d40b1dbd247f5 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 7 Nov 2023 21:45:21 +0100 Subject: [PATCH 0939/1312] Add test cases for unreachable heap memory from globals --- .../regression/76-memleak/08-unreachable-mem.c | 12 ++++++++++++ .../76-memleak/09-unreachable-with-local-var.c | 17 +++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 tests/regression/76-memleak/08-unreachable-mem.c create mode 100644 tests/regression/76-memleak/09-unreachable-with-local-var.c diff --git a/tests/regression/76-memleak/08-unreachable-mem.c b/tests/regression/76-memleak/08-unreachable-mem.c new file mode 100644 index 0000000000..08e7b4e741 --- /dev/null +++ b/tests/regression/76-memleak/08-unreachable-mem.c @@ -0,0 +1,12 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak +#include + +int *g; + +int main(int argc, char const *argv[]) { + g = malloc(sizeof(int)); + // Reference to g's heap contents is lost here + g = NULL; + + return 0; //WARN +} diff --git a/tests/regression/76-memleak/09-unreachable-with-local-var.c b/tests/regression/76-memleak/09-unreachable-with-local-var.c new file mode 100644 index 0000000000..1614b19132 --- /dev/null +++ b/tests/regression/76-memleak/09-unreachable-with-local-var.c @@ -0,0 +1,17 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak +#include + +int *g; + +int main(int argc, char const *argv[]) { + g = malloc(sizeof(int)); + // Reference to g's heap contents is lost here + g = NULL; + // We get a false positive for p's memory being unreachable + // It's still leaked, but due to free() being commented out + // TODO: Should we only improve the error reporting for unreachable memory in this case? + int *p = malloc(sizeof(int)); + //free(p); + + return 0; //WARN +} From d5584ba06c517ec4fe2704398ae373c941032e4b Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 8 Nov 2023 11:11:58 +0100 Subject: [PATCH 0940/1312] Add `__VERIFIER_nondet_size_t` --- src/analyses/libraryFunctions.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index f84fe1d4e3..7c376fe426 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -972,6 +972,7 @@ let svcomp_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__VERIFIER_atomic_end", special [] @@ Unlock verifier_atomic); ("__VERIFIER_nondet_loff_t", unknown []); (* cannot give it in sv-comp.c without including stdlib or similar *) ("__VERIFIER_nondet_int", unknown []); (* declare invalidate actions to prevent invalidating globals when extern in regression tests *) + ("__VERIFIER_nondet_size_t ", unknown []); (* cannot give it in sv-comp.c without including stdlib or similar *) ] let ncurses_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ From 9a06289988dc949f3b49a672678d0f059b966490 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 8 Nov 2023 11:15:49 +0100 Subject: [PATCH 0941/1312] Add `__builtin_memcmp` --- src/analyses/libraryFunctions.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 7c376fe426..448b621fe4 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -32,6 +32,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__builtin_strncat", special [__ "dest" [r; w]; __ "src" [r]; __ "n" []] @@ fun dest src n -> Strcat { dest; src; n = Some n; }); ("__builtin___strncat_chk", special [__ "dest" [r; w]; __ "src" [r]; __ "n" []; drop "os" []] @@ fun dest src n -> Strcat { dest; src; n = Some n; }); ("memcmp", unknown [drop "s1" [r]; drop "s2" [r]; drop "n" []]); + ("__builtin_memcmp", unknown [drop "s1" [r]; drop "s2" [r]; drop "n" []]); ("memchr", unknown [drop "s" [r]; drop "c" []; drop "n" []]); ("asctime", unknown ~attrs:[ThreadUnsafe] [drop "time_ptr" [r_deep]]); ("fclose", unknown [drop "stream" [r_deep; w_deep; f_deep]]); From 67c8d9b395e8a263f1b8a0461602d66921ddb0c4 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 8 Nov 2023 11:17:10 +0100 Subject: [PATCH 0942/1312] Add `__builtin_strlen` --- src/analyses/libraryFunctions.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 448b621fe4..c3726f6d95 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -63,6 +63,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("localeconv", unknown ~attrs:[ThreadUnsafe] []); ("localtime", unknown ~attrs:[ThreadUnsafe] [drop "time" [r]]); ("strlen", special [__ "s" [r]] @@ fun s -> Strlen s); + ("__builtin_strlen", special [__ "s" [r]] @@ fun s -> Strlen s); ("strstr", special [__ "haystack" [r]; __ "needle" [r]] @@ fun haystack needle -> Strstr { haystack; needle; }); ("strcmp", special [__ "s1" [r]; __ "s2" [r]] @@ fun s1 s2 -> Strcmp { s1; s2; n = None; }); ("strtok", unknown ~attrs:[ThreadUnsafe] [drop "str" [r; w]; drop "delim" [r]]); From 24aca6767df3e2f0cf4296009d761ae56749231e Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 8 Nov 2023 11:42:31 +0100 Subject: [PATCH 0943/1312] Add `__fread_unlocked_*` --- src/analyses/libraryFunctions.ml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index c3726f6d95..75392c2243 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -52,6 +52,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("putc", unknown [drop "ch" []; drop "stream" [r_deep; w_deep]]); ("fputs", unknown [drop "str" [r]; drop "stream" [r_deep; w_deep]]); ("fread", unknown [drop "buffer" [w]; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); + ("fread_unlocked", unknown ~attrs:[ThreadUnsafe] [drop "buffer" [w]; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); ("fseek", unknown [drop "stream" [r_deep; w_deep]; drop "offset" []; drop "origin" []]); ("ftell", unknown [drop "stream" [r_deep]]); ("fwrite", unknown [drop "buffer" [r]; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); @@ -578,6 +579,9 @@ let glibc_desc_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__fgets_chk", unknown [drop "__s" [w]; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); ("__fread_alias", unknown [drop "__ptr" [w]; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); ("__fread_chk", unknown [drop "__ptr" [w]; drop "__ptrlen" []; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); + ("__fread_unlocked_alias", unknown ~attrs:[ThreadUnsafe] [drop "__ptr" [w]; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); + ("__fread_unlocked_chk", unknown ~attrs:[ThreadUnsafe] [drop "__ptr" [w]; drop "__ptrlen" []; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); + ("__fread_unlocked_chk_warn", unknown ~attrs:[ThreadUnsafe] [drop "__ptr" [w]; drop "__ptrlen" []; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); ("__read_chk", unknown [drop "__fd" []; drop "__buf" [w]; drop "__nbytes" []; drop "__buflen" []]); ("__read_alias", unknown [drop "__fd" []; drop "__buf" [w]; drop "__nbytes" []]); ("__readlink_chk", unknown [drop "path" [r]; drop "buf" [w]; drop "len" []; drop "buflen" []]); From 8972bd853f3bb5b11b1c659147a34b6697d7e2cf Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 8 Nov 2023 12:41:12 +0100 Subject: [PATCH 0944/1312] Fix typo Co-authored-by: Simmo Saan --- src/analyses/libraryFunctions.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 75392c2243..93c1cbaabe 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -978,7 +978,7 @@ let svcomp_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__VERIFIER_atomic_end", special [] @@ Unlock verifier_atomic); ("__VERIFIER_nondet_loff_t", unknown []); (* cannot give it in sv-comp.c without including stdlib or similar *) ("__VERIFIER_nondet_int", unknown []); (* declare invalidate actions to prevent invalidating globals when extern in regression tests *) - ("__VERIFIER_nondet_size_t ", unknown []); (* cannot give it in sv-comp.c without including stdlib or similar *) + ("__VERIFIER_nondet_size_t", unknown []); (* cannot give it in sv-comp.c without including stdlib or similar *) ] let ncurses_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ From b3ef0621077e46441516f91b73cd9f23ce56492e Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 8 Nov 2023 14:01:25 +0100 Subject: [PATCH 0945/1312] _Exit / _exit --- src/analyses/libraryFunctions.ml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 93c1cbaabe..c71c7a7f7e 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -149,6 +149,8 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("atomic_flag_test_and_set_explicit", unknown [drop "obj" [r; w]; drop "order" []]); ("atomic_load", unknown [drop "obj" [r]]); ("atomic_store", unknown [drop "obj" [w]; drop "desired" []]); + ("_Exit", special [drop "status" []] @@ Abort); + ("_exit", special [drop "status" []] @@ Abort); ] (** C POSIX library functions. From 2fc622067b2a7a2754f6d67a4990ed4b05008d32 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 8 Nov 2023 14:12:18 +0100 Subject: [PATCH 0946/1312] `__assert` --- src/analyses/libraryFunctions.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index c71c7a7f7e..eeddfbb218 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -510,6 +510,7 @@ let gcc_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__builtin_unreachable", special' [] @@ fun () -> if get_bool "sem.builtin_unreachable.dead_code" then Abort else Unknown); (* https://github.com/sosy-lab/sv-benchmarks/issues/1296 *) ("__assert_rtn", special [drop "func" [r]; drop "file" [r]; drop "line" []; drop "exp" [r]] @@ Abort); (* MacOS's built-in assert *) ("__assert_fail", special [drop "assertion" [r]; drop "file" [r]; drop "line" []; drop "function" [r]] @@ Abort); (* gcc's built-in assert *) + ("__assert", special [drop "assertion" [r]; drop "file" [r]; drop "line" []] @@ Abort); (* header says: The following is not at all used here but needed for standard compliance. *) ("__builtin_return_address", unknown [drop "level" []]); ("__builtin___sprintf_chk", unknown (drop "s" [w] :: drop "flag" [] :: drop "os" [] :: drop "fmt" [r] :: VarArgs (drop' [r]))); ("__builtin_add_overflow", unknown [drop "a" []; drop "b" []; drop "c" [w]]); From c4353f9f2c46e1f9b6b6d0e9d3d7168ccdc8a577 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 8 Nov 2023 14:15:02 +0100 Subject: [PATCH 0947/1312] Move `_exit` to C, as C usually takes precedence over posix for us --- src/analyses/libraryFunctions.ml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index eeddfbb218..7774e37ee2 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -340,7 +340,6 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("regexec", unknown [drop "preg" [r_deep]; drop "string" [r]; drop "nmatch" []; drop "pmatch" [w_deep]; drop "eflags" []]); ("regfree", unknown [drop "preg" [f_deep]]); ("ffs", unknown [drop "i" []]); - ("_exit", special [drop "status" []] Abort); ("execvp", unknown [drop "file" [r]; drop "argv" [r_deep]]); ("execl", unknown (drop "path" [r] :: drop "arg" [r] :: VarArgs (drop' [r]))); ("statvfs", unknown [drop "path" [r]; drop "buf" [w]]); From c056b32954f095f4ee4ab3a2fcab536eec35bdf2 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 8 Nov 2023 20:51:19 +0100 Subject: [PATCH 0948/1312] Remove //TODO comment from `76/09` --- tests/regression/76-memleak/09-unreachable-with-local-var.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/regression/76-memleak/09-unreachable-with-local-var.c b/tests/regression/76-memleak/09-unreachable-with-local-var.c index 1614b19132..bc71bb560e 100644 --- a/tests/regression/76-memleak/09-unreachable-with-local-var.c +++ b/tests/regression/76-memleak/09-unreachable-with-local-var.c @@ -7,11 +7,9 @@ int main(int argc, char const *argv[]) { g = malloc(sizeof(int)); // Reference to g's heap contents is lost here g = NULL; - // We get a false positive for p's memory being unreachable - // It's still leaked, but due to free() being commented out - // TODO: Should we only improve the error reporting for unreachable memory in this case? + + // According to `valid-memtrack`, the memory of p is unreachable and we don't have a false positive int *p = malloc(sizeof(int)); - //free(p); return 0; //WARN } From 175b003aa3fae00a07052e6ae58d3cf6a8173cac Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 8 Nov 2023 21:52:45 +0200 Subject: [PATCH 0949/1312] Don't set `InvalidMemTrack` flag a second time Co-authored-by: Michael Schwarz --- src/analyses/memLeak.ml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index f7f555a70a..865ecaffc4 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -55,7 +55,6 @@ struct (* Check and warn if some of the allocated memory is not deallocated at program exit *) match assert_exp_imprecise, exp with | true, Some exp -> - set_mem_safety_flag InvalidMemTrack; set_mem_safety_flag InvalidMemcleanup; M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Assert expression %a is unknown. Memory leak might possibly occur for heap variables: %a" d_exp exp D.pretty allocated_mem | _ -> From 05e4892feac736b520c81c151ed5b2558187e1a0 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 8 Nov 2023 21:53:56 +0200 Subject: [PATCH 0950/1312] Don't set `InvalidMemTrack` flag a second time Co-authored-by: Michael Schwarz --- src/analyses/memLeak.ml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index 865ecaffc4..fc015f458b 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -58,7 +58,6 @@ struct set_mem_safety_flag InvalidMemcleanup; M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Assert expression %a is unknown. Memory leak might possibly occur for heap variables: %a" d_exp exp D.pretty allocated_mem | _ -> - set_mem_safety_flag InvalidMemTrack; set_mem_safety_flag InvalidMemcleanup; M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory leak detected for heap variables: %a" D.pretty allocated_mem From f343d74efa850011f6ebebf1ce819f9bc1acefb9 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 8 Nov 2023 22:52:27 +0100 Subject: [PATCH 0951/1312] Add 3 regr. tests for trying out global struct variables --- .../76-memleak/10-global-struct-no-ptr.c | 16 ++++++++++++ .../76-memleak/11-global-struct-ptr.c | 19 ++++++++++++++ .../76-memleak/12-global-nested-struct-ptr.c | 25 +++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 tests/regression/76-memleak/10-global-struct-no-ptr.c create mode 100644 tests/regression/76-memleak/11-global-struct-ptr.c create mode 100644 tests/regression/76-memleak/12-global-nested-struct-ptr.c diff --git a/tests/regression/76-memleak/10-global-struct-no-ptr.c b/tests/regression/76-memleak/10-global-struct-no-ptr.c new file mode 100644 index 0000000000..490b2bb443 --- /dev/null +++ b/tests/regression/76-memleak/10-global-struct-no-ptr.c @@ -0,0 +1,16 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak +#include + +typedef struct st { + int *a; + int b; +} st; + +st st_nonptr; + +int main(int argc, char const *argv[]) { + st_nonptr.a = malloc(sizeof(int)); + st_nonptr.a = NULL; + + return 0; //WARN +} diff --git a/tests/regression/76-memleak/11-global-struct-ptr.c b/tests/regression/76-memleak/11-global-struct-ptr.c new file mode 100644 index 0000000000..4ebe1c16b8 --- /dev/null +++ b/tests/regression/76-memleak/11-global-struct-ptr.c @@ -0,0 +1,19 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak +#include + +typedef struct st { + int *a; + int b; +} st; + +st *st_ptr; + +int main(int argc, char const *argv[]) { + st_ptr = malloc(sizeof(st)); + st_ptr->a = malloc(sizeof(int)); + st_ptr->a = NULL; + free(st_ptr); + + // Only st_ptr->a is causing trouble here + return 0; //WARN +} diff --git a/tests/regression/76-memleak/12-global-nested-struct-ptr.c b/tests/regression/76-memleak/12-global-nested-struct-ptr.c new file mode 100644 index 0000000000..e0f5175064 --- /dev/null +++ b/tests/regression/76-memleak/12-global-nested-struct-ptr.c @@ -0,0 +1,25 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak +#include + +typedef struct st { + int *a; + int b; +} st; + +typedef struct st2 { + st *st_ptr; +} st2; + +st2 *st_var; + +int main(int argc, char const *argv[]) { + st_var = malloc(sizeof(st2)); + st_var->st_ptr = malloc(sizeof(st)); + st_var->st_ptr->a = malloc(sizeof(int)); + st_var->st_ptr->a = NULL; + free(st_var->st_ptr); + free(st_var); + + // Only st_var->st_ptr->a is causing trouble here + return 0; //WARN +} From dd3de9e01ef9bce8d04369c7e698f973f25d00ea Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 9 Nov 2023 10:16:11 +0100 Subject: [PATCH 0952/1312] Move `_exit` back --- src/analyses/libraryFunctions.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 7774e37ee2..df3217e380 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -150,7 +150,6 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("atomic_load", unknown [drop "obj" [r]]); ("atomic_store", unknown [drop "obj" [w]; drop "desired" []]); ("_Exit", special [drop "status" []] @@ Abort); - ("_exit", special [drop "status" []] @@ Abort); ] (** C POSIX library functions. @@ -340,6 +339,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("regexec", unknown [drop "preg" [r_deep]; drop "string" [r]; drop "nmatch" []; drop "pmatch" [w_deep]; drop "eflags" []]); ("regfree", unknown [drop "preg" [f_deep]]); ("ffs", unknown [drop "i" []]); + ("_exit", special [drop "status" []] @@ Abort); ("execvp", unknown [drop "file" [r]; drop "argv" [r_deep]]); ("execl", unknown (drop "path" [r] :: drop "arg" [r] :: VarArgs (drop' [r]))); ("statvfs", unknown [drop "path" [r]; drop "buf" [w]]); From 39a942752434625e0044f59158484d203752fb58 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 9 Nov 2023 10:18:15 +0100 Subject: [PATCH 0953/1312] Move `fread_unlocked` to glibc --- src/analyses/libraryFunctions.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index df3217e380..117dcbd236 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -52,7 +52,6 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("putc", unknown [drop "ch" []; drop "stream" [r_deep; w_deep]]); ("fputs", unknown [drop "str" [r]; drop "stream" [r_deep; w_deep]]); ("fread", unknown [drop "buffer" [w]; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); - ("fread_unlocked", unknown ~attrs:[ThreadUnsafe] [drop "buffer" [w]; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); ("fseek", unknown [drop "stream" [r_deep; w_deep]; drop "offset" []; drop "origin" []]); ("ftell", unknown [drop "stream" [r_deep]]); ("fwrite", unknown [drop "buffer" [r]; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); @@ -581,6 +580,7 @@ let glibc_desc_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__fgets_chk", unknown [drop "__s" [w]; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); ("__fread_alias", unknown [drop "__ptr" [w]; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); ("__fread_chk", unknown [drop "__ptr" [w]; drop "__ptrlen" []; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); + ("fread_unlocked", unknown ~attrs:[ThreadUnsafe] [drop "buffer" [w]; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); ("__fread_unlocked_alias", unknown ~attrs:[ThreadUnsafe] [drop "__ptr" [w]; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); ("__fread_unlocked_chk", unknown ~attrs:[ThreadUnsafe] [drop "__ptr" [w]; drop "__ptrlen" []; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); ("__fread_unlocked_chk_warn", unknown ~attrs:[ThreadUnsafe] [drop "__ptr" [w]; drop "__ptrlen" []; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); From f7c022fcfbf53f9b0673dcd78a69f9beb176643a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 9 Nov 2023 12:38:50 +0200 Subject: [PATCH 0954/1312] Account for invariant_set size when esitmating YAML witness buffer --- src/witness/yamlWitness.ml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index a580b319db..2c0340a997 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -143,12 +143,12 @@ struct } end -let yaml_entries_to_file yaml_entries file = +let yaml_entries_to_file ?(invariants=0) yaml_entries file = let yaml = `A yaml_entries in (* Yaml_unix.to_file_exn file yaml *) (* to_file/to_string uses a fixed-size buffer... *) (* estimate how big it should be + extra in case empty *) - let text = match Yaml.to_string ~len:(List.length yaml_entries * 4096 + 2048) yaml with + let text = match Yaml.to_string ~len:((List.length yaml_entries + invariants) * 4096 + 2048) yaml with | Ok text -> text | Error (`Msg m) -> failwith ("Yaml.to_string: " ^ m) in @@ -412,7 +412,7 @@ struct in (* Generate invariant set *) - let entries = + let (entries, invariants) = if entry_type_enabled YamlWitnessType.InvariantSet.entry_type then ( let invariants = [] in @@ -473,10 +473,10 @@ struct let invariants = List.rev invariants in let entry = Entry.invariant_set ~task ~invariants in - entry :: entries + (entry :: entries, List.length invariants) ) else - entries + (entries, 0) in let yaml_entries = List.rev_map YamlWitnessType.Entry.to_yaml entries in (* reverse to make entries in file in the same order as generation messages *) @@ -485,7 +485,7 @@ struct (Pretty.dprintf "total generation entries: %d" (List.length yaml_entries), None); ]; - yaml_entries_to_file yaml_entries (Fpath.v (GobConfig.get_string "witness.yaml.path")) + yaml_entries_to_file ~invariants yaml_entries (Fpath.v (GobConfig.get_string "witness.yaml.path")) let write () = Timing.wrap "yaml witness" write () From d884744ccb793eb4e0c21fdb507e876932efcc9f Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 9 Nov 2023 12:43:33 +0200 Subject: [PATCH 0955/1312] Add option witness.yaml.format-version --- src/common/util/options.schema.json | 10 ++++++++++ src/witness/yamlWitness.ml | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/common/util/options.schema.json b/src/common/util/options.schema.json index a51c5c59cf..5993c5b1e4 100644 --- a/src/common/util/options.schema.json +++ b/src/common/util/options.schema.json @@ -2418,6 +2418,16 @@ "type": "boolean", "default": false }, + "format-version": { + "title": "witness.yaml.format-version", + "description": "YAML witness format version", + "type": "string", + "enum": [ + "0.1", + "2.0" + ], + "default": "0.1" + }, "entry-types": { "title": "witness.yaml.entry-types", "description": "YAML witness entry types to output/input.", diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index 2c0340a997..019ee67813 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -25,7 +25,7 @@ struct let uuid = Uuidm.v4_gen uuid_random_state () in let creation_time = TimeUtil.iso8601_now () in { - format_version = "0.1"; + format_version = GobConfig.get_string "witness.yaml.format-version"; uuid = Uuidm.to_string uuid; creation_time; producer; From a933266b4a6fbf8a47937d657401dc6e14b378f6 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 9 Nov 2023 13:08:52 +0200 Subject: [PATCH 0956/1312] Fix invariant_set loop_invariant option check --- src/witness/yamlWitness.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index 019ee67813..f8532a1551 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -446,7 +446,7 @@ struct (* Generate loop invariants *) let invariants = - if entry_type_enabled YamlWitnessType.InvariantSet.LoopInvariant.invariant_type then ( + if invariant_type_enabled YamlWitnessType.InvariantSet.LoopInvariant.invariant_type then ( NH.fold (fun n local acc -> let loc = Node.location n in if WitnessInvariant.emit_loop_head && WitnessUtil.NH.mem WitnessInvariant.loop_heads n then ( From 7fc834308201ea8e7d2cd0d8c79bfe0c81833cae Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 9 Nov 2023 13:56:10 +0200 Subject: [PATCH 0957/1312] Fix relation read_globals_to_locals reading untracked variables --- src/analyses/apron/relationAnalysis.apron.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index 13f549fc44..0fa26781dd 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -70,7 +70,7 @@ struct let visitor = object inherit nopCilVisitor method! vlval = function - | (Var v, NoOffset) when v.vglob || ThreadEscape.has_escaped ask v -> + | (Var v, NoOffset) when (v.vglob || ThreadEscape.has_escaped ask v) && RD.Tracked.varinfo_tracked v -> let v_in = if VH.mem v_ins v then VH.find v_ins v From fbc66e32b897891d5a00dbe47ee719df7de75772 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 9 Nov 2023 15:18:59 +0200 Subject: [PATCH 0958/1312] Remove default Apron polyhedra in svcomp24-validate conf --- conf/svcomp24-validate.json | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/conf/svcomp24-validate.json b/conf/svcomp24-validate.json index 6479bd01b5..ce11af12f6 100644 --- a/conf/svcomp24-validate.json +++ b/conf/svcomp24-validate.json @@ -12,10 +12,6 @@ "float": { "interval": true }, - "apron": { - "domain": "polyhedra", - "strengthening": true - }, "activated": [ "base", "threadid", @@ -35,8 +31,7 @@ "region", "thread", "threadJoins", - "unassume", - "apron" + "unassume" ], "path_sens": [ "mutex", From fb92abc2904021e5ec3f7bf075b25bf305a97c14 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 10 Nov 2023 19:24:20 +0100 Subject: [PATCH 0959/1312] Rename NonTerminating -> Termination --- scripts/update_suite.rb | 2 +- src/analyses/loopTermination.ml | 4 ++-- src/common/util/messageCategory.ml | 10 +++++----- src/common/util/options.schema.json | 6 +++--- src/framework/constraints.ml | 4 ++-- src/framework/control.ml | 2 +- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/scripts/update_suite.rb b/scripts/update_suite.rb index a7d60b5c21..bbf1a71a9d 100755 --- a/scripts/update_suite.rb +++ b/scripts/update_suite.rb @@ -145,7 +145,7 @@ def collect_warnings @vars = $1 @evals = $2 end - if l =~ /\[NonTerminating\]/ then warnings[-1] = "nonterm" end # Get NonTerminating warning + if l =~ /\[Termination\]/ then warnings[-1] = "nonterm" end # Get Termination warning next unless l =~ /(.*)\(.*?\:(\d+)(?:\:\d+)?(?:-(?:\d+)(?:\:\d+)?)?\)/ obj,i = $1,$2.to_i diff --git a/src/analyses/loopTermination.ml b/src/analyses/loopTermination.ml index 077615ad10..f4db165795 100644 --- a/src/analyses/loopTermination.ml +++ b/src/analyses/loopTermination.ml @@ -49,7 +49,7 @@ struct let finalize () = (* Multithreaded *) if not (!single_thread) then ( - M.warn ~category:NonTerminating "The program might not terminate! (Multithreaded)\n" + M.warn ~category:Termination "The program might not terminate! (Multithreaded)\n" ) (** Recognizes a call of [__goblint_bounded] to check the EvalInt of the @@ -64,7 +64,7 @@ struct ctx.sideg () (G.add (`Lifted loop_statement) is_bounded (ctx.global ())); (* In case the loop is not bounded, a warning is created. *) if not (is_bounded) then ( - M.warn ~loc:(M.Location.CilLocation (Cilfacade.get_stmtLoc loop_statement)) ~category:NonTerminating "The program might not terminate! (Loop analysis)" + M.warn ~loc:(M.Location.CilLocation (Cilfacade.get_stmtLoc loop_statement)) ~category:Termination "The program might not terminate! (Loop analysis)" ); () with Not_found -> diff --git a/src/common/util/messageCategory.ml b/src/common/util/messageCategory.ml index ba589db8d0..41c9bc08e1 100644 --- a/src/common/util/messageCategory.ml +++ b/src/common/util/messageCategory.ml @@ -46,7 +46,7 @@ type category = | Imprecise | Witness | Program - | NonTerminating + | Termination [@@deriving eq, ord, hash] type t = category [@@deriving eq, ord, hash] @@ -205,7 +205,7 @@ let should_warn e = | Imprecise -> "imprecise" | Witness -> "witness" | Program -> "program" - | NonTerminating -> "nonTerminating" + | Termination -> "termination" (* Don't forget to add option to schema! *) in get_bool ("warn." ^ (to_string e)) @@ -226,7 +226,7 @@ let path_show e = | Imprecise -> ["Imprecise"] | Witness -> ["Witness"] | Program -> ["Program"] - | NonTerminating -> ["NonTerminating"] + | Termination -> ["Termination"] let show x = String.concat " > " (path_show x) @@ -266,7 +266,7 @@ let categoryName = function | Overflow -> "Overflow"; | DivByZero -> "DivByZero") | Float -> "Float" - | NonTerminating -> "NonTerminating" + | Termination -> "Termination" let from_string_list (s: string list) = @@ -287,7 +287,7 @@ let from_string_list (s: string list) = | "imprecise" -> Imprecise | "witness" -> Witness | "program" -> Program - | "nonTerminating" -> NonTerminating + | "termination" -> Termination | _ -> Unknown let to_yojson x = `List (List.map (fun x -> `String x) (path_show x)) diff --git a/src/common/util/options.schema.json b/src/common/util/options.schema.json index c6e00d3da3..0b6f8b649f 100644 --- a/src/common/util/options.schema.json +++ b/src/common/util/options.schema.json @@ -2105,9 +2105,9 @@ "type": "boolean", "default": true }, - "nonTerminating": { - "title": "warn.nonTerminating", - "description": "Non Termination warning", + "termination": { + "title": "warn.termination", + "description": "Non-Termination warning", "type": "boolean", "default": true }, diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 6ba31488de..b1bbc73660 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1689,7 +1689,7 @@ struct List.iter handle_path (S.paths_as_set conv_ctx); if !AnalysisState.should_warn && List.mem "termination" @@ get_string_list "ana.activated" then ( AnalysisState.svcomp_may_not_terminate := true; - M.warn ~category:NonTerminating "The program might not terminate! (Longjmp)" + M.warn ~category:Termination "The program might not terminate! (Longjmp)" ); S.D.bot () | _ -> S.special conv_ctx lv f args @@ -1774,7 +1774,7 @@ struct AnalysisState.svcomp_may_not_terminate := true; (*set the indicator for a non-terminating program for the sv comp*) (*Cycle found*) let loc = M.Location.CilLocation fundec.svar.vdecl in - M.warn ~loc ~category:NonTerminating "The program might not terminate! (Fundec %a is contained in a call graph cycle)" CilType.Fundec.pretty fundec) (* output a warning for non-termination*) + M.warn ~loc ~category:Termination "The program might not terminate! (Fundec %a is contained in a call graph cycle)" CilType.Fundec.pretty fundec) (* output a warning for non-termination*) else if not (LH.mem global_visited_calls call) then begin LH.replace global_visited_calls call (); let new_path_visited_calls = LS.add call path_visited_calls in diff --git a/src/framework/control.ml b/src/framework/control.ml index ae1cb3e2b3..a78f125eae 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -146,7 +146,7 @@ struct let fundec_live = live fi fname in if ( not (BatISet.is_empty fundec_live)) then ( AnalysisState.svcomp_may_not_terminate := true; - M.warn ~loc:(M.Location.CilLocation l) ~category:NonTerminating "The program might not terminate! (Upjumping Goto)"); + M.warn ~loc:(M.Location.CilLocation l) ~category:Termination "The program might not terminate! (Upjumping Goto)"); ) (!live_lines)) (!Cilfacade.upjumping_gotos); From aad465cf1803bc9ee0d10373e90397e4f0809321 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 10 Nov 2023 19:33:35 +0100 Subject: [PATCH 0960/1312] Autotune for Termination --- src/autoTune.ml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/autoTune.ml b/src/autoTune.ml index 8300241bac..c756b17eb2 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -243,7 +243,14 @@ let focusOnSpecification (spec: Svcomp.Specification.t) = | NoDataRace -> (*enable all thread analyses*) print_endline @@ "Specification: NoDataRace -> enabling thread analyses \"" ^ (String.concat ", " notNeccessaryThreadAnalyses) ^ "\""; enableAnalyses notNeccessaryThreadAnalyses; - | Termination -> () + | Termination -> + let terminationAnas = ["termination"; "isEverMultiThreaded"; "apron"] in + print_endline @@ "Specification: Termination -> enabling termination analyses \"" ^ (String.concat ", " terminationAnas) ^ "\""; + enableAnalyses terminationAnas; + set_string "sem.int.signed_overflow" "assume_none"; + set_bool "ana.int.interval" true; + set_string "ana.apron.domain" "polyhedra"; (* TODO: Needed? *) + () | NoOverflow -> (*We focus on integer analysis*) set_bool "ana.int.def_exc" true; set_bool "ana.int.interval" true From 2ff979bb14dd41896e1ad3f2c3dff6f1af574241 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 10 Nov 2023 19:54:35 +0100 Subject: [PATCH 0961/1312] Simplify variable names --- src/util/terminationPreprocessing.ml | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index 32a9468233..8070438943 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -2,22 +2,16 @@ open GoblintCil module VarToStmt = Map.Make(CilType.Varinfo) (* maps varinfos (= loop counter variable) to the statement of the corresponding loop*) -let extract_file_name s = (*There still may be a need to filter more chars*) - let ls = String.split_on_char '/' s in (*Assuming '/' as path seperator*) - let ls = List.rev ls in - let s' = List.nth ls 0 in - let ls = String.split_on_char '.' s' in - let s' = List.nth ls 0 in - let without_spaces = String.split_on_char ' ' s' in - let s' = String.concat "" without_spaces in - s' - -let show_location_id l = - string_of_int l.line ^ "_" ^ string_of_int l.column ^ "-file" ^ "_" ^ extract_file_name l.file - class loopCounterVisitor lc (fd : fundec) = object(self) inherit nopCilVisitor + (* Counter of variables inserted for termination *) + val mutable vcounter = ref 0 + + method! vfunc _ = + vcounter := 0; + DoChildren + method! vstmt s = let specialFunction name = @@ -36,9 +30,9 @@ class loopCounterVisitor lc (fd : fundec) = object(self) let action s = match s.skind with | Loop (b, loc, eloc, _, _) -> - let name = "term"^show_location_id loc in - let typ = Cil.intType in - let v = (Cil.makeLocalVar fd name typ) in (*Not tested for incremental mode*) + let vname = "term" ^ string_of_int loc.line ^ "_" ^ string_of_int loc.column ^ "-id" ^ (string_of_int !vcounter) in + incr vcounter; + let v = Cil.makeLocalVar fd vname Cil.intType in (*Not tested for incremental mode*) let init_stmt = mkStmtOneInstr @@ Set (var v, min_int_exp, loc, eloc) in let inc_stmt = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in let inc_stmt2 = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in From b49356c2451d2576b73d2d42813aad01c37c5eaa Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 10 Nov 2023 20:02:52 +0100 Subject: [PATCH 0962/1312] Deduplicate preprocessing --- src/analyses/loopTermination.ml | 2 +- src/common/util/cilfacade.ml | 16 +--------------- src/util/cilCfg.ml | 6 ++---- 3 files changed, 4 insertions(+), 20 deletions(-) diff --git a/src/analyses/loopTermination.ml b/src/analyses/loopTermination.ml index f4db165795..312bcfd9b9 100644 --- a/src/analyses/loopTermination.ml +++ b/src/analyses/loopTermination.ml @@ -99,5 +99,5 @@ struct end let () = - Cilfacade.register_preprocess_cil (Spec.name ()) (new loopCounterVisitor loop_counters); + Cilfacade.register_preprocess (Spec.name ()) (new loopCounterVisitor loop_counters); MCP.register_analysis (module Spec : MCPSpec) diff --git a/src/common/util/cilfacade.ml b/src/common/util/cilfacade.ml index 47cf6d6210..d645fd949d 100644 --- a/src/common/util/cilfacade.ml +++ b/src/common/util/cilfacade.ml @@ -74,27 +74,13 @@ let print (fileAST: file) = let rmTemps fileAST = RmUnused.removeUnused fileAST - let visitors = ref [] let register_preprocess name visitor_fun = visitors := !visitors @ [name, visitor_fun] let do_preprocess ast = - let f fd (name, visitor_fun) = - (* this has to be done here, since the settings aren't available when register_preprocess is called *) - if List.mem name (get_string_list "ana.activated") then - ignore @@ visitCilFunction (visitor_fun fd) fd - in - iterGlobals ast (function GFun (fd,_) -> List.iter (f fd) !visitors | _ -> ()) - -let visitors_cil = ref [] -(* does exactly the same as register_preprocess but it is executed earlier, before the CFG is created*) -let register_preprocess_cil name visitor_fun = - visitors_cil := !visitors_cil @ [name, visitor_fun] - -let do_preprocess_cil ast = (* this has to be done here, since the settings aren't available when register_preprocess is called *) - let active_visitors = List.filter_map (fun (name, visitor_fun) -> if List.mem name (get_string_list "ana.activated") then Some visitor_fun else None) !visitors_cil in + let active_visitors = List.filter_map (fun (name, visitor_fun) -> if List.mem name (get_string_list "ana.activated") then Some visitor_fun else None) !visitors in let f fd visitor_fun = ignore @@ visitCilFunction (visitor_fun fd) fd in if active_visitors <> [] then iterGlobals ast (function GFun (fd,_) -> List.iter (f fd) active_visitors | _ -> ()) diff --git a/src/util/cilCfg.ml b/src/util/cilCfg.ml index 72143e97d7..923cf7600b 100644 --- a/src/util/cilCfg.ml +++ b/src/util/cilCfg.ml @@ -42,6 +42,7 @@ let loopCount file = let createCFG (fileAST: file) = + Cilfacade.do_preprocess fileAST; (* The analyzer keeps values only for blocks. So if you want a value for every program point, each instruction *) (* needs to be in its own block. end_basic_blocks does that. *) (* After adding support for VLAs, there are new VarDecl instructions at the point where a variable was declared and *) @@ -50,7 +51,6 @@ let createCFG (fileAST: file) = (* Since we want the output of justcil to compile, we do not run allBB visitor if justcil is enable, regardless of *) (* exp.basic-blocks. This does not matter, as we will not run any analysis anyway, when justcil is enabled. *) (* the preprocessing must be done here, to add the changes of CIL to the CFG*) - Cilfacade.do_preprocess_cil fileAST; if not (get_bool "exp.basic-blocks") && not (get_bool "justcil") then end_basic_blocks fileAST; (* We used to renumber vids but CIL already generates them fresh, so no need. @@ -68,6 +68,4 @@ let createCFG (fileAST: file) = computeCFGInfo fd true | _ -> () ); - if get_bool "dbg.run_cil_check" then assert (Check.checkFile [] fileAST); - - Cilfacade.do_preprocess fileAST + if get_bool "dbg.run_cil_check" then assert (Check.checkFile [] fileAST); \ No newline at end of file From 490203df2dcf64cb8a9ccfcbd8286bc50f85e37a Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 10 Nov 2023 20:21:51 +0100 Subject: [PATCH 0963/1312] Move `everMultiThreaded` to `threadflag` --- src/analyses/everMultiThreaded.ml | 47 ------------------------------- src/analyses/threadFlag.ml | 4 +++ src/autoTune.ml | 4 +-- src/framework/analyses.ml | 6 ++++ src/maingoblint.ml | 4 +-- 5 files changed, 14 insertions(+), 51 deletions(-) delete mode 100644 src/analyses/everMultiThreaded.ml diff --git a/src/analyses/everMultiThreaded.ml b/src/analyses/everMultiThreaded.ml deleted file mode 100644 index 2ab203887c..0000000000 --- a/src/analyses/everMultiThreaded.ml +++ /dev/null @@ -1,47 +0,0 @@ -(** Analysis to register whether any additional thread has ever been spawned ([evermultithreaded]). *) - -open Analyses - -module UnitV = -struct - include Printable.Unit - include StdV -end - -module Spec : Analyses.MCPSpec = -struct - - (** Provides some default implementations *) - include Analyses.IdentitySpec - - let name () = "evermultithreaded" - - module D = Lattice.Unit - module C = D - module V = UnitV - module G = BoolDomain.MayBool - - let startstate _ = () - let exitstate = startstate - - (** Sets the global invariant to true when a thread is spawned *) - let threadspawn ctx ~multiple lval f args fctx = - ctx.sideg () true; - () - - let query ctx (type a) (q: a Queries.t) : a Queries.result = - match q with - | Queries.IsEverMultiThreaded -> - (match ctx.global () with - (* I don't know why this wrapping in a match construct is necessary. - * Without it, the compiler throws an error. *) - true -> true - | false -> false) - | _ -> - Queries.Result.top q - -end - -let () = - (* Register this analysis within the master control program *) - MCP.register_analysis (module Spec : MCPSpec) diff --git a/src/analyses/threadFlag.ml b/src/analyses/threadFlag.ml index 6bd466caef..a751ae074a 100644 --- a/src/analyses/threadFlag.ml +++ b/src/analyses/threadFlag.ml @@ -21,6 +21,8 @@ struct module D = Flag module C = Flag module P = IdentityP (D) + module V = UnitV + module G = BoolDomain.MayBool let name () = "threadflag" @@ -44,6 +46,7 @@ struct match x with | Queries.MustBeSingleThreaded _ -> not (Flag.is_multi ctx.local) (* If this analysis can tell, it is the case since the start *) | Queries.MustBeUniqueThread -> not (Flag.is_not_main ctx.local) + | Queries.IsEverMultiThreaded -> (ctx.global () : bool) (* requires annotation to compile *) (* This used to be in base but also commented out. *) (* | Queries.MayBePublic _ -> Flag.is_multi ctx.local *) | _ -> Queries.Result.top x @@ -64,6 +67,7 @@ struct [create_tid f] let threadspawn ctx ~multiple lval f args fctx = + ctx.sideg () true; if not (has_ever_been_multi (Analyses.ask_of_ctx ctx)) then ctx.emit Events.EnterMultiThreaded; D.join ctx.local (Flag.get_main ()) diff --git a/src/autoTune.ml b/src/autoTune.ml index c756b17eb2..51e4ea412e 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -180,7 +180,7 @@ let enableAnalyses anas = List.iter (GobConfig.set_auto "ana.activated[+]") anas (*If only one thread is used in the program, we can disable most thread analyses*) -(*The exceptions are analyses that are depended on by others: base -> mutex -> mutexEvents, access; termination -> isEverMultiThreaded *) +(*The exceptions are analyses that are depended on by others: base -> mutex -> mutexEvents, access; termination -> threadflag *) (*escape is also still enabled, because otherwise we get a warning*) (*does not consider dynamic calls!*) @@ -244,7 +244,7 @@ let focusOnSpecification (spec: Svcomp.Specification.t) = print_endline @@ "Specification: NoDataRace -> enabling thread analyses \"" ^ (String.concat ", " notNeccessaryThreadAnalyses) ^ "\""; enableAnalyses notNeccessaryThreadAnalyses; | Termination -> - let terminationAnas = ["termination"; "isEverMultiThreaded"; "apron"] in + let terminationAnas = ["termination"; "threadflag"; "apron"] in print_endline @@ "Specification: Termination -> enabling termination analyses \"" ^ (String.concat ", " terminationAnas) ^ "\""; enableAnalyses terminationAnas; set_string "sem.int.signed_overflow" "assume_none"; diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 5fb09546cb..a37a3043c2 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -612,6 +612,12 @@ struct let is_write_only _ = false end +module UnitV = +struct + include Printable.Unit + include StdV +end + module VarinfoV = struct include CilType.Varinfo (* TODO: or Basetype.Variables? *) diff --git a/src/maingoblint.ml b/src/maingoblint.ml index b9cc6c7e47..937c646d47 100644 --- a/src/maingoblint.ml +++ b/src/maingoblint.ml @@ -162,9 +162,9 @@ let check_arguments () = if get_bool "solvers.td3.space" && get_bool "solvers.td3.remove-wpoint" then fail "solvers.td3.space is incompatible with solvers.td3.remove-wpoint"; if get_bool "solvers.td3.space" && get_string "solvers.td3.side_widen" = "sides-local" then fail "solvers.td3.space is incompatible with solvers.td3.side_widen = 'sides-local'"; if List.mem "termination" @@ get_string_list "ana.activated" then ( - set_list "ana.activated" (GobConfig.get_list "ana.activated" @ [`String ("evermultithreaded")]); + set_list "ana.activated" (GobConfig.get_list "ana.activated" @ [`String ("threadflag")]); set_string "sem.int.signed_overflow" "assume_none"; - warn "termination analysis implicitly activates evermultithreaded analysis and set sem.int.signed_overflow to assume_none" + warn "termination analysis implicitly activates threadflag analysis and set sem.int.signed_overflow to assume_none" ); if not (get_bool "ana.sv-comp.enabled") && get_bool "witness.graphml.enabled" then fail "witness.graphml.enabled: cannot generate GraphML witness without SV-COMP mode (ana.sv-comp.enabled)" From e25b708198ab19972677c3a4f7f4de5ca0243209 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 10 Nov 2023 20:40:24 +0100 Subject: [PATCH 0964/1312] Simplify `loopTermination` --- src/analyses/loopTermination.ml | 40 ++++++++------------------------- 1 file changed, 9 insertions(+), 31 deletions(-) diff --git a/src/analyses/loopTermination.ml b/src/analyses/loopTermination.ml index 312bcfd9b9..e24b321d05 100644 --- a/src/analyses/loopTermination.ml +++ b/src/analyses/loopTermination.ml @@ -4,10 +4,6 @@ open Analyses open GoblintCil open TerminationPreprocessing -(** Stores the result of the query whether the program is single threaded or not - since finalize does not have access to ctx. *) -let single_thread : bool ref = ref false - (** Contains all loop counter variables (varinfo) and maps them to their corresponding loop statement. *) let loop_counters : stmt VarToStmt.t ref = ref VarToStmt.empty @@ -35,23 +31,12 @@ struct module D = Lattice.Unit module C = D - module V = - struct - include Printable.Unit - let is_write_only _ = true - end + module V = UnitV module G = MapDomain.MapBot (Statements) (BoolDomain.MustBool) let startstate _ = () let exitstate = startstate - (** Warnings for detected possible non-termination *) - let finalize () = - (* Multithreaded *) - if not (!single_thread) then ( - M.warn ~category:Termination "The program might not terminate! (Multithreaded)\n" - ) - (** Recognizes a call of [__goblint_bounded] to check the EvalInt of the * respective loop counter variable at that position. *) let special ctx (lval : lval option) (f : varinfo) (arglist : exp list) = @@ -72,28 +57,21 @@ struct | _ -> () else () - (* Checks whether the program always remains single-threaded. - If the program does not remain single-threaded, we assume non-termination (see query function). *) - let must_always_be_single_threaded ctx = - let single_threaded = not (ctx.ask Queries.IsEverMultiThreaded) in - single_thread := single_threaded; - single_threaded - let query ctx (type a) (q: a Queries.t): a Queries.result = match q with | Queries.MustTermLoop loop_statement -> - must_always_be_single_threaded ctx + let multithreaded = ctx.ask Queries.IsEverMultiThreaded in + (not multithreaded) && (match G.find_opt (`Lifted loop_statement) (ctx.global ()) with Some b -> b | None -> false) | Queries.MustTermAllLoops -> - let always_single_threaded = must_always_be_single_threaded ctx in - (* Must be the first to be evaluated! This has the side effect that - * single_thread is set. In case of another order and due to lazy - * evaluation, the correct value of single_thread can not be guaranteed! - * Therefore, we use a let-in clause here. *) - always_single_threaded - && G.for_all (fun _ term_info -> term_info) (ctx.global ()) + let multithreaded = ctx.ask Queries.IsEverMultiThreaded in + if multithreaded then ( + M.warn ~category:Termination "The program might not terminate! (Multithreaded)\n"; + false) + else + G.for_all (fun _ term_info -> term_info) (ctx.global ()) | _ -> Queries.Result.top q end From d4ab699b1717ba1b52d86ef609c6f8800f552e13 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 10 Nov 2023 20:46:37 +0100 Subject: [PATCH 0965/1312] Fail when `termination` is active and incremental analysis is enabled --- src/maingoblint.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/maingoblint.ml b/src/maingoblint.ml index 937c646d47..878e68dcf5 100644 --- a/src/maingoblint.ml +++ b/src/maingoblint.ml @@ -162,6 +162,7 @@ let check_arguments () = if get_bool "solvers.td3.space" && get_bool "solvers.td3.remove-wpoint" then fail "solvers.td3.space is incompatible with solvers.td3.remove-wpoint"; if get_bool "solvers.td3.space" && get_string "solvers.td3.side_widen" = "sides-local" then fail "solvers.td3.space is incompatible with solvers.td3.side_widen = 'sides-local'"; if List.mem "termination" @@ get_string_list "ana.activated" then ( + if GobConfig.get_bool "incremental.load" || GobConfig.get_bool "incremental.save" then fail "termination analysis is not compatible with incremental analysis"; set_list "ana.activated" (GobConfig.get_list "ana.activated" @ [`String ("threadflag")]); set_string "sem.int.signed_overflow" "assume_none"; warn "termination analysis implicitly activates threadflag analysis and set sem.int.signed_overflow to assume_none" From 47d0f5d258c493a8ee05691a29cdcee5dce55c32 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 10 Nov 2023 20:55:07 +0100 Subject: [PATCH 0966/1312] Termination: set `is_write_only` --- src/analyses/loopTermination.ml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/analyses/loopTermination.ml b/src/analyses/loopTermination.ml index e24b321d05..7cde0cb9c6 100644 --- a/src/analyses/loopTermination.ml +++ b/src/analyses/loopTermination.ml @@ -31,7 +31,10 @@ struct module D = Lattice.Unit module C = D - module V = UnitV + module V = struct + include UnitV + let is_write_only _ = true + end module G = MapDomain.MapBot (Statements) (BoolDomain.MustBool) let startstate _ = () From 4417b9c8f02fb349184e27342e1a8b981f01cb18 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 10 Nov 2023 21:01:26 +0100 Subject: [PATCH 0967/1312] Reset `upjumping_gotos` --- src/common/util/cilfacade.ml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/common/util/cilfacade.ml b/src/common/util/cilfacade.ml index d645fd949d..8b55c0a1ba 100644 --- a/src/common/util/cilfacade.ml +++ b/src/common/util/cilfacade.ml @@ -667,6 +667,10 @@ let find_stmt_sid sid = with Not_found -> IntH.find (ResettableLazy.force stmt_sids) sid +(** Contains the locations of the upjumping gotos and the respective functions + * they are being called in. *) +let upjumping_gotos : (location * fundec) list ref = ref [] + let reset_lazy () = StmtH.clear pseudo_return_to_fun; ResettableLazy.reset stmt_fundecs; @@ -674,7 +678,8 @@ let reset_lazy () = ResettableLazy.reset name_fundecs; ResettableLazy.reset varinfo_roles; ResettableLazy.reset original_names; - ResettableLazy.reset stmt_sids + ResettableLazy.reset stmt_sids; + upjumping_gotos := [] let stmt_pretty_short () x = @@ -699,8 +704,4 @@ let add_function_declarations (file: Cil.file): unit = in let fun_decls = List.filter_map declaration_from_GFun functions in let globals = upto_last_type @ fun_decls @ non_types @ functions in - file.globals <- globals - -(** Contains the locations of the upjumping gotos and the respective functions - * they are being called in. *) -let upjumping_gotos : (location * fundec) list ref = ref [] + file.globals <- globals \ No newline at end of file From de416eeb01e12df4874679da3437358e11405a74 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 10 Nov 2023 22:09:47 +0100 Subject: [PATCH 0968/1312] Cleanup upjumping_gotos --- src/common/util/cilfacade.ml | 10 +++++---- src/framework/control.ml | 32 ++++++++++++++++------------ src/maingoblint.ml | 2 +- src/util/terminationPreprocessing.ml | 12 ++++++++--- 4 files changed, 34 insertions(+), 22 deletions(-) diff --git a/src/common/util/cilfacade.ml b/src/common/util/cilfacade.ml index 8b55c0a1ba..26a2f082a4 100644 --- a/src/common/util/cilfacade.ml +++ b/src/common/util/cilfacade.ml @@ -666,20 +666,22 @@ let find_stmt_sid sid = try IntH.find pseudo_return_stmt_sids sid with Not_found -> IntH.find (ResettableLazy.force stmt_sids) sid +module FunLocH = Hashtbl.Make(CilType.Fundec) +module LocSet = Hashtbl.Make(CilType.Location) (** Contains the locations of the upjumping gotos and the respective functions * they are being called in. *) -let upjumping_gotos : (location * fundec) list ref = ref [] +let funs_with_upjumping_gotos: unit LocSet.t FunLocH.t = FunLocH.create 13 -let reset_lazy () = +let reset_lazy ?(keepupjumpinggotos=false) () = StmtH.clear pseudo_return_to_fun; + if not keepupjumpinggotos then FunLocH.clear funs_with_upjumping_gotos; ResettableLazy.reset stmt_fundecs; ResettableLazy.reset varinfo_fundecs; ResettableLazy.reset name_fundecs; ResettableLazy.reset varinfo_roles; ResettableLazy.reset original_names; - ResettableLazy.reset stmt_sids; - upjumping_gotos := [] + ResettableLazy.reset stmt_sids let stmt_pretty_short () x = diff --git a/src/framework/control.ml b/src/framework/control.ml index a78f125eae..0c9b61739b 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -105,6 +105,8 @@ struct let module StringMap = BatMap.Make (String) in let live_lines = ref StringMap.empty in let dead_lines = ref StringMap.empty in + let module FunSet = Hashtbl.Make (CilType.Fundec) in + let live_funs: unit FunSet.t = FunSet.create 13 in let add_one n v = match n with | Statement s when Cilfacade.(StmtH.mem pseudo_return_to_fun s) -> @@ -115,6 +117,7 @@ struct See: https://github.com/goblint/analyzer/issues/290#issuecomment-881258091. *) let l = UpdateCil.getLoc n in let f = Node.find_fundec n in + FunSet.replace live_funs f (); let add_fun = BatISet.add l.line in let add_file = StringMap.modify_def BatISet.empty f.svar.vname add_fun in let is_dead = LT.for_all (fun (_,x,f) -> Spec.D.is_bot x) v in @@ -136,20 +139,21 @@ struct try StringMap.find fn (StringMap.find file !live_lines) with Not_found -> BatISet.empty in - (*check if we have upjumping gotos*) - List.iter - (fun x -> - let ((l: location), (fd: fundec)) = x in (*unpack tuple for later use*) - let fname = fd.svar.vname in - StringMap.iter - (fun fi _ -> - let fundec_live = live fi fname in - if ( not (BatISet.is_empty fundec_live)) then ( - AnalysisState.svcomp_may_not_terminate := true; - M.warn ~loc:(M.Location.CilLocation l) ~category:Termination "The program might not terminate! (Upjumping Goto)"); - ) - (!live_lines)) - (!Cilfacade.upjumping_gotos); + if List.mem "termination" @@ get_string_list "ana.activated" then ( + (* check if we have upjumping gotos *) + let open Cilfacade in + let warn_for_upjumps fundec gotos = + if FunSet.mem live_funs fundec then ( + (* set nortermiantion flag *) + AnalysisState.svcomp_may_not_terminate := true; + (* iterate through locations to produce warnings *) + LocSet.iter (fun l _ -> + M.warn ~loc:(M.Location.CilLocation l) ~category:Termination "The program might not terminate! (Upjumping Goto)" + ) gotos + ) + in + FunLocH.iter warn_for_upjumps funs_with_upjumping_gotos + ); dead_lines := StringMap.mapi (fun fi -> StringMap.mapi (fun fu ded -> BatISet.diff ded (live fi fu))) !dead_lines; dead_lines := StringMap.map (StringMap.filter (fun _ x -> not (BatISet.is_empty x))) !dead_lines; dead_lines := StringMap.filter (fun _ x -> not (StringMap.is_empty x)) !dead_lines; diff --git a/src/maingoblint.ml b/src/maingoblint.ml index 878e68dcf5..c44a6d9360 100644 --- a/src/maingoblint.ml +++ b/src/maingoblint.ml @@ -493,7 +493,7 @@ let merge_parsed parsed = Cilfacade.current_file := merged_AST; (* Set before createCFG, so Cilfacade maps can be computed for loop unrolling. *) CilCfg.createCFG merged_AST; (* Create CIL CFG from CIL AST. *) - Cilfacade.reset_lazy (); (* Reset Cilfacade maps, which need to be recomputer after loop unrolling. *) + Cilfacade.reset_lazy ~keepupjumpinggotos:true (); (* Reset Cilfacade maps, which need to be recomputer after loop unrolling but keep gotos. *) merged_AST let preprocess_parse_merge () = diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index 8070438943..4674a0ca10 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -50,9 +50,15 @@ class loopCounterVisitor lc (fd : fundec) = object(self) | Goto (sref, l) -> let goto_jmp_stmt = sref.contents.skind in let loc_stmt = Cil.get_stmtLoc goto_jmp_stmt in - if CilType.Location.compare l loc_stmt >= 0 (*is pos if first loc is greater -> below the second loc*) - then - Cilfacade.upjumping_gotos := List.append !Cilfacade.upjumping_gotos ([(l, fd)] : (location * fundec) list); (*problem: the program might not terminate!*) + if CilType.Location.compare l loc_stmt >= 0 then ( + (* is pos if first loc is greater -> below the second loc *) + (* problem: the program might not terminate! *) + let open Cilfacade in + let current = FunLocH.find_opt funs_with_upjumping_gotos fd in + let current = BatOption.default (LocSet.create 13) current in + LocSet.replace current l (); + FunLocH.replace funs_with_upjumping_gotos fd current; + ); s | _ -> s in ChangeDoChildrenPost (s, action); From e66cf56afc8473c3bc76494a8f104ced4aa1c0ee Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Thu, 9 Nov 2023 18:12:21 +0100 Subject: [PATCH 0969/1312] Make type of variables used for loop termination analysis more easily exchangable. --- src/util/terminationPreprocessing.ml | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index 4674a0ca10..a0065caabc 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -2,6 +2,11 @@ open GoblintCil module VarToStmt = Map.Make(CilType.Varinfo) (* maps varinfos (= loop counter variable) to the statement of the corresponding loop*) +let counter_ikind = IInt +let counter_typ = TInt (counter_ikind, []) +let min_int_exp = Const(CInt(Cilint.zero_cilint, counter_ikind, None)) +let min_int_exp = Const(CInt(Cilint.shift_left_cilint Cilint.mone_cilint ((bytesSizeOfInt counter_ikind)*8-1), IInt, None)) + class loopCounterVisitor lc (fd : fundec) = object(self) inherit nopCilVisitor @@ -15,7 +20,7 @@ class loopCounterVisitor lc (fd : fundec) = object(self) method! vstmt s = let specialFunction name = - { svar = makeGlobalVar name (TFun(voidType, Some [("exp", intType, [])], false,[])); + { svar = makeGlobalVar name (TFun(voidType, Some [("exp", counter_typ, [])], false,[])); smaxid = 0; slocals = []; sformals = []; @@ -24,7 +29,12 @@ class loopCounterVisitor lc (fd : fundec) = object(self) sallstmts = []; } in - let min_int_exp = Const(CInt(Cilint.shift_left_cilint Cilint.mone_cilint ((bytesSizeOfInt IInt)*8-1), IInt, None)) in + + let increment_expression lval = + let et = typeOf lval in + let bop = PlusA in + let one = Const (CInt (Cilint.one_cilint, counter_ikind, None)) in + constFold false (BinOp(bop, lval, one, et)) in let f_bounded = Lval (var (specialFunction "__goblint_bounded").svar) in @@ -32,11 +42,12 @@ class loopCounterVisitor lc (fd : fundec) = object(self) | Loop (b, loc, eloc, _, _) -> let vname = "term" ^ string_of_int loc.line ^ "_" ^ string_of_int loc.column ^ "-id" ^ (string_of_int !vcounter) in incr vcounter; - let v = Cil.makeLocalVar fd vname Cil.intType in (*Not tested for incremental mode*) + let v = Cil.makeLocalVar fd vname counter_typ in (*Not tested for incremental mode*) + let lval = Lval (Var v, NoOffset) in let init_stmt = mkStmtOneInstr @@ Set (var v, min_int_exp, loc, eloc) in - let inc_stmt = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in - let inc_stmt2 = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in - let exit_stmt = mkStmtOneInstr @@ Call (None, f_bounded, [Lval (var v)], loc, locUnknown) in + let inc_stmt = mkStmtOneInstr @@ Set (var v, increment_expression lval, loc, eloc) in + let inc_stmt2 = mkStmtOneInstr @@ Set (var v, increment_expression lval, loc, eloc) in + let exit_stmt = mkStmtOneInstr @@ Call (None, f_bounded, [lval], loc, locUnknown) in (match b.bstmts with | s :: ss -> (*duplicate increment statement here to fix inconsistencies in nested loops*) b.bstmts <- exit_stmt :: inc_stmt :: s :: inc_stmt2 :: ss; From e02801a1dd0c33aa12917a7ec3380cd01b4ef866 Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Fri, 10 Nov 2023 10:58:41 +0100 Subject: [PATCH 0970/1312] Remove redundant code for setting todo variable --- scripts/update_suite.rb | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/scripts/update_suite.rb b/scripts/update_suite.rb index bbf1a71a9d..258626422e 100755 --- a/scripts/update_suite.rb +++ b/scripts/update_suite.rb @@ -331,20 +331,14 @@ def parse_tests (lines) end end case lines[0] - when /TODO/ - case lines[0] - when /NONTERM/ - tests[-1] = "nonterm" - todo << -1 - when /TERM/ - tests[-1] = "term" - todo << -1 - end when /NONTERM/ tests[-1] = "nonterm" when /TERM/ tests[-1] = "term" end + if lines[0] =~ /TODO/ then + todo << -1 + end Tests.new(self, tests, tests_line, todo) end From 3d3c06956f787ba0f8f98278d33a647749b3da68 Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Mon, 13 Nov 2023 11:14:45 +0100 Subject: [PATCH 0971/1312] Add define min_int_exp depending on the signedness of the counter variable introduced. Also remove custom definition for incrementing expression by one. --- src/util/terminationPreprocessing.ml | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index a0065caabc..9605484af3 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -4,8 +4,12 @@ module VarToStmt = Map.Make(CilType.Varinfo) (* maps varinfos (= loop counter va let counter_ikind = IInt let counter_typ = TInt (counter_ikind, []) -let min_int_exp = Const(CInt(Cilint.zero_cilint, counter_ikind, None)) -let min_int_exp = Const(CInt(Cilint.shift_left_cilint Cilint.mone_cilint ((bytesSizeOfInt counter_ikind)*8-1), IInt, None)) +let min_int_exp = + (* Currently only tested for IInt type, which is signed *) + if Cil.isSigned counter_ikind then + Const(CInt(Cilint.shift_left_cilint Cilint.mone_cilint ((bytesSizeOfInt counter_ikind)*8-1), IInt, None)) + else + Const(CInt(Cilint.zero_cilint, counter_ikind, None)) class loopCounterVisitor lc (fd : fundec) = object(self) inherit nopCilVisitor @@ -29,13 +33,6 @@ class loopCounterVisitor lc (fd : fundec) = object(self) sallstmts = []; } in - - let increment_expression lval = - let et = typeOf lval in - let bop = PlusA in - let one = Const (CInt (Cilint.one_cilint, counter_ikind, None)) in - constFold false (BinOp(bop, lval, one, et)) in - let f_bounded = Lval (var (specialFunction "__goblint_bounded").svar) in let action s = match s.skind with @@ -45,8 +42,8 @@ class loopCounterVisitor lc (fd : fundec) = object(self) let v = Cil.makeLocalVar fd vname counter_typ in (*Not tested for incremental mode*) let lval = Lval (Var v, NoOffset) in let init_stmt = mkStmtOneInstr @@ Set (var v, min_int_exp, loc, eloc) in - let inc_stmt = mkStmtOneInstr @@ Set (var v, increment_expression lval, loc, eloc) in - let inc_stmt2 = mkStmtOneInstr @@ Set (var v, increment_expression lval, loc, eloc) in + let inc_stmt = mkStmtOneInstr @@ Set (var v, increm lval 1, loc, eloc) in + let inc_stmt2 = mkStmtOneInstr @@ Set (var v, increm lval 1, loc, eloc) in let exit_stmt = mkStmtOneInstr @@ Call (None, f_bounded, [lval], loc, locUnknown) in (match b.bstmts with | s :: ss -> (*duplicate increment statement here to fix inconsistencies in nested loops*) From 46b0f672b2dc1a28a1c3eff6001de0b71a4f52ca Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Mon, 13 Nov 2023 11:48:18 +0100 Subject: [PATCH 0972/1312] Change indentation in update_suite.rb back to reduce number of lines changed. --- scripts/update_suite.rb | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/scripts/update_suite.rb b/scripts/update_suite.rb index 258626422e..ab2aeea53f 100755 --- a/scripts/update_suite.rb +++ b/scripts/update_suite.rb @@ -151,25 +151,25 @@ def collect_warnings ranking = ["other", "warn", "goto", "fundec", "loop", "term", "nonterm", "race", "norace", "deadlock", "nodeadlock", "success", "fail", "unknown"] thiswarn = case obj - when /\(conf\. \d+\)/ then "race" - when /Deadlock/ then "deadlock" - when /lock (before|after):/ then "deadlock" - when /Assertion .* will fail/ then "fail" - when /Assertion .* will succeed/ then "success" - when /Assertion .* is unknown/ then "unknown" - when /invariant confirmed/ then "success" - when /invariant unconfirmed/ then "unknown" - when /invariant refuted/ then "fail" - when /(Upjumping Goto)/ then "goto" + when /\(conf\. \d+\)/ then "race" + when /Deadlock/ then "deadlock" + when /lock (before|after):/ then "deadlock" + when /Assertion .* will fail/ then "fail" + when /Assertion .* will succeed/ then "success" + when /Assertion .* is unknown/ then "unknown" + when /invariant confirmed/ then "success" + when /invariant unconfirmed/ then "unknown" + when /invariant refuted/ then "fail" + when /(Upjumping Goto)/ then "goto" when /(Fundec \w+ is contained in a call graph cycle)/ then "fundec" - when /(Loop analysis)/ then "loop" - when /^\[Warning\]/ then "warn" - when /^\[Error\]/ then "warn" - when /^\[Info\]/ then "warn" - when /^\[Success\]/ then "success" - when /\[Debug\]/ then next # debug "warnings" shouldn't count as other warnings (against NOWARN) - when /^ on line \d+ $/ then next # dead line warnings shouldn't count (used for unreachability with NOWARN) - when /^ on lines \d+..\d+ $/ then next # dead line warnings shouldn't count (used for unreachability with NOWARN) + when /(Loop analysis)/ then "loop" + when /^\[Warning\]/ then "warn" + when /^\[Error\]/ then "warn" + when /^\[Info\]/ then "warn" + when /^\[Success\]/ then "success" + when /\[Debug\]/ then next # debug "warnings" shouldn't count as other warnings (against NOWARN) + when /^ on line \d+ $/ then next # dead line warnings shouldn't count (used for unreachability with NOWARN) + when /^ on lines \d+..\d+ $/ then next # dead line warnings shouldn't count (used for unreachability with NOWARN) else "other" end oldwarn = warnings[i] From 21d405db4814cff6eafbdc059412282530f87377 Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Mon, 13 Nov 2023 11:49:49 +0100 Subject: [PATCH 0973/1312] Change indentatation back. --- scripts/update_suite.rb | 42 ++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/scripts/update_suite.rb b/scripts/update_suite.rb index ab2aeea53f..8817ef599a 100755 --- a/scripts/update_suite.rb +++ b/scripts/update_suite.rb @@ -150,27 +150,27 @@ def collect_warnings obj,i = $1,$2.to_i ranking = ["other", "warn", "goto", "fundec", "loop", "term", "nonterm", "race", "norace", "deadlock", "nodeadlock", "success", "fail", "unknown"] - thiswarn = case obj - when /\(conf\. \d+\)/ then "race" - when /Deadlock/ then "deadlock" - when /lock (before|after):/ then "deadlock" - when /Assertion .* will fail/ then "fail" - when /Assertion .* will succeed/ then "success" - when /Assertion .* is unknown/ then "unknown" - when /invariant confirmed/ then "success" - when /invariant unconfirmed/ then "unknown" - when /invariant refuted/ then "fail" - when /(Upjumping Goto)/ then "goto" - when /(Fundec \w+ is contained in a call graph cycle)/ then "fundec" - when /(Loop analysis)/ then "loop" - when /^\[Warning\]/ then "warn" - when /^\[Error\]/ then "warn" - when /^\[Info\]/ then "warn" - when /^\[Success\]/ then "success" - when /\[Debug\]/ then next # debug "warnings" shouldn't count as other warnings (against NOWARN) - when /^ on line \d+ $/ then next # dead line warnings shouldn't count (used for unreachability with NOWARN) - when /^ on lines \d+..\d+ $/ then next # dead line warnings shouldn't count (used for unreachability with NOWARN) - else "other" + thiswarn = case obj + when /\(conf\. \d+\)/ then "race" + when /Deadlock/ then "deadlock" + when /lock (before|after):/ then "deadlock" + when /Assertion .* will fail/ then "fail" + when /Assertion .* will succeed/ then "success" + when /Assertion .* is unknown/ then "unknown" + when /invariant confirmed/ then "success" + when /invariant unconfirmed/ then "unknown" + when /invariant refuted/ then "fail" + when /(Upjumping Goto)/ then "goto" + when /(Fundec \w+ is contained in a call graph cycle)/ then "fundec" + when /(Loop analysis)/ then "loop" + when /^\[Warning\]/ then "warn" + when /^\[Error\]/ then "warn" + when /^\[Info\]/ then "warn" + when /^\[Success\]/ then "success" + when /\[Debug\]/ then next # debug "warnings" shouldn't count as other warnings (against NOWARN) + when /^ on line \d+ $/ then next # dead line warnings shouldn't count (used for unreachability with NOWARN) + when /^ on lines \d+..\d+ $/ then next # dead line warnings shouldn't count (used for unreachability with NOWARN) + else "other" end oldwarn = warnings[i] if oldwarn.nil? then From bc126fabf558024b61fd7eee6bb5613daaab9818 Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Mon, 13 Nov 2023 11:50:46 +0100 Subject: [PATCH 0974/1312] Fix indentation of line. --- scripts/update_suite.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/update_suite.rb b/scripts/update_suite.rb index 8817ef599a..2722b3ddb5 100755 --- a/scripts/update_suite.rb +++ b/scripts/update_suite.rb @@ -171,7 +171,7 @@ def collect_warnings when /^ on line \d+ $/ then next # dead line warnings shouldn't count (used for unreachability with NOWARN) when /^ on lines \d+..\d+ $/ then next # dead line warnings shouldn't count (used for unreachability with NOWARN) else "other" - end + end oldwarn = warnings[i] if oldwarn.nil? then warnings[i] = thiswarn From 0e0986a974c9ae86f7af188b5d604d7c264fb593 Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Mon, 13 Nov 2023 13:24:04 +0100 Subject: [PATCH 0975/1312] Use Z instead of Cilint --- src/util/terminationPreprocessing.ml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index 9605484af3..4e25f232e3 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -1,4 +1,5 @@ open GoblintCil +(* module Z = Big_int_Z *) module VarToStmt = Map.Make(CilType.Varinfo) (* maps varinfos (= loop counter variable) to the statement of the corresponding loop*) @@ -7,9 +8,9 @@ let counter_typ = TInt (counter_ikind, []) let min_int_exp = (* Currently only tested for IInt type, which is signed *) if Cil.isSigned counter_ikind then - Const(CInt(Cilint.shift_left_cilint Cilint.mone_cilint ((bytesSizeOfInt counter_ikind)*8-1), IInt, None)) + Const(CInt(Z.shift_left Cilint.mone_cilint ((bytesSizeOfInt counter_ikind)*8-1), IInt, None)) else - Const(CInt(Cilint.zero_cilint, counter_ikind, None)) + Const(CInt(Z.zero, counter_ikind, None)) class loopCounterVisitor lc (fd : fundec) = object(self) inherit nopCilVisitor From f18fe581d6d06e674106eaec9c6c26d136aa68eb Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Mon, 13 Nov 2023 13:51:31 +0100 Subject: [PATCH 0976/1312] Change - to _ in name of variable introduced in terminationPreprocessing. - is not a legal part of a variable. --- src/util/terminationPreprocessing.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index 4e25f232e3..919de3e5d9 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -38,7 +38,7 @@ class loopCounterVisitor lc (fd : fundec) = object(self) let action s = match s.skind with | Loop (b, loc, eloc, _, _) -> - let vname = "term" ^ string_of_int loc.line ^ "_" ^ string_of_int loc.column ^ "-id" ^ (string_of_int !vcounter) in + let vname = "term" ^ string_of_int loc.line ^ "_" ^ string_of_int loc.column ^ "_id" ^ (string_of_int !vcounter) in incr vcounter; let v = Cil.makeLocalVar fd vname counter_typ in (*Not tested for incremental mode*) let lval = Lval (Var v, NoOffset) in From aef46ce9c6ea20e5f50b9725abe92dd7f4a0d84e Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Mon, 13 Nov 2023 14:39:08 +0100 Subject: [PATCH 0977/1312] Add LoopTermination and TerminationPreprocessing to goblint_lib --- src/goblint_lib.ml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/goblint_lib.ml b/src/goblint_lib.ml index e1087f8fc9..e06f5fd245 100644 --- a/src/goblint_lib.ml +++ b/src/goblint_lib.ml @@ -124,6 +124,7 @@ module RelationPriv = RelationPriv module ThreadEscape = ThreadEscape module PthreadSignals = PthreadSignals module ExtractPthread = ExtractPthread +module LoopTermination = LoopTermination (** {2 Longjmp} @@ -338,6 +339,7 @@ module Tracing = Tracing module Preprocessor = Preprocessor module CompilationDatabase = CompilationDatabase module MakefileUtil = MakefileUtil +module TerminationPreprocessing = TerminationPreprocessing (** {2 Witnesses} From 72c14e208fab17f89fa325cb704317929bd8e739 Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Mon, 13 Nov 2023 15:33:12 +0100 Subject: [PATCH 0978/1312] Extract function find_loop, given a loop_counter. --- src/analyses/loopTermination.ml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/analyses/loopTermination.ml b/src/analyses/loopTermination.ml index 7cde0cb9c6..5fac2e9e1a 100644 --- a/src/analyses/loopTermination.ml +++ b/src/analyses/loopTermination.ml @@ -31,7 +31,7 @@ struct module D = Lattice.Unit module C = D - module V = struct + module V = struct include UnitV let is_write_only _ = true end @@ -40,6 +40,9 @@ struct let startstate _ = () let exitstate = startstate + let find_loop ~loop_counter = + VarToStmt.find loop_counter !loop_counters + (** Recognizes a call of [__goblint_bounded] to check the EvalInt of the * respective loop counter variable at that position. *) let special ctx (lval : lval option) (f : varinfo) (arglist : exp list) = @@ -47,7 +50,7 @@ struct match f.vname, arglist with "__goblint_bounded", [Lval (Var x, NoOffset)] -> (try - let loop_statement = VarToStmt.find x !loop_counters in + let loop_statement = find_loop ~loop_counter:x in let is_bounded = check_bounded ctx x in ctx.sideg () (G.add (`Lifted loop_statement) is_bounded (ctx.global ())); (* In case the loop is not bounded, a warning is created. *) @@ -70,7 +73,7 @@ struct | None -> false) | Queries.MustTermAllLoops -> let multithreaded = ctx.ask Queries.IsEverMultiThreaded in - if multithreaded then ( + if multithreaded then ( M.warn ~category:Termination "The program might not terminate! (Multithreaded)\n"; false) else From ddfcaea7430c74b08103b243f19ec07305a1d422 Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Mon, 13 Nov 2023 15:34:34 +0100 Subject: [PATCH 0979/1312] Rename x to loop_counter. --- src/analyses/loopTermination.ml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/analyses/loopTermination.ml b/src/analyses/loopTermination.ml index 5fac2e9e1a..10e0f5c5f4 100644 --- a/src/analyses/loopTermination.ml +++ b/src/analyses/loopTermination.ml @@ -48,10 +48,10 @@ struct let special ctx (lval : lval option) (f : varinfo) (arglist : exp list) = if !AnalysisState.postsolving then match f.vname, arglist with - "__goblint_bounded", [Lval (Var x, NoOffset)] -> + "__goblint_bounded", [Lval (Var loop_counter, NoOffset)] -> (try - let loop_statement = find_loop ~loop_counter:x in - let is_bounded = check_bounded ctx x in + let loop_statement = find_loop ~loop_counter in + let is_bounded = check_bounded ctx loop_counter in ctx.sideg () (G.add (`Lifted loop_statement) is_bounded (ctx.global ())); (* In case the loop is not bounded, a warning is created. *) if not (is_bounded) then ( From 72413249dd0cfb5c72d052d4f44110f20f4a2af3 Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Mon, 13 Nov 2023 16:34:17 +0100 Subject: [PATCH 0980/1312] Use increment_expression that takes the ikind of the counter variable into account. --- src/util/terminationPreprocessing.ml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index 919de3e5d9..b6ffc12c14 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -36,6 +36,14 @@ class loopCounterVisitor lc (fd : fundec) = object(self) let f_bounded = Lval (var (specialFunction "__goblint_bounded").svar) in + (* Yields increment expression e + 1 where the added "1" that has the same type as the expression [e]. + Using Cil.increm instead does not work for non-[IInt] ikinds. *) + let increment_expression e = + let et = typeOf e in + let bop = PlusA in + let one = Const (CInt (Cilint.one_cilint, counter_ikind, None)) in + constFold false (BinOp(bop, e, one, et)) in + let action s = match s.skind with | Loop (b, loc, eloc, _, _) -> let vname = "term" ^ string_of_int loc.line ^ "_" ^ string_of_int loc.column ^ "_id" ^ (string_of_int !vcounter) in @@ -43,8 +51,8 @@ class loopCounterVisitor lc (fd : fundec) = object(self) let v = Cil.makeLocalVar fd vname counter_typ in (*Not tested for incremental mode*) let lval = Lval (Var v, NoOffset) in let init_stmt = mkStmtOneInstr @@ Set (var v, min_int_exp, loc, eloc) in - let inc_stmt = mkStmtOneInstr @@ Set (var v, increm lval 1, loc, eloc) in - let inc_stmt2 = mkStmtOneInstr @@ Set (var v, increm lval 1, loc, eloc) in + let inc_stmt = mkStmtOneInstr @@ Set (var v, increment_expression lval, loc, eloc) in + let inc_stmt2 = mkStmtOneInstr @@ Set (var v, increment_expression lval, loc, eloc) in let exit_stmt = mkStmtOneInstr @@ Call (None, f_bounded, [lval], loc, locUnknown) in (match b.bstmts with | s :: ss -> (*duplicate increment statement here to fix inconsistencies in nested loops*) From 76751d4947cc013c5c439e9b79b9733b9676fc19 Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Mon, 13 Nov 2023 17:59:28 +0100 Subject: [PATCH 0981/1312] Termination analysis: Insert only one increment statement, do not set assume no overflow. --- src/maingoblint.ml | 3 +-- src/util/terminationPreprocessing.ml | 8 +------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/maingoblint.ml b/src/maingoblint.ml index c44a6d9360..3d9959ba6c 100644 --- a/src/maingoblint.ml +++ b/src/maingoblint.ml @@ -164,8 +164,7 @@ let check_arguments () = if List.mem "termination" @@ get_string_list "ana.activated" then ( if GobConfig.get_bool "incremental.load" || GobConfig.get_bool "incremental.save" then fail "termination analysis is not compatible with incremental analysis"; set_list "ana.activated" (GobConfig.get_list "ana.activated" @ [`String ("threadflag")]); - set_string "sem.int.signed_overflow" "assume_none"; - warn "termination analysis implicitly activates threadflag analysis and set sem.int.signed_overflow to assume_none" + warn "termination analysis implicitly activates threadflag analysis." ); if not (get_bool "ana.sv-comp.enabled") && get_bool "witness.graphml.enabled" then fail "witness.graphml.enabled: cannot generate GraphML witness without SV-COMP mode (ana.sv-comp.enabled)" diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index b6ffc12c14..eb950b1dbb 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -52,14 +52,8 @@ class loopCounterVisitor lc (fd : fundec) = object(self) let lval = Lval (Var v, NoOffset) in let init_stmt = mkStmtOneInstr @@ Set (var v, min_int_exp, loc, eloc) in let inc_stmt = mkStmtOneInstr @@ Set (var v, increment_expression lval, loc, eloc) in - let inc_stmt2 = mkStmtOneInstr @@ Set (var v, increment_expression lval, loc, eloc) in let exit_stmt = mkStmtOneInstr @@ Call (None, f_bounded, [lval], loc, locUnknown) in - (match b.bstmts with - | s :: ss -> (*duplicate increment statement here to fix inconsistencies in nested loops*) - b.bstmts <- exit_stmt :: inc_stmt :: s :: inc_stmt2 :: ss; - | ss -> - b.bstmts <- exit_stmt :: inc_stmt :: ss; - ); + b.bstmts <- exit_stmt :: inc_stmt :: b.bstmts; lc := VarToStmt.add (v: varinfo) (s: stmt) !lc; let nb = mkBlock [init_stmt; mkStmt s.skind] in s.skind <- Block nb; From 5d5482726f5464605e59a5ac2263830a03358002 Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Mon, 13 Nov 2023 18:01:02 +0100 Subject: [PATCH 0982/1312] Set regresion tests to todo that are now imprecise. --- tests/regression/78-termination/03-nested-loop-terminating.c | 2 +- .../regression/78-termination/07-nested-for-loop-terminating.c | 2 +- .../regression/78-termination/25-leave-loop-goto-terminating.c | 2 +- .../78-termination/30-goto-out-of-inner-loop-terminating.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/regression/78-termination/03-nested-loop-terminating.c b/tests/regression/78-termination/03-nested-loop-terminating.c index fd1ee14f39..6b31204567 100644 --- a/tests/regression/78-termination/03-nested-loop-terminating.c +++ b/tests/regression/78-termination/03-nested-loop-terminating.c @@ -1,4 +1,4 @@ -// SKIP TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/78-termination/07-nested-for-loop-terminating.c b/tests/regression/78-termination/07-nested-for-loop-terminating.c index f1dde17dc5..3293a1fa2c 100644 --- a/tests/regression/78-termination/07-nested-for-loop-terminating.c +++ b/tests/regression/78-termination/07-nested-for-loop-terminating.c @@ -1,4 +1,4 @@ -// SKIP TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/78-termination/25-leave-loop-goto-terminating.c b/tests/regression/78-termination/25-leave-loop-goto-terminating.c index 61c8b8f58d..b882759bff 100644 --- a/tests/regression/78-termination/25-leave-loop-goto-terminating.c +++ b/tests/regression/78-termination/25-leave-loop-goto-terminating.c @@ -1,4 +1,4 @@ -// SKIP TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() diff --git a/tests/regression/78-termination/30-goto-out-of-inner-loop-terminating.c b/tests/regression/78-termination/30-goto-out-of-inner-loop-terminating.c index 999ee6d3fd..c07b558d07 100644 --- a/tests/regression/78-termination/30-goto-out-of-inner-loop-terminating.c +++ b/tests/regression/78-termination/30-goto-out-of-inner-loop-terminating.c @@ -1,4 +1,4 @@ -// SKIP TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// SKIP TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra #include int main() From f81ca2c0346716ada6abfc51f09dbf9143dc34c1 Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Mon, 13 Nov 2023 23:15:40 +0100 Subject: [PATCH 0983/1312] Termination: Use unsigned long long for counter variables; reactivate assume no overflow when termination analysis is activated. --- lib/goblint/runtime/include/goblint.h | 2 +- lib/goblint/runtime/src/goblint.c | 4 ++-- src/maingoblint.ml | 3 ++- src/util/terminationPreprocessing.ml | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/goblint/runtime/include/goblint.h b/lib/goblint/runtime/include/goblint.h index 3c1abae838..af87035d33 100644 --- a/lib/goblint/runtime/include/goblint.h +++ b/lib/goblint/runtime/include/goblint.h @@ -7,4 +7,4 @@ void __goblint_assume_join(/* pthread_t thread */); // undeclared argument to av void __goblint_split_begin(int exp); void __goblint_split_end(int exp); -void __goblint_bounded(int exp); \ No newline at end of file +void __goblint_bounded(unsigned long long exp); \ No newline at end of file diff --git a/lib/goblint/runtime/src/goblint.c b/lib/goblint/runtime/src/goblint.c index 7929fcf37a..cbcb7cf505 100644 --- a/lib/goblint/runtime/src/goblint.c +++ b/lib/goblint/runtime/src/goblint.c @@ -29,6 +29,6 @@ void __goblint_split_end(int exp) { } -void __goblint_bounded(int exp) { - +void __goblint_bounded(unsigned long long exp) { + } \ No newline at end of file diff --git a/src/maingoblint.ml b/src/maingoblint.ml index 3d9959ba6c..82a19aa4ae 100644 --- a/src/maingoblint.ml +++ b/src/maingoblint.ml @@ -164,7 +164,8 @@ let check_arguments () = if List.mem "termination" @@ get_string_list "ana.activated" then ( if GobConfig.get_bool "incremental.load" || GobConfig.get_bool "incremental.save" then fail "termination analysis is not compatible with incremental analysis"; set_list "ana.activated" (GobConfig.get_list "ana.activated" @ [`String ("threadflag")]); - warn "termination analysis implicitly activates threadflag analysis." + set_string "sem.int.signed_overflow" "assume_none"; + warn "termination analysis implicitly activates threadflag analysis and set sem.int.signed_overflow to assume_none"; ); if not (get_bool "ana.sv-comp.enabled") && get_bool "witness.graphml.enabled" then fail "witness.graphml.enabled: cannot generate GraphML witness without SV-COMP mode (ana.sv-comp.enabled)" diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml index eb950b1dbb..9023a68f8a 100644 --- a/src/util/terminationPreprocessing.ml +++ b/src/util/terminationPreprocessing.ml @@ -3,7 +3,7 @@ open GoblintCil module VarToStmt = Map.Make(CilType.Varinfo) (* maps varinfos (= loop counter variable) to the statement of the corresponding loop*) -let counter_ikind = IInt +let counter_ikind = IULongLong let counter_typ = TInt (counter_ikind, []) let min_int_exp = (* Currently only tested for IInt type, which is signed *) From 2728c2a56c87808e367b1b18841bed6a7e5744c3 Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Tue, 14 Nov 2023 10:19:58 +0100 Subject: [PATCH 0984/1312] Termination: Update verdict to TODO for 78/35. Case can no longer be handled when no longer using signed integers and assuming no-overflow. --- .../35-goto-out-of-inner-loop-with-print-terminating.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression/78-termination/35-goto-out-of-inner-loop-with-print-terminating.c b/tests/regression/78-termination/35-goto-out-of-inner-loop-with-print-terminating.c index 0554a0bc25..4c738e1173 100644 --- a/tests/regression/78-termination/35-goto-out-of-inner-loop-with-print-terminating.c +++ b/tests/regression/78-termination/35-goto-out-of-inner-loop-with-print-terminating.c @@ -1,4 +1,4 @@ -// SKIP TERM PARAM: --set "ana.activated[+]" termination --set "ana.activated[+]" apron --enable ana.int.interval --set ana.apron.domain polyhedra --set sem.int.signed_overflow assume_none +// SKIP TODO TERM PARAM: --set "ana.activated[+]" termination --set "ana.activated[+]" apron --enable ana.int.interval --set ana.apron.domain polyhedra --set sem.int.signed_overflow assume_none #include int main() From 8934a2133c009059738063f0851ed483f74d6276 Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Tue, 14 Nov 2023 14:10:00 +0100 Subject: [PATCH 0985/1312] Autotuner: Activate termination analysis in autotuner. --- conf/svcomp23.json | 3 ++- src/autoTune.ml | 17 ++++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/conf/svcomp23.json b/conf/svcomp23.json index af584f1593..f46ae13e86 100644 --- a/conf/svcomp23.json +++ b/conf/svcomp23.json @@ -70,7 +70,8 @@ "congruence", "octagon", "wideningThresholds", - "loopUnrollHeuristic" + "loopUnrollHeuristic", + "termination" ] } }, diff --git a/src/autoTune.ml b/src/autoTune.ml index 51e4ea412e..672d71b7ba 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -243,7 +243,7 @@ let focusOnSpecification (spec: Svcomp.Specification.t) = | NoDataRace -> (*enable all thread analyses*) print_endline @@ "Specification: NoDataRace -> enabling thread analyses \"" ^ (String.concat ", " notNeccessaryThreadAnalyses) ^ "\""; enableAnalyses notNeccessaryThreadAnalyses; - | Termination -> + | Termination -> let terminationAnas = ["termination"; "threadflag"; "apron"] in print_endline @@ "Specification: Termination -> enabling termination analyses \"" ^ (String.concat ", " terminationAnas) ^ "\""; enableAnalyses terminationAnas; @@ -466,6 +466,11 @@ let wideningOption factors file = print_endline "Enabled widening thresholds"; } +let activateTerminationAnalysis () = + enableAnalyses ["termination"; "apron"]; + set_string "sem.int.signed_overflow" "assume_none"; + set_string "ana.apron.domain" "polyhedra"; + set_bool "ana.int.interval_threshold_widening" true let estimateComplexity factors file = let pathsEstimate = factors.loops + factors.controlFlowStatements / 90 in @@ -495,6 +500,8 @@ let chooseFromOptions costTarget options = let isActivated a = get_bool "ana.autotune.enabled" && List.mem a @@ get_string_list "ana.autotune.activated" +let isTerminationTask () = List.mem Svcomp.Specification.Termination (Svcomp.Specification.of_option ()) + let chooseConfig file = let factors = collectFactors visitCilFileSameGlobals file in let fileCompplexity = estimateComplexity factors file in @@ -528,10 +535,14 @@ let chooseConfig file = let options = [] in let options = if isActivated "congruence" then (congruenceOption factors file)::options else options in - let options = if isActivated "octagon" then (apronOctagonOption factors file)::options else options in + + (* Termination analysis uses apron in a different configuration. *) + let options = if isActivated "octagon" && not (isTerminationTask ()) then (apronOctagonOption factors file)::options else options in let options = if isActivated "wideningThresholds" then (wideningOption factors file)::options else options in - List.iter (fun o -> o.activate ()) @@ chooseFromOptions (totalTarget - fileCompplexity) options + List.iter (fun o -> o.activate ()) @@ chooseFromOptions (totalTarget - fileCompplexity) options; + if isActivated "termination" && isTerminationTask () then + activateTerminationAnalysis () let reset_lazy () = ResettableLazy.reset functionCallMaps From e64558da31a28216909b948556d9848ca279f0cf Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Tue, 14 Nov 2023 14:46:20 +0100 Subject: [PATCH 0986/1312] Cache option ana.base.strings.domain, reset the cache in server. --- src/cdomains/stringDomain.ml | 27 ++++++++++++++++++++++----- src/cdomains/stringDomain.mli | 3 +++ src/util/server.ml | 1 + 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/cdomains/stringDomain.ml b/src/cdomains/stringDomain.ml index 925a7fec62..bc4f6d3955 100644 --- a/src/cdomains/stringDomain.ml +++ b/src/cdomains/stringDomain.ml @@ -2,10 +2,27 @@ include Printable.StdLeaf let name () = "string" +type string_domain = Unit | Disjoint | Flat +let string_domain = ref None +let string_domain_config = "ana.base.strings.domain" +let parse config = match config with + | "unit" -> Unit + | "disjoint" -> Disjoint + | "flat" -> Flat + | _ -> raise @@ GobConfig.ConfigError ("Invalid option for " ^ string_domain_config) + +let get_string_domain () = + if !string_domain = None then + string_domain := Some (parse (GobConfig.get_string string_domain_config)); + Option.get !string_domain + +let reset_lazy () = + string_domain := None + type t = string option [@@deriving eq, ord, hash] let hash x = - if GobConfig.get_string "ana.base.strings.domain" = "disjoint" then + if get_string_domain () = Disjoint then hash x else 13859 @@ -22,7 +39,7 @@ include Printable.SimpleShow ( ) let of_string x = - if GobConfig.get_string "ana.base.strings.domain" = "unit" then + if get_string_domain () = Unit then None else Some x @@ -74,7 +91,7 @@ let join x y = | _, None -> None | Some a, Some b when a = b -> Some a | Some a, Some b (* when a <> b *) -> - if GobConfig.get_string "ana.base.strings.domain" = "disjoint" then + if get_string_domain () = Disjoint then raise Lattice.Uncomparable else None @@ -85,13 +102,13 @@ let meet x y = | a, None -> a | Some a, Some b when a = b -> Some a | Some a, Some b (* when a <> b *) -> - if GobConfig.get_string "ana.base.strings.domain" = "disjoint" then + if get_string_domain () = Disjoint then raise Lattice.Uncomparable else raise Lattice.BotValue let repr x = - if GobConfig.get_string "ana.base.strings.domain" = "disjoint" then + if get_string_domain () = Disjoint then x (* everything else is kept separate, including strings if not limited *) else None (* all strings together if limited *) diff --git a/src/cdomains/stringDomain.mli b/src/cdomains/stringDomain.mli index 3541dac6e7..66423caa0b 100644 --- a/src/cdomains/stringDomain.mli +++ b/src/cdomains/stringDomain.mli @@ -2,6 +2,9 @@ include Printable.S +val reset_lazy: unit -> unit +(** Reset the cached configuration of the string domain. *) + val of_string: string -> t (** Convert from string. *) diff --git a/src/util/server.ml b/src/util/server.ml index 22f5a03350..829ee92ee8 100644 --- a/src/util/server.ml +++ b/src/util/server.ml @@ -280,6 +280,7 @@ let analyze ?(reset=false) (s: t) = InvariantCil.reset_lazy (); WideningThresholds.reset_lazy (); IntDomain.reset_lazy (); + StringDomain.reset_lazy (); PrecisionUtil.reset_lazy (); ApronDomain.reset_lazy (); AutoTune.reset_lazy (); From a2306181263a9c04f35fdb2fbc7874af2e3b1578 Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Tue, 14 Nov 2023 14:47:50 +0100 Subject: [PATCH 0987/1312] Add newlines between functions. --- src/cdomains/stringDomain.ml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/cdomains/stringDomain.ml b/src/cdomains/stringDomain.ml index bc4f6d3955..978482a503 100644 --- a/src/cdomains/stringDomain.ml +++ b/src/cdomains/stringDomain.ml @@ -3,8 +3,11 @@ include Printable.StdLeaf let name () = "string" type string_domain = Unit | Disjoint | Flat + let string_domain = ref None + let string_domain_config = "ana.base.strings.domain" + let parse config = match config with | "unit" -> Unit | "disjoint" -> Disjoint From d5662c5455290e166d5023724d843f0b3a92ef07 Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Wed, 15 Nov 2023 13:13:12 +0100 Subject: [PATCH 0988/1312] Indicate that termination analysis is activated. --- src/autoTune.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/autoTune.ml b/src/autoTune.ml index 672d71b7ba..c1e982aace 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -467,6 +467,7 @@ let wideningOption factors file = } let activateTerminationAnalysis () = + print_endline "Enabling termination analysis"; enableAnalyses ["termination"; "apron"]; set_string "sem.int.signed_overflow" "assume_none"; set_string "ana.apron.domain" "polyhedra"; From 245b438a5c9e313d78391fae6be93579b8b4a5bb Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Wed, 15 Nov 2023 15:19:20 +0100 Subject: [PATCH 0989/1312] Change wording from enabling to enabled for consistency. --- src/autoTune.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/autoTune.ml b/src/autoTune.ml index c1e982aace..645fcfcf84 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -467,11 +467,11 @@ let wideningOption factors file = } let activateTerminationAnalysis () = - print_endline "Enabling termination analysis"; enableAnalyses ["termination"; "apron"]; set_string "sem.int.signed_overflow" "assume_none"; set_string "ana.apron.domain" "polyhedra"; - set_bool "ana.int.interval_threshold_widening" true + set_bool "ana.int.interval_threshold_widening" true; + print_endline "Enabled termination analysis" let estimateComplexity factors file = let pathsEstimate = factors.loops + factors.controlFlowStatements / 90 in From cbe4ad0cb57f57b19e6f009c30c033cc9f5b0eb8 Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Wed, 15 Nov 2023 15:21:39 +0100 Subject: [PATCH 0990/1312] Add termination to autotune for svcomp.json, but remove it from svcomp23.json. --- conf/svcomp.json | 3 ++- conf/svcomp23.json | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/conf/svcomp.json b/conf/svcomp.json index 73f99500b9..107c59994c 100644 --- a/conf/svcomp.json +++ b/conf/svcomp.json @@ -71,7 +71,8 @@ "octagon", "wideningThresholds", "loopUnrollHeuristic", - "memsafetySpecification" + "memsafetySpecification", + "termination" ] } }, diff --git a/conf/svcomp23.json b/conf/svcomp23.json index f46ae13e86..af584f1593 100644 --- a/conf/svcomp23.json +++ b/conf/svcomp23.json @@ -70,8 +70,7 @@ "congruence", "octagon", "wideningThresholds", - "loopUnrollHeuristic", - "termination" + "loopUnrollHeuristic" ] } }, From a466650aa752cd83655dded84cd56c52e8b1d0a0 Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Wed, 15 Nov 2023 15:26:44 +0100 Subject: [PATCH 0991/1312] Move LoopTermination in Goblint_lib to analyses/other section. --- src/goblint_lib.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/goblint_lib.ml b/src/goblint_lib.ml index e06f5fd245..cb18ad0dd7 100644 --- a/src/goblint_lib.ml +++ b/src/goblint_lib.ml @@ -124,7 +124,6 @@ module RelationPriv = RelationPriv module ThreadEscape = ThreadEscape module PthreadSignals = PthreadSignals module ExtractPthread = ExtractPthread -module LoopTermination = LoopTermination (** {2 Longjmp} @@ -149,6 +148,7 @@ module UnitAnalysis = UnitAnalysis module Assert = Assert module FileUse = FileUse +module LoopTermination = LoopTermination module Uninit = Uninit module Expsplit = Expsplit module StackTrace = StackTrace From b57b9e4589dccfba5810f841728355a4f72c205b Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Wed, 15 Nov 2023 15:54:14 +0100 Subject: [PATCH 0992/1312] Extract some loops into functions; this reduces runtime of analysis. --- .../15-complex-loop-combination-terminating.c | 84 ++++++++++--------- 1 file changed, 46 insertions(+), 38 deletions(-) diff --git a/tests/regression/78-termination/15-complex-loop-combination-terminating.c b/tests/regression/78-termination/15-complex-loop-combination-terminating.c index 1365611410..9b97d4051b 100644 --- a/tests/regression/78-termination/15-complex-loop-combination-terminating.c +++ b/tests/regression/78-termination/15-complex-loop-combination-terminating.c @@ -2,6 +2,49 @@ // Apron is not precise enough for some nested loops #include +int nested_while_loop_with_break(){ + int m; + + // Nested while loop with a break statement + int n = 1; + while (n <= 5) + { + printf("Outer While loop iteration: %d\n", n); + m = 1; + while (1) + { + printf("Inner While loop iteration: %d\n", m); + m++; + if (m == 4) + { + break; + } + } + n++; + } + return 0; +} + +int nested_loop_with_conditions(){ + // Loop with nested conditions + for (int v = 1; v <= 10; v++) + { + printf("Loop with Nested Conditions: %d - ", v); + if (v < 5) + { + printf("Less than 5\n"); + } + else if (v > 5) + { + printf("Greater than 5\n"); + } + else + { + printf("Equal to 5\n"); + } + } +} + int main() { // Non-nested loops @@ -67,44 +110,9 @@ int main() p++; } while (p <= 5); - // Additional loops - int m; - - // Nested while loop with a break statement - int n = 1; - while (n <= 5) - { - printf("Outer While loop iteration: %d\n", n); - m = 1; - while (1) - { - printf("Inner While loop iteration: %d\n", m); - m++; - if (m == 4) - { - break; - } - } - n++; - } - - // Loop with nested conditions - for (int v = 1; v <= 10; v++) - { - printf("Loop with Nested Conditions: %d - ", v); - if (v < 5) - { - printf("Less than 5\n"); - } - else if (v > 5) - { - printf("Greater than 5\n"); - } - else - { - printf("Equal to 5\n"); - } - } + // Additional nested loops + nested_while_loop_with_break(); + nested_loop_with_conditions(); return 0; } From 70ef2d293a647b8ddcd477789054e87e18eeff4f Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Wed, 15 Nov 2023 18:11:08 +0100 Subject: [PATCH 0993/1312] RelationalAnalysis: Assert type-bounds also for signed types when no-overflow is assumed. --- src/analyses/apron/relationAnalysis.apron.ml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index 13f549fc44..8ac55ca323 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -196,7 +196,7 @@ struct let assert_type_bounds ask rel x = assert (RD.Tracked.varinfo_tracked x); match Cilfacade.get_ikind x.vtype with - | ik when not (IntDomain.should_ignore_overflow ik) -> (* don't add type bounds for signed when assume_none *) + | ik -> (* don't add type bounds for signed when assume_none *) let (type_min, type_max) = IntDomain.Size.range ik in (* TODO: don't go through CIL exp? *) let e1 = BinOp (Le, Lval (Cil.var x), (Cil.kintegerCilint ik type_max), intType) in @@ -204,7 +204,6 @@ struct let rel = RD.assert_inv rel e1 false (no_overflow ask e1) in (* TODO: how can be overflow when asserting type bounds? *) let rel = RD.assert_inv rel e2 false (no_overflow ask e2) in rel - | _ | exception Invalid_argument _ -> rel From 6decbddb2503a7633c73b3151df40e158417ede8 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 15 Nov 2023 23:20:11 +0100 Subject: [PATCH 0994/1312] Improve the detection of reachable memory through global struct vars --- src/analyses/memLeak.ml | 89 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 88 insertions(+), 1 deletion(-) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index fc015f458b..4d7e864c06 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -23,6 +23,14 @@ struct (* Filtering by GVar seems to account for declarations, as well as definitions of global vars *) List.filter_map (function GVar (v, _, _) -> Some v | _ -> None) !Cilfacade.current_file.globals + let get_global_struct_ptr_vars () = + List.filter_map (function GVar (v, _, _) -> Some v | _ -> None) !Cilfacade.current_file.globals + |> List.filter (fun v -> match v.vtype with TPtr (TComp _, _) | TPtr ((TNamed ({ttype = TComp _; _}, _)), _) -> true | _ -> false) + + let get_global_struct_non_ptr_vars () = + List.filter_map (function GVar (v, _, _) -> Some v | _ -> None) !Cilfacade.current_file.globals + |> List.filter (fun v -> match v.vtype with TComp (_, _) | (TNamed ({ttype = TComp _; _}, _)) -> true | _ -> false) + let get_reachable_mem_from_globals (global_vars:varinfo list) ctx = global_vars |> List.map (fun v -> Lval (Var v, NoOffset)) @@ -35,6 +43,81 @@ struct end | _ -> None) + let rec get_reachable_mem_from_str_ptr_globals (global_struct_ptr_vars:varinfo list) ctx = + let eval_value_of_heap_var heap_var = + match ctx.ask (Queries.EvalValue (Lval (Var heap_var, NoOffset))) with + | a when not (Queries.VD.is_top a) -> + begin match a with + | Struct s -> + List.fold_left (fun acc f -> + if isPointerType f.ftype then + begin match ValueDomain.Structs.get s f with + | Queries.VD.Address a -> + let reachable_from_addr_set = + List.fold_left (fun acc addr -> + match addr with + | Queries.AD.Addr.Addr (v, _) -> List.append acc (v :: get_reachable_mem_from_str_ptr_globals [v] ctx) + | _ -> acc + ) [] (Queries.AD.elements a) + in List.append acc reachable_from_addr_set + | _ -> acc + end + else acc + ) [] (ValueDomain.Structs.keys s) + | _ -> [] + end + | _ -> [] + in + let get_pts_of_non_heap_ptr_var var = + match ctx.ask (Queries.MayPointTo (Lval (Var var, NoOffset))) with + | a when not (Queries.AD.is_top a) && Queries.AD.cardinal a = 1 -> + begin match List.hd @@ Queries.AD.elements a with + | Queries.AD.Addr.Addr (v, _) when (ctx.ask (Queries.IsHeapVar v)) && not (ctx.ask (Queries.IsMultiple v)) -> v :: (eval_value_of_heap_var v) + | Queries.AD.Addr.Addr (v, _) when not (ctx.ask (Queries.IsAllocVar v)) && isPointerType v.vtype -> get_reachable_mem_from_str_ptr_globals [v] ctx + | _ -> [] + end + | _ -> [] + in + global_struct_ptr_vars + |> List.fold_left (fun acc var -> + if ctx.ask (Queries.IsHeapVar var) then eval_value_of_heap_var var + else if not (ctx.ask (Queries.IsAllocVar var)) && isPointerType var.vtype then get_pts_of_non_heap_ptr_var var + else acc + ) [] + + let get_reachable_mem_from_str_non_ptr_globals (global_struct_non_ptr_vars:varinfo list) ctx = + global_struct_non_ptr_vars + (* Filter out global struct vars that don't have pointer fields *) + |> List.filter_map (fun v -> + match ctx.ask (Queries.EvalValue (Lval (Var v, NoOffset))) with + | a when not (Queries.VD.is_top a) -> + begin match a with + | Queries.VD.Struct s -> + let struct_fields = ValueDomain.Structs.keys s in + let ptr_struct_fields = List.filter (fun f -> isPointerType f.ftype) struct_fields in + if List.length ptr_struct_fields = 0 then None else Some (s, ptr_struct_fields) + | _ -> None + end + | _ -> None + ) + |> List.fold_left (fun acc_struct (s, fields) -> + let reachable_from_fields = + List.fold_left (fun acc_field field -> + match ValueDomain.Structs.get s field with + | Queries.VD.Address a -> + let reachable_from_addr_set = + List.fold_left (fun acc_addr addr -> + match addr with + | Queries.AD.Addr.Addr (v, _) -> List.append acc_addr (v :: get_reachable_mem_from_str_ptr_globals [v] ctx) + | _ -> acc_addr + ) [] (Queries.AD.elements a) + in List.append acc_field reachable_from_addr_set + | _ -> acc_field + ) [] fields + in + List.append acc_struct reachable_from_fields + ) [] + let warn_for_multi_threaded ctx = if not (ctx.ask (Queries.MustBeSingleThreaded { since_start = true })) then ( set_mem_safety_flag InvalidMemTrack; @@ -45,7 +128,11 @@ struct let check_for_mem_leak ?(assert_exp_imprecise = false) ?(exp = None) ctx = let allocated_mem = ctx.local in if not (D.is_empty allocated_mem) then - let reachable_mem = D.of_list (get_reachable_mem_from_globals (get_global_vars ()) ctx) in + let reachable_mem_from_non_struct_globals = D.of_list (get_reachable_mem_from_globals (get_global_vars ()) ctx) in + let reachable_mem_from_struct_ptr_globals = D.of_list (get_reachable_mem_from_str_ptr_globals (get_global_struct_ptr_vars ()) ctx) in + let reachable_mem_from_struct_non_ptr_globals = D.of_list (get_reachable_mem_from_str_non_ptr_globals (get_global_struct_non_ptr_vars ()) ctx) in + let reachable_mem_from_struct_globals = D.join reachable_mem_from_struct_ptr_globals reachable_mem_from_struct_non_ptr_globals in + let reachable_mem = D.join reachable_mem_from_non_struct_globals reachable_mem_from_struct_globals in (* Check and warn if there's unreachable allocated memory at program exit *) let allocated_and_unreachable_mem = D.diff allocated_mem reachable_mem in if not (D.is_empty allocated_and_unreachable_mem) then ( From c3804260bf8bc43ea90e76ea9f2cf0f7ebb7186f Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 15 Nov 2023 23:20:53 +0100 Subject: [PATCH 0995/1312] Add regr. tests for reachable memory through global struct vars --- .../13-global-nested-struct-ptr-reachable.c | 29 +++++++++++++++++++ ...4-global-nested-struct-non-ptr-reachable.c | 25 ++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 tests/regression/76-memleak/13-global-nested-struct-ptr-reachable.c create mode 100644 tests/regression/76-memleak/14-global-nested-struct-non-ptr-reachable.c diff --git a/tests/regression/76-memleak/13-global-nested-struct-ptr-reachable.c b/tests/regression/76-memleak/13-global-nested-struct-ptr-reachable.c new file mode 100644 index 0000000000..1726625a59 --- /dev/null +++ b/tests/regression/76-memleak/13-global-nested-struct-ptr-reachable.c @@ -0,0 +1,29 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak +#include + +typedef struct st { + int *a; + int b; +} st; + +typedef struct st2 { + st *st_ptr; +} st2; + +st2 *st_var; + +int main(int argc, char const *argv[]) { + st_var = malloc(sizeof(st2)); + st_var->st_ptr = malloc(sizeof(st)); + int *local_ptr = malloc(sizeof(int)); + st_var->st_ptr->a = local_ptr; + local_ptr = NULL; + + free(st_var->st_ptr); + free(st_var); + + // local_ptr's memory is reachable through st_var->st_ptr->a + // It's leaked, because we don't call free() on it + // Hence, there should be a single warning for a memory leak, but not for unreachable memory + return 0; //WARN +} diff --git a/tests/regression/76-memleak/14-global-nested-struct-non-ptr-reachable.c b/tests/regression/76-memleak/14-global-nested-struct-non-ptr-reachable.c new file mode 100644 index 0000000000..1153bd81e0 --- /dev/null +++ b/tests/regression/76-memleak/14-global-nested-struct-non-ptr-reachable.c @@ -0,0 +1,25 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak +#include + +typedef struct st { + int *a; + int b; +} st; + +typedef struct st2 { + st *st_ptr; +} st2; + +st2 st_var; + +int main(int argc, char const *argv[]) { + st_var.st_ptr = malloc(sizeof(st)); + int *local_ptr = malloc(sizeof(int)); + st_var.st_ptr->a = local_ptr; + local_ptr = NULL; + free(st_var.st_ptr); + + // local_ptr's memory is reachable through st_var.st_ptr->a, but it's not freed + // Hence, there should be only a single warning for a memory leak, but not for unreachable memory + return 0; //WARN +} From 845910410bbda2f9964ccd727d63a6e7b811ac7f Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 15 Nov 2023 23:33:58 +0100 Subject: [PATCH 0996/1312] Fix semgrep warning for using `List.length` for an emptiness check --- src/analyses/memLeak.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index a5d357d9c8..51a5a2ff75 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -96,7 +96,7 @@ struct | Queries.VD.Struct s -> let struct_fields = ValueDomain.Structs.keys s in let ptr_struct_fields = List.filter (fun f -> isPointerType f.ftype) struct_fields in - if List.length ptr_struct_fields = 0 then None else Some (s, ptr_struct_fields) + if ptr_struct_fields = [] then None else Some (s, ptr_struct_fields) | _ -> None end | _ -> None From f209afdae5be755eeded7bb0080473fe15571b7d Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 16 Nov 2023 07:52:56 +0100 Subject: [PATCH 0997/1312] First attempt to improve precision for multi-threaded valid-memcleanup --- src/analyses/memLeak.ml | 72 +++++++++++++++++++++++++++++++++-------- 1 file changed, 59 insertions(+), 13 deletions(-) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index 9c09c05cf6..c64bb95697 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -6,30 +6,50 @@ open MessageCategory open AnalysisStateUtil module ToppedVarInfoSet = SetDomain.ToppedSet(CilType.Varinfo)(struct let topname = "All Heap Variables" end) - +module WasMallocCalled = BoolDomain.MustBool module Spec : Analyses.MCPSpec = struct include Analyses.IdentitySpec let name () = "memLeak" - module D = ToppedVarInfoSet + (* module D = ToppedVarInfoSet *) + module D = Lattice.Prod(ToppedVarInfoSet)(WasMallocCalled) module C = D module P = IdentityP (D) + module G = ToppedVarInfoSet + module V = + struct + include ThreadIdDomain.Thread + include StdV + end let context _ d = d (* HELPER FUNCTIONS *) - let warn_for_multi_threaded ctx = - if not (ctx.ask (Queries.MustBeSingleThreaded { since_start = true })) then ( + let warn_for_multi_threaded_due_to_abort ctx = + let state = ctx.local in + if not (ctx.ask (Queries.MustBeSingleThreaded { since_start = true })) && snd state then ( set_mem_safety_flag InvalidMemTrack; set_mem_safety_flag InvalidMemcleanup; - M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Program isn't running in single-threaded mode. A memory leak might occur due to multi-threading" + M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Program aborted while running in multi-threaded mode. A memory leak might occur" + ) + + (* If [is_return] is set to [true], then a thread return occurred, else a thread join *) + let warn_for_thread_return_or_exit current_thread ctx is_return = + let global_state = ctx.global current_thread in + if not (G.is_empty global_state) then ( + set_mem_safety_flag InvalidMemcleanup; + M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory may be leaked at thread %s" (if is_return then "return" else "join") ) + (* if not (ToppedVarInfoSet.is_empty (fst state)) && snd state then ( + set_mem_safety_flag InvalidMemcleanup; + M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory may be leaked at thread %s" (if is_return then "return" else "join") + ) *) let check_for_mem_leak ?(assert_exp_imprecise = false) ?(exp = None) ctx = let state = ctx.local in - if not @@ D.is_empty state then + if not (ToppedVarInfoSet.is_empty (fst state)) then match assert_exp_imprecise, exp with | true, Some exp -> set_mem_safety_flag InvalidMemTrack; @@ -42,6 +62,12 @@ struct (* TRANSFER FUNCTIONS *) let return ctx (exp:exp option) (f:fundec) : D.t = + (* Check for a valid-memcleanup violation in a multi-threaded setting *) + if (ctx.ask (Queries.MayBeThreadReturn)) then ( + match ctx.ask (Queries.CurrentThreadId) with + | `Lifted tid -> warn_for_thread_return_or_exit tid ctx true + | _ -> () + ); (* Returning from "main" is one possible program exit => need to check for memory leaks *) if f.svar.vname = "main" then check_for_mem_leak ctx; ctx.local @@ -53,25 +79,39 @@ struct | Malloc _ | Calloc _ | Realloc _ -> - (* Warn about multi-threaded programs as soon as we encounter a dynamic memory allocation function *) - warn_for_multi_threaded ctx; begin match ctx.ask (Queries.AllocVar {on_stack = false}) with - | `Lifted var -> D.add var state - | _ -> state + | `Lifted var -> + begin match ctx.ask (Queries.CurrentThreadId) with + | `Lifted tid -> + let current_globals = ctx.global tid in + let globals_to_side_effect = G.add var current_globals in + ctx.sideg tid globals_to_side_effect; + | _ -> () + end; + (ToppedVarInfoSet.add var (fst state), true) + | _ -> (fst state, true) end | Free ptr -> begin match ctx.ask (Queries.MayPointTo ptr) with | ad when not (Queries.AD.is_top ad) && Queries.AD.cardinal ad = 1 -> (* Note: Need to always set "ana.malloc.unique_address_count" to a value > 0 *) begin match Queries.AD.choose ad with - | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsAllocVar v) && ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsMultiple v) -> D.remove v state (* Unique pointed to heap vars *) + | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsAllocVar v) && ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsMultiple v) -> + begin match ctx.ask (Queries.CurrentThreadId) with + | `Lifted tid -> + let current_globals = ctx.global tid in + let globals_to_side_effect = G.remove v current_globals in + ctx.sideg tid globals_to_side_effect + | _ -> () + end; + (ToppedVarInfoSet.remove v (fst state), snd state) (* Unique pointed to heap vars *) | _ -> state end | _ -> state end | Abort -> - (* An "Abort" special function indicates program exit => need to check for memory leaks *) - check_for_mem_leak ctx; + (* Upon a call to the "Abort" special function, we give up and conservatively warn *) + warn_for_multi_threaded_due_to_abort ctx; state | Assert { exp; _ } -> let warn_for_assert_exp = @@ -89,6 +129,12 @@ struct in warn_for_assert_exp; state + | ThreadExit _ -> + begin match ctx.ask (Queries.CurrentThreadId) with + | `Lifted tid -> warn_for_thread_return_or_exit tid ctx false + | _ -> () + end; + state | _ -> state let startstate v = D.bot () From c0fe89e93352f530317cf1a7836684de65b216f3 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 16 Nov 2023 07:53:21 +0100 Subject: [PATCH 0998/1312] Add regr. test cases for multi-threaded valid-memcleanup --- .../08-invalid-memcleanup-multi-threaded.c | 33 +++++++++++++++++++ ...-invalid-memcleanup-multi-threaded-abort.c | 33 +++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 tests/regression/76-memleak/08-invalid-memcleanup-multi-threaded.c create mode 100644 tests/regression/76-memleak/09-invalid-memcleanup-multi-threaded-abort.c diff --git a/tests/regression/76-memleak/08-invalid-memcleanup-multi-threaded.c b/tests/regression/76-memleak/08-invalid-memcleanup-multi-threaded.c new file mode 100644 index 0000000000..50b17fa65d --- /dev/null +++ b/tests/regression/76-memleak/08-invalid-memcleanup-multi-threaded.c @@ -0,0 +1,33 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak +#include +#include + +int *g; +int *m1; +int *m2; + +void *f1(void *arg) { + m1 = malloc(sizeof(int)); + // Thread t1 leaks m1 here + pthread_exit(NULL); //WARN +} + +void *f2(void *arg) { + m2 = malloc(sizeof(int)); + free(m2); // No leak for thread t2, since it calls free before exiting + pthread_exit(NULL); //NOWARN +} + +int main(int argc, char const *argv[]) { + g = malloc(sizeof(int)); + pthread_t t1; + pthread_create(&t1, NULL, f1, NULL); + + pthread_t t2; + pthread_create(&t2, NULL, f2, NULL); + + free(g); + + // main thread is not leaking anything + return 0; //NOWARN +} diff --git a/tests/regression/76-memleak/09-invalid-memcleanup-multi-threaded-abort.c b/tests/regression/76-memleak/09-invalid-memcleanup-multi-threaded-abort.c new file mode 100644 index 0000000000..9aef45198e --- /dev/null +++ b/tests/regression/76-memleak/09-invalid-memcleanup-multi-threaded-abort.c @@ -0,0 +1,33 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak +#include +#include + +int *g; +int *m1; +int *m2; + +void *f1(void *arg) { + m1 = malloc(sizeof(int)); + // Thread t1 leaks m1 here + exit(2); //WARN +} + +void *f2(void *arg) { + m2 = malloc(sizeof(int)); + free(m2); // No leak for thread t2, since it calls free before exiting + pthread_exit(NULL); //NOWARN +} + +int main(int argc, char const *argv[]) { + g = malloc(sizeof(int)); + pthread_t t1; + pthread_create(&t1, NULL, f1, NULL); + + pthread_t t2; + pthread_create(&t2, NULL, f2, NULL); + + free(g); + + // main thread is not leaking anything + return 0; //NOWARN +} From 9420a089fcc1134f289d61f5eb8e45571ca47d23 Mon Sep 17 00:00:00 2001 From: stilscher <66023521+stilscher@users.noreply.github.com> Date: Thu, 16 Nov 2023 16:13:29 +0100 Subject: [PATCH 0999/1312] add sqrt handling in float domain --- src/analyses/base.ml | 1 + src/analyses/libraryDesc.ml | 4 +++- src/analyses/libraryFunctions.ml | 6 +++--- src/cdomains/floatDomain.ml | 14 ++++++++++++++ src/cdomains/floatDomain.mli | 3 +++ src/cdomains/floatOps/floatOps.ml | 3 +++ src/cdomains/floatOps/floatOps.mli | 1 + src/cdomains/floatOps/stubs.c | 14 ++++++++++++++ 8 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index a8ad9af95b..86951ab4a5 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2356,6 +2356,7 @@ struct | Isunordered (x,y) -> Int(ID.cast_to IInt (apply_binary FDouble FD.unordered x y)) | Fmax (fd, x ,y) -> Float (apply_binary fd FD.fmax x y) | Fmin (fd, x ,y) -> Float (apply_binary fd FD.fmin x y) + | Sqrt (fk, x) -> Float (apply_unary fk FD.sqrt x) end in begin match lv with diff --git a/src/analyses/libraryDesc.ml b/src/analyses/libraryDesc.ml index 72a4261cb5..5403612fae 100644 --- a/src/analyses/libraryDesc.ml +++ b/src/analyses/libraryDesc.ml @@ -38,7 +38,8 @@ type math = | Atan2 of (CilType.Fkind.t * Basetype.CilExp.t * Basetype.CilExp.t) | Cos of (CilType.Fkind.t * Basetype.CilExp.t) | Sin of (CilType.Fkind.t * Basetype.CilExp.t) - | Tan of (CilType.Fkind.t * Basetype.CilExp.t) [@@deriving eq, ord, hash] + | Tan of (CilType.Fkind.t * Basetype.CilExp.t) + | Sqrt of (CilType.Fkind.t * Basetype.CilExp.t) [@@deriving eq, ord, hash] (** Type of special function, or {!Unknown}. *) (* Use inline record if not single {!Cil.exp} argument. *) @@ -170,6 +171,7 @@ module MathPrintable = struct | Cos (fk, exp) -> Pretty.dprintf "(%a )cos(%a)" d_fkind fk d_exp exp | Sin (fk, exp) -> Pretty.dprintf "(%a )sin(%a)" d_fkind fk d_exp exp | Tan (fk, exp) -> Pretty.dprintf "(%a )tan(%a)" d_fkind fk d_exp exp + | Sqrt (fk, exp) -> Pretty.dprintf "(%a )sqrt(%a)" d_fkind fk d_exp exp include Printable.SimplePretty ( struct diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 117dcbd236..8f8a43cb29 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -944,9 +944,9 @@ let math_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("scalbln", unknown [drop "arg" []; drop "exp" []]); ("scalblnf", unknown [drop "arg" []; drop "exp" []]); ("scalblnl", unknown [drop "arg" []; drop "exp" []]); - ("sqrt", unknown [drop "x" []]); - ("sqrtf", unknown [drop "x" []]); - ("sqrtl", unknown [drop "x" []]); + ("sqrt", special [__ "x" []] @@ fun x -> Math { fun_args = (Sqrt (FDouble, x)) }); + ("sqrtf", special [__ "x" []] @@ fun x -> Math { fun_args = (Sqrt (FFloat, x)) }); + ("sqrtl", special [__ "x" []] @@ fun x -> Math { fun_args = (Sqrt (FLongDouble, x)) }); ("tgamma", unknown [drop "x" []]); ("tgammaf", unknown [drop "x" []]); ("tgammal", unknown [drop "x" []]); diff --git a/src/cdomains/floatDomain.ml b/src/cdomains/floatDomain.ml index f52c849111..39d3744401 100644 --- a/src/cdomains/floatDomain.ml +++ b/src/cdomains/floatDomain.ml @@ -40,6 +40,8 @@ module type FloatArith = sig (** sin(x) *) val tan : t -> t (** tan(x) *) + val sqrt : t -> t + (** sqrt(x) *) (** {inversions of unary functions}*) val inv_ceil : ?asPreciseAsConcrete:bool -> t -> t @@ -670,6 +672,14 @@ module FloatIntervalImpl(Float_t : CFloatType) = struct | (l, h) when l = h && l = Float_t.zero -> of_const 0. (*tan(0) = 0*) | _ -> top () (**could be exact for intervals where l=h, or even for some intervals *) + let eval_sqrt = function + | (l, h) when l = Float_t.zero && h = Float_t.zero -> of_const 0. + | (l, h) when l >= Float_t.zero -> + let low = Float_t.sqrt Down l in + let high = Float_t.sqrt Up h in + Interval (low, high) + | _ -> top () + let eval_inv_ceil ?(asPreciseAsConcrete=false) = function | (l, h) -> if (Float_t.sub Up (Float_t.ceil l) (Float_t.sub Down (Float_t.ceil l) (Float_t.of_float Nearest 1.0)) = (Float_t.of_float Nearest 1.0)) then ( @@ -784,6 +794,7 @@ module FloatIntervalImpl(Float_t : CFloatType) = struct let cos = eval_unop eval_cos let sin = eval_unop eval_sin let tan = eval_unop eval_tan + let sqrt = eval_unop eval_sqrt let inv_ceil ?(asPreciseAsConcrete=false) = eval_unop ~warn:false (eval_inv_ceil ~asPreciseAsConcrete:asPreciseAsConcrete) let inv_floor ?(asPreciseAsConcrete=false) = eval_unop ~warn:false (eval_inv_floor ~asPreciseAsConcrete:asPreciseAsConcrete) @@ -899,6 +910,7 @@ module FloatIntervalImplLifted = struct let cos = lift (F1.cos, F2.cos) let sin = lift (F1.sin, F2.sin) let tan = lift (F1.tan, F2.tan) + let sqrt = lift (F1.sqrt, F2.sqrt) let inv_ceil ?(asPreciseAsConcrete=BoolDomain.MustBool.top ()) = function | F32 a -> F32 (F1.inv_ceil ~asPreciseAsConcrete:true a) @@ -1159,6 +1171,8 @@ module FloatDomTupleImpl = struct map { f1= (fun (type a) (module F : FloatDomain with type t = a) -> F.sin); } let tan = map { f1= (fun (type a) (module F : FloatDomain with type t = a) -> F.tan); } + let sqrt = + map { f1= (fun (type a) (module F : FloatDomain with type t = a) -> F.sqrt); } (*"asPreciseAsConcrete" has no meaning here*) let inv_ceil ?(asPreciseAsConcrete=BoolDomain.MustBool.top ()) = diff --git a/src/cdomains/floatDomain.mli b/src/cdomains/floatDomain.mli index 06bca69aca..d958e1ee81 100644 --- a/src/cdomains/floatDomain.mli +++ b/src/cdomains/floatDomain.mli @@ -57,6 +57,9 @@ module type FloatArith = sig val tan : t -> t (** tan(x) *) + val sqrt : t -> t + (** sqrt(x) *) + (** {inversions of unary functions}*) val inv_ceil : ?asPreciseAsConcrete:bool -> t -> t (** (inv_ceil z -> x) if (z = ceil(x)) *) diff --git a/src/cdomains/floatOps/floatOps.ml b/src/cdomains/floatOps/floatOps.ml index a951ec08fe..a4e39d930e 100644 --- a/src/cdomains/floatOps/floatOps.ml +++ b/src/cdomains/floatOps/floatOps.ml @@ -35,6 +35,7 @@ module type CFloatType = sig val sub: round_mode -> t -> t -> t val mul: round_mode -> t -> t -> t val div: round_mode -> t -> t -> t + val sqrt: round_mode -> t -> t val atof: round_mode -> string -> t end @@ -74,6 +75,7 @@ module CDouble = struct external sub: round_mode -> t -> t -> t = "sub_double" external mul: round_mode -> t -> t -> t = "mul_double" external div: round_mode -> t -> t -> t = "div_double" + external sqrt: round_mode -> t -> t = "sqrt_double" external atof: round_mode -> string -> t = "atof_double" end @@ -107,6 +109,7 @@ module CFloat = struct external sub: round_mode -> t -> t -> t = "sub_float" external mul: round_mode -> t -> t -> t = "mul_float" external div: round_mode -> t -> t -> t = "div_float" + external sqrt: round_mode -> t -> t = "sqrt_float" external atof: round_mode -> string -> t = "atof_float" diff --git a/src/cdomains/floatOps/floatOps.mli b/src/cdomains/floatOps/floatOps.mli index 05bf363872..cf24f75ed5 100644 --- a/src/cdomains/floatOps/floatOps.mli +++ b/src/cdomains/floatOps/floatOps.mli @@ -38,6 +38,7 @@ module type CFloatType = sig val sub: round_mode -> t -> t -> t val mul: round_mode -> t -> t -> t val div: round_mode -> t -> t -> t + val sqrt: round_mode -> t -> t val atof: round_mode -> string -> t end diff --git a/src/cdomains/floatOps/stubs.c b/src/cdomains/floatOps/stubs.c index e0485883dd..50e4a2fb31 100644 --- a/src/cdomains/floatOps/stubs.c +++ b/src/cdomains/floatOps/stubs.c @@ -36,6 +36,20 @@ static void change_round_mode(int mode) } } +#define UNARY_OP(name, type, op) \ + CAMLprim value name##_##type(value mode, value x) \ + { \ + int old_roundingmode = fegetround(); \ + change_round_mode(Int_val(mode)); \ + volatile type r, x1 = Double_val(x); \ + r = op(x1); \ + fesetround(old_roundingmode); \ + return caml_copy_double(r); \ + } + +UNARY_OP(sqrt, double, sqrt); +UNARY_OP(sqrt, float, sqrtf); + #define BINARY_OP(name, type, op) \ CAMLprim value name##_##type(value mode, value x, value y) \ { \ From 8141869487223039bddcfce8e9215632a9db60bb Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Thu, 16 Nov 2023 17:44:58 +0100 Subject: [PATCH 1000/1312] Termination distribute loops into multiple functions to make analysis terminate faster. --- .../09-complex-for-loop-terminating.c | 15 ++- .../10-complex-loop-terminating.c | 98 ++++++++++++++++- .../15-complex-loop-combination-terminating.c | 100 ++++++++++-------- 3 files changed, 163 insertions(+), 50 deletions(-) diff --git a/tests/regression/78-termination/09-complex-for-loop-terminating.c b/tests/regression/78-termination/09-complex-for-loop-terminating.c index fb2acaf569..74ee41eae8 100644 --- a/tests/regression/78-termination/09-complex-for-loop-terminating.c +++ b/tests/regression/78-termination/09-complex-for-loop-terminating.c @@ -2,8 +2,7 @@ // Apron is not precise enough for some nested loops #include -int main() -{ +int loops0(){ int i, j, k; // Outer loop @@ -33,6 +32,11 @@ int main() } printf("\n"); } + return 0; +} + +int loops1(){ + int i, j, k; // Loop with conditions for (i = 1; i <= 10; i++) @@ -82,6 +86,13 @@ int main() { printf("%d %d %d\n", a, b, c); } + return 0; +} + +int main() +{ + loops0(); + loops1(); return 0; } diff --git a/tests/regression/78-termination/10-complex-loop-terminating.c b/tests/regression/78-termination/10-complex-loop-terminating.c index 738fe78683..96253c445f 100644 --- a/tests/regression/78-termination/10-complex-loop-terminating.c +++ b/tests/regression/78-termination/10-complex-loop-terminating.c @@ -2,7 +2,87 @@ // Apron is not precise enough for some nested loops #include -int main() +int loops0(){ + int i = 1; + int j = 1; + int k = 5; + + // Outer while loop + while (i <= 5) + { + // Inner while loop 1 + while (j <= i) + { + printf("%d ", j); + j++; + } + printf("\n"); + j = 1; + + // Inner while loop 2 + while (k >= 1) + { + printf("%d ", k); + k--; + } + printf("\n"); + k = 5; + + i++; + } + + // Additional while loop + i = 5; + while (i >= 1) + { + j = i; + while (j >= 1) + { + printf("%d ", j); + j--; + } + printf("\n"); + i--; + } + + // Loop with conditions + i = 1; + while (i <= 10) + { + if (i % 2 == 0) + { + printf("%d is even\n", i); + } + else + { + printf("%d is odd\n", i); + } + i++; + } + + // Loop with nested conditions + i = 1; + while (i <= 10) + { + printf("Number: %d - ", i); + if (i < 5) + { + printf("Less than 5\n"); + } + else if (i > 5) + { + printf("Greater than 5\n"); + } + else + { + printf("Equal to 5\n"); + } + i++; + } + return 0; +} + +int loops1() { int i = 1; int j = 1; @@ -61,6 +141,14 @@ int main() i++; } + return 0; +} + +int loops2(){ + int i = 1; + int j = 1; + int k = 5; + // Loop with nested conditions i = 1; while (i <= 10) @@ -119,6 +207,12 @@ int main() b += 2; c += 3; } - return 0; } + +int main(){ + loops0(); + loops1(); + loops2(); + return 0; +} \ No newline at end of file diff --git a/tests/regression/78-termination/15-complex-loop-combination-terminating.c b/tests/regression/78-termination/15-complex-loop-combination-terminating.c index 9b97d4051b..4912bbb1f2 100644 --- a/tests/regression/78-termination/15-complex-loop-combination-terminating.c +++ b/tests/regression/78-termination/15-complex-loop-combination-terminating.c @@ -2,51 +2,7 @@ // Apron is not precise enough for some nested loops #include -int nested_while_loop_with_break(){ - int m; - - // Nested while loop with a break statement - int n = 1; - while (n <= 5) - { - printf("Outer While loop iteration: %d\n", n); - m = 1; - while (1) - { - printf("Inner While loop iteration: %d\n", m); - m++; - if (m == 4) - { - break; - } - } - n++; - } - return 0; -} - -int nested_loop_with_conditions(){ - // Loop with nested conditions - for (int v = 1; v <= 10; v++) - { - printf("Loop with Nested Conditions: %d - ", v); - if (v < 5) - { - printf("Less than 5\n"); - } - else if (v > 5) - { - printf("Greater than 5\n"); - } - else - { - printf("Equal to 5\n"); - } - } -} - -int main() -{ +int non_nested_loops(){ // Non-nested loops int i; @@ -71,8 +27,11 @@ int main() printf("Do-While loop iteration: %d\n", k); k++; } while (k <= 10); + return 0; +} - // Nested loops +int nested_loops(){ + // Nested loops int a, b; // Nested for and while loop @@ -109,7 +68,56 @@ int main() } p++; } while (p <= 5); + return 0; +} + +int nested_while_loop_with_break(){ + int m; + // Nested while loop with a break statement + int n = 1; + while (n <= 5) + { + printf("Outer While loop iteration: %d\n", n); + m = 1; + while (1) + { + printf("Inner While loop iteration: %d\n", m); + m++; + if (m == 4) + { + break; + } + } + n++; + } + return 0; +} + +int nested_loop_with_conditions(){ + // Loop with nested conditions + for (int v = 1; v <= 10; v++) + { + printf("Loop with Nested Conditions: %d - ", v); + if (v < 5) + { + printf("Less than 5\n"); + } + else if (v > 5) + { + printf("Greater than 5\n"); + } + else + { + printf("Equal to 5\n"); + } + } +} + +int main() +{ + non_nested_loops(); + nested_loops(); // Additional nested loops nested_while_loop_with_break(); nested_loop_with_conditions(); From 1ef011c6b675d78b25f4928cbbac64b07edee740 Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Thu, 16 Nov 2023 17:48:19 +0100 Subject: [PATCH 1001/1312] Add test case that did not work when not asserting the type bounds for signed types when sem.int.signed_overflow was set to assume_none. --- .../78-termination/50-decreasing-signed-int.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 tests/regression/78-termination/50-decreasing-signed-int.c diff --git a/tests/regression/78-termination/50-decreasing-signed-int.c b/tests/regression/78-termination/50-decreasing-signed-int.c new file mode 100644 index 0000000000..01daa5ee21 --- /dev/null +++ b/tests/regression/78-termination/50-decreasing-signed-int.c @@ -0,0 +1,13 @@ +// SKIP TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain octagon +int main() +{ + int x; + + if(x <= 0){ + return 0; + } + while (x > 0) { + x = x - 1; + } + return 0; +} From d9f39105b35e4c65ac01b457cdb19ab82ac17c87 Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Thu, 16 Nov 2023 17:49:31 +0100 Subject: [PATCH 1002/1312] Remove outdated comment. --- src/analyses/apron/relationAnalysis.apron.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index 8ac55ca323..8b128eb271 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -196,7 +196,7 @@ struct let assert_type_bounds ask rel x = assert (RD.Tracked.varinfo_tracked x); match Cilfacade.get_ikind x.vtype with - | ik -> (* don't add type bounds for signed when assume_none *) + | ik -> let (type_min, type_max) = IntDomain.Size.range ik in (* TODO: don't go through CIL exp? *) let e1 = BinOp (Le, Lval (Cil.var x), (Cil.kintegerCilint ik type_max), intType) in From 92023855b6872e71c8893d8e56b243032eefab01 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 17 Nov 2023 11:03:58 +0200 Subject: [PATCH 1003/1312] Add unsound 09-regsion/41-per-thread-array-init-race --- .../41-per-thread-array-init-race.c | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 tests/regression/09-regions/41-per-thread-array-init-race.c diff --git a/tests/regression/09-regions/41-per-thread-array-init-race.c b/tests/regression/09-regions/41-per-thread-array-init-race.c new file mode 100644 index 0000000000..f6d267495e --- /dev/null +++ b/tests/regression/09-regions/41-per-thread-array-init-race.c @@ -0,0 +1,40 @@ +// PARAM: --set ana.activated[+] region --enable ana.sv-comp.functions +// Per-thread array pointers passed via argument but initialized before thread create. +// Extracted from silver searcher. +#include +#include +extern void abort(void); +void assume_abort_if_not(int cond) { + if(!cond) {abort();} +} +extern int __VERIFIER_nondet_int(); + +void *thread(void *arg) { + int *p = arg; + int i = *p; // RACE! + return NULL; +} + +int main() { + int threads_total = __VERIFIER_nondet_int(); + assume_abort_if_not(threads_total >= 0); + + pthread_t *tids = malloc(threads_total * sizeof(pthread_t)); + int *is = calloc(threads_total, sizeof(int)); + + // create threads + for (int i = 0; i < threads_total; i++) { + pthread_create(&tids[i], NULL, &thread, &is[i]); // may fail but doesn't matter + is[i] = i; // RACE! + } + + // join threads + for (int i = 0; i < threads_total; i++) { + pthread_join(tids[i], NULL); + } + + free(tids); + free(is); + + return 0; +} From 0740491dcc290a8e41e693f0b8534af9540ebb37 Mon Sep 17 00:00:00 2001 From: stilscher <66023521+stilscher@users.noreply.github.com> Date: Thu, 16 Nov 2023 20:09:24 +0100 Subject: [PATCH 1004/1312] implement abs function for integers --- src/analyses/base.ml | 18 ++++++++++++++++++ src/analyses/libraryDesc.ml | 2 ++ src/analyses/libraryFunctions.ml | 2 +- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 86951ab4a5..bca3cb6588 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2328,6 +2328,23 @@ struct | _ -> failwith ("non-floating-point argument in call to function "^f.vname) end in + let apply_abs ik x = + let eval_x = eval_rv (Analyses.ask_of_ctx ctx) gs st x in + begin match eval_x with + | Int int_x -> + let xcast = ID.cast_to ik int_x in + (* TODO cover case where x is the MIN value -> undefined *) + (match ID.le xcast (ID.of_int ik Z.zero) with + | d when d = ID.of_int ik Z.zero -> xcast (* x positive *) + | d when d = ID.of_int ik Z.one -> ID.neg xcast (* x negative *) + | _ -> (* both possible *) + let x1 = ID.neg (ID.meet (ID.ending ik Z.zero) xcast) in + let x2 = ID.meet (ID.starting ik Z.zero) xcast in + ID.join x1 x2 + ) + | _ -> failwith ("non-integer argument in call to function "^f.vname) + end + in let result:value = begin match fun_args with | Nan (fk, str) when Cil.isPointerType (Cilfacade.typeOf str) -> Float (FD.nan_of fk) @@ -2357,6 +2374,7 @@ struct | Fmax (fd, x ,y) -> Float (apply_binary fd FD.fmax x y) | Fmin (fd, x ,y) -> Float (apply_binary fd FD.fmin x y) | Sqrt (fk, x) -> Float (apply_unary fk FD.sqrt x) + | Abs x -> Int (ID.cast_to IInt (apply_abs IInt x)) end in begin match lv with diff --git a/src/analyses/libraryDesc.ml b/src/analyses/libraryDesc.ml index 5403612fae..8cd3dfa1ba 100644 --- a/src/analyses/libraryDesc.ml +++ b/src/analyses/libraryDesc.ml @@ -27,6 +27,7 @@ type math = | Islessequal of (Basetype.CilExp.t * Basetype.CilExp.t) | Islessgreater of (Basetype.CilExp.t * Basetype.CilExp.t) | Isunordered of (Basetype.CilExp.t * Basetype.CilExp.t) + | Abs of Basetype.CilExp.t | Ceil of (CilType.Fkind.t * Basetype.CilExp.t) | Floor of (CilType.Fkind.t * Basetype.CilExp.t) | Fabs of (CilType.Fkind.t * Basetype.CilExp.t) @@ -159,6 +160,7 @@ module MathPrintable = struct | Islessequal (exp1, exp2) -> Pretty.dprintf "isLessEqual(%a, %a)" d_exp exp1 d_exp exp2 | Islessgreater (exp1, exp2) -> Pretty.dprintf "isLessGreater(%a, %a)" d_exp exp1 d_exp exp2 | Isunordered (exp1, exp2) -> Pretty.dprintf "isUnordered(%a, %a)" d_exp exp1 d_exp exp2 + | Abs exp -> Pretty.dprintf "abs(%a)" d_exp exp | Ceil (fk, exp) -> Pretty.dprintf "(%a )ceil(%a)" d_fkind fk d_exp exp | Floor (fk, exp) -> Pretty.dprintf "(%a )floor(%a)" d_fkind fk d_exp exp | Fabs (fk, exp) -> Pretty.dprintf "(%a )fabs(%a)" d_fkind fk d_exp exp diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 8f8a43cb29..ae33f57f70 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -130,7 +130,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("wcstombs", unknown ~attrs:[ThreadUnsafe] [drop "dst" [w]; drop "src" [r]; drop "size" []]); ("wcsrtombs", unknown ~attrs:[ThreadUnsafe] [drop "dst" [w]; drop "src" [r_deep; w]; drop "size" []; drop "ps" [r_deep; w_deep]]); ("mbstowcs", unknown [drop "dest" [w]; drop "src" [r]; drop "n" []]); - ("abs", unknown [drop "j" []]); + ("abs", special [__ "j" []] @@ fun j -> Math { fun_args = (Abs j) }); ("localtime_r", unknown [drop "timep" [r]; drop "result" [w]]); ("strpbrk", unknown [drop "s" [r]; drop "accept" [r]]); ("_setjmp", special [__ "env" [w]] @@ fun env -> Setjmp { env }); (* only has one underscore *) From 73bf6fee56c6f4fe0114b677efa80369ee80654c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 17 Nov 2023 12:07:43 +0200 Subject: [PATCH 1005/1312] Fix region bullet escaping (issue #107) --- src/analyses/region.ml | 10 +++++++- src/cdomains/regionDomain.ml | 23 +++++++++++++++++-- .../regression/09-regions/38-escape_malloc.c | 4 ++-- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/analyses/region.ml b/src/analyses/region.ml index 652526543c..5b10586aba 100644 --- a/src/analyses/region.ml +++ b/src/analyses/region.ml @@ -177,7 +177,15 @@ struct let threadenter ctx ~multiple lval f args = [`Lifted (RegMap.bot ())] - let threadspawn ctx ~multiple lval f args fctx = ctx.local + let threadspawn ctx ~multiple lval f args fctx = + match ctx.local with + | `Lifted reg -> + let old_regpart = ctx.global () in + let regpart, reg = List.fold_right Reg.assign_escape args (old_regpart, reg) in + if not (RegPart.leq regpart old_regpart) then + ctx.sideg () regpart; + `Lifted reg + | x -> x let exitstate v = `Lifted (RegMap.bot ()) diff --git a/src/cdomains/regionDomain.ml b/src/cdomains/regionDomain.ml index b577e3499f..681eb79007 100644 --- a/src/cdomains/regionDomain.ml +++ b/src/cdomains/regionDomain.ml @@ -157,13 +157,13 @@ struct (* This is the main logic for dealing with the bullet and finding it an * owner... *) - let add_set (s:set) llist (p,m:t): t = + let add_set ?(escape=false) (s:set) llist (p,m:t): t = if RS.has_bullet s then let f key value (ys, x) = if RS.has_bullet value then key::ys, RS.join value x else ys,x in let ys,x = RegMap.fold f m (llist, RS.remove_bullet s) in let x = RS.remove_bullet x in - if RS.is_empty x then + if not escape && RS.is_empty x then p, RegMap.add_list_set llist RS.single_bullet m else RegPart.add x p, RegMap.add_list_set ys x m @@ -215,6 +215,25 @@ struct | Some (_,x,_) -> p, RegMap.add x RS.single_bullet m | _ -> p,m + (* Copied & modified from assign. *) + let assign_escape (rval: exp) (st: t): t = + (* let _ = printf "%a = %a\n" (printLval plainCilPrinter) lval (printExp plainCilPrinter) rval in *) + let t = Cilfacade.typeOf rval in + if isPointerType t then begin (* TODO: this currently allows function pointers, e.g. in iowarrior, but should it? *) + match eval_exp rval with + (* TODO: should offs_x matter? *) + | Some (deref_y,y,offs_y) -> + let (p,m) = st in begin + match is_global y with + | true -> + add_set ~escape:true (RS.single_vf y) [] st + | false -> + add_set ~escape:true (RegMap.find y m) [y] st + end + | _ -> st + end else + st + let related_globals (deref_vfd: eval_t) (p,m: t): elt list = let add_o o2 (v,o) = (v, F.add_offset o o2) in match deref_vfd with diff --git a/tests/regression/09-regions/38-escape_malloc.c b/tests/regression/09-regions/38-escape_malloc.c index 9f5b44531e..c849d5bbe3 100644 --- a/tests/regression/09-regions/38-escape_malloc.c +++ b/tests/regression/09-regions/38-escape_malloc.c @@ -9,7 +9,7 @@ pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER; void *t_fun(void *arg) { int *p = (int *) arg; pthread_mutex_lock(&mutex1); - (*p)++; // TODO RACE! + (*p)++; // RACE! pthread_mutex_unlock(&mutex1); return NULL; } @@ -20,7 +20,7 @@ int main(void) { // TODO: q escapes as region owner pthread_create(&id, NULL, t_fun, (void *) q); pthread_mutex_lock(&mutex2); - (*q)++; // TODO RACE! + (*q)++; // RACE! pthread_mutex_unlock(&mutex2); pthread_join (id, NULL); return 0; From b9ede51e83b64db87b6a2e0322d3c348c114c07c Mon Sep 17 00:00:00 2001 From: stilscher <66023521+stilscher@users.noreply.github.com> Date: Fri, 17 Nov 2023 11:11:14 +0100 Subject: [PATCH 1006/1312] implement special case for most-negative value --- src/analyses/base.ml | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index bca3cb6588..f25f0a9693 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2333,14 +2333,19 @@ struct begin match eval_x with | Int int_x -> let xcast = ID.cast_to ik int_x in - (* TODO cover case where x is the MIN value -> undefined *) - (match ID.le xcast (ID.of_int ik Z.zero) with - | d when d = ID.of_int ik Z.zero -> xcast (* x positive *) - | d when d = ID.of_int ik Z.one -> ID.neg xcast (* x negative *) - | _ -> (* both possible *) - let x1 = ID.neg (ID.meet (ID.ending ik Z.zero) xcast) in - let x2 = ID.meet (ID.starting ik Z.zero) xcast in - ID.join x1 x2 + (* the absolute value of the most-negative value is out of range for 2'complement types *) + (match (ID.minimal xcast), (ID.minimal (ID.top_of ik)) with + | _, None + | None, _ -> ID.top_of ik + | Some mx, Some mm when Z.equal mx mm -> ID.top_of ik + | _, _ -> + match ID.le xcast (ID.of_int ik Z.zero) with + | d when d = ID.of_int ik Z.zero -> xcast (* x positive *) + | d when d = ID.of_int ik Z.one -> ID.neg xcast (* x negative *) + | _ -> (* both possible *) + let x1 = ID.neg (ID.meet (ID.ending ik Z.zero) xcast) in + let x2 = ID.meet (ID.starting ik Z.zero) xcast in + ID.join x1 x2 ) | _ -> failwith ("non-integer argument in call to function "^f.vname) end From 6f549915ebd3c319422e36acab66eb90c614eea5 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 17 Nov 2023 12:57:05 +0200 Subject: [PATCH 1007/1312] Fix YAML witness invariants for unrolled loops (closes #1225) --- src/witness/yamlWitness.ml | 96 +++++++++++++++++++++++--------------- 1 file changed, 59 insertions(+), 37 deletions(-) diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index 9e8ebeff51..dc4890753d 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -145,6 +145,16 @@ struct module FCMap = BatHashtbl.Make (Printable.Prod (CilType.Fundec) (Spec.C)) type con_inv = {node: Node.t; context: Spec.C.t; invariant: Invariant.t; state: Spec.D.t} + (* TODO: fix location hack *) + module LH = BatHashtbl.Make (CilType.Location) + let location2nodes: Node.t list LH.t Lazy.t = lazy ( + let lh = LH.create 113 in + NH.iter (fun n _ -> + LH.modify_def [] (Node.location n) (List.cons n) lh + ) (Lazy.force nh); + lh + ) + let write () = let input_files = GobConfig.get_string_list "files" in let data_model = match GobConfig.get_string "exp.architecture" with @@ -208,26 +218,32 @@ struct (* Generate location invariants (without precondition) *) let entries = if entry_type_enabled YamlWitnessType.LocationInvariant.entry_type then ( - NH.fold (fun n local acc -> - let loc = Node.location n in - if is_invariant_node n then ( - let lvals = local_lvals n local in - match R.ask_local_node n ~local (Invariant {Invariant.default_context with lvals}) with - | `Lifted inv -> - let invs = WitnessUtil.InvariantExp.process_exp inv in - List.fold_left (fun acc inv -> - let location_function = (Node.find_fundec n).svar.vname in - let location = Entry.location ~location:loc ~location_function in - let invariant = Entry.invariant (CilType.Exp.show inv) in - let entry = Entry.location_invariant ~task ~location ~invariant in - entry :: acc - ) acc invs - | `Bot | `Top -> (* TODO: 0 for bot (dead code)? *) - acc - ) - else + LH.fold (fun loc ns acc -> + let fundec = ref None in + let inv = List.fold_left (fun acc n -> + if is_invariant_node n then ( + fundec := Some (Node.find_fundec n); (* TODO: fix location hack *) + let local = try NH.find (Lazy.force nh) n with Not_found -> Spec.D.bot () in + let lvals = local_lvals n local in + Invariant.(acc || R.ask_local_node n ~local (Invariant {Invariant.default_context with lvals})) + ) + else + acc + ) (Invariant.bot ()) ns + in + match inv with + | `Lifted inv -> + let invs = WitnessUtil.InvariantExp.process_exp inv in + List.fold_left (fun acc inv -> + let location_function = (Option.get !fundec).svar.vname in + let location = Entry.location ~location:loc ~location_function in + let invariant = Entry.invariant (CilType.Exp.show inv) in + let entry = Entry.location_invariant ~task ~location ~invariant in + entry :: acc + ) acc invs + | `Bot | `Top -> (* TODO: 0 for bot (dead code)? *) acc - ) (Lazy.force nh) entries + ) (Lazy.force location2nodes) entries ) else entries @@ -236,25 +252,31 @@ struct (* Generate loop invariants (without precondition) *) let entries = if entry_type_enabled YamlWitnessType.LoopInvariant.entry_type then ( - NH.fold (fun n local acc -> - let loc = Node.location n in - if WitnessInvariant.emit_loop_head && WitnessUtil.NH.mem WitnessInvariant.loop_heads n then ( - match R.ask_local_node n ~local (Invariant Invariant.default_context) with - | `Lifted inv -> - let invs = WitnessUtil.InvariantExp.process_exp inv in - List.fold_left (fun acc inv -> - let location_function = (Node.find_fundec n).svar.vname in - let location = Entry.location ~location:loc ~location_function in - let invariant = Entry.invariant (CilType.Exp.show inv) in - let entry = Entry.loop_invariant ~task ~location ~invariant in - entry :: acc - ) acc invs - | `Bot | `Top -> (* TODO: 0 for bot (dead code)? *) - acc - ) - else + LH.fold (fun loc ns acc -> + let fundec = ref None in + let inv = List.fold_left (fun acc n -> + if WitnessInvariant.emit_loop_head && WitnessUtil.NH.mem WitnessInvariant.loop_heads n then ( + fundec := Some (Node.find_fundec n); (* TODO: fix location hack *) + let local = try NH.find (Lazy.force nh) n with Not_found -> Spec.D.bot () in + Invariant.(acc || R.ask_local_node n ~local (Invariant Invariant.default_context)) + ) + else + acc + ) (Invariant.bot ()) ns + in + match inv with + | `Lifted inv -> + let invs = WitnessUtil.InvariantExp.process_exp inv in + List.fold_left (fun acc inv -> + let location_function = (Option.get !fundec).svar.vname in + let location = Entry.location ~location:loc ~location_function in + let invariant = Entry.invariant (CilType.Exp.show inv) in + let entry = Entry.loop_invariant ~task ~location ~invariant in + entry :: acc + ) acc invs + | `Bot | `Top -> (* TODO: 0 for bot (dead code)? *) acc - ) (Lazy.force nh) entries + ) (Lazy.force location2nodes) entries ) else entries From 952b90dbfcfd2c8ef4750abf4daca6cd5da6d8bd Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 17 Nov 2023 13:19:09 +0200 Subject: [PATCH 1008/1312] Fix bisect_ppx build --- src/witness/yamlWitness.ml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index 023212fa42..22800c07dc 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -251,7 +251,7 @@ struct fundec := Some (Node.find_fundec n); (* TODO: fix location hack *) let local = try NH.find (Lazy.force nh) n with Not_found -> Spec.D.bot () in let lvals = local_lvals n local in - Invariant.(acc || R.ask_local_node n ~local (Invariant {Invariant.default_context with lvals})) + Invariant.(acc || R.ask_local_node n ~local (Invariant {Invariant.default_context with lvals})) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) ) else acc @@ -284,7 +284,7 @@ struct if WitnessInvariant.emit_loop_head && WitnessUtil.NH.mem WitnessInvariant.loop_heads n then ( fundec := Some (Node.find_fundec n); (* TODO: fix location hack *) let local = try NH.find (Lazy.force nh) n with Not_found -> Spec.D.bot () in - Invariant.(acc || R.ask_local_node n ~local (Invariant Invariant.default_context)) + Invariant.(acc || R.ask_local_node n ~local (Invariant Invariant.default_context)) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) ) else acc @@ -448,7 +448,7 @@ struct fundec := Some (Node.find_fundec n); (* TODO: fix location hack *) let local = try NH.find (Lazy.force nh) n with Not_found -> Spec.D.bot () in let lvals = local_lvals n local in - Invariant.(acc || R.ask_local_node n ~local (Invariant {Invariant.default_context with lvals})) + Invariant.(acc || R.ask_local_node n ~local (Invariant {Invariant.default_context with lvals})) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) ) else acc @@ -481,7 +481,7 @@ struct if WitnessInvariant.emit_loop_head && WitnessUtil.NH.mem WitnessInvariant.loop_heads n then ( fundec := Some (Node.find_fundec n); (* TODO: fix location hack *) let local = try NH.find (Lazy.force nh) n with Not_found -> Spec.D.bot () in - Invariant.(acc || R.ask_local_node n ~local (Invariant Invariant.default_context)) + Invariant.(acc || R.ask_local_node n ~local (Invariant Invariant.default_context)) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) ) else acc From 4ae355695675d2030b00c5df991666b8952d6357 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 17 Nov 2023 15:11:36 +0200 Subject: [PATCH 1009/1312] Fix missing unrolled iterations in YAML witness invariants Unrolled loop heads are different nodes, which aren't actually loop heads. For sound invariants, they must also be included if at a location if at least one is. --- src/witness/yamlWitness.ml | 152 ++++++++++++++++++------------------- 1 file changed, 76 insertions(+), 76 deletions(-) diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index 22800c07dc..1f83c8625e 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -245,29 +245,29 @@ struct let entries = if entry_type_enabled YamlWitnessType.LocationInvariant.entry_type then ( LH.fold (fun loc ns acc -> - let fundec = ref None in - let inv = List.fold_left (fun acc n -> - if is_invariant_node n then ( + if List.exists is_invariant_node ns then ( + let fundec = ref None in + let inv = List.fold_left (fun acc n -> fundec := Some (Node.find_fundec n); (* TODO: fix location hack *) let local = try NH.find (Lazy.force nh) n with Not_found -> Spec.D.bot () in let lvals = local_lvals n local in Invariant.(acc || R.ask_local_node n ~local (Invariant {Invariant.default_context with lvals})) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) - ) - else - acc - ) (Invariant.bot ()) ns - in - match inv with - | `Lifted inv -> - let invs = WitnessUtil.InvariantExp.process_exp inv in - List.fold_left (fun acc inv -> - let location_function = (Option.get !fundec).svar.vname in - let location = Entry.location ~location:loc ~location_function in - let invariant = Entry.invariant (CilType.Exp.show inv) in - let entry = Entry.location_invariant ~task ~location ~invariant in - entry :: acc - ) acc invs - | `Bot | `Top -> (* TODO: 0 for bot (dead code)? *) + ) (Invariant.bot ()) ns + in + match inv with + | `Lifted inv -> + let invs = WitnessUtil.InvariantExp.process_exp inv in + List.fold_left (fun acc inv -> + let location_function = (Option.get !fundec).svar.vname in + let location = Entry.location ~location:loc ~location_function in + let invariant = Entry.invariant (CilType.Exp.show inv) in + let entry = Entry.location_invariant ~task ~location ~invariant in + entry :: acc + ) acc invs + | `Bot | `Top -> (* TODO: 0 for bot (dead code)? *) + acc + ) + else acc ) (Lazy.force location2nodes) entries ) @@ -279,28 +279,28 @@ struct let entries = if entry_type_enabled YamlWitnessType.LoopInvariant.entry_type then ( LH.fold (fun loc ns acc -> - let fundec = ref None in - let inv = List.fold_left (fun acc n -> - if WitnessInvariant.emit_loop_head && WitnessUtil.NH.mem WitnessInvariant.loop_heads n then ( + if WitnessInvariant.emit_loop_head && List.exists (WitnessUtil.NH.mem WitnessInvariant.loop_heads) ns then ( + let fundec = ref None in + let inv = List.fold_left (fun acc n -> fundec := Some (Node.find_fundec n); (* TODO: fix location hack *) let local = try NH.find (Lazy.force nh) n with Not_found -> Spec.D.bot () in Invariant.(acc || R.ask_local_node n ~local (Invariant Invariant.default_context)) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) - ) - else - acc - ) (Invariant.bot ()) ns - in - match inv with - | `Lifted inv -> - let invs = WitnessUtil.InvariantExp.process_exp inv in - List.fold_left (fun acc inv -> - let location_function = (Option.get !fundec).svar.vname in - let location = Entry.location ~location:loc ~location_function in - let invariant = Entry.invariant (CilType.Exp.show inv) in - let entry = Entry.loop_invariant ~task ~location ~invariant in - entry :: acc - ) acc invs - | `Bot | `Top -> (* TODO: 0 for bot (dead code)? *) + ) (Invariant.bot ()) ns + in + match inv with + | `Lifted inv -> + let invs = WitnessUtil.InvariantExp.process_exp inv in + List.fold_left (fun acc inv -> + let location_function = (Option.get !fundec).svar.vname in + let location = Entry.location ~location:loc ~location_function in + let invariant = Entry.invariant (CilType.Exp.show inv) in + let entry = Entry.loop_invariant ~task ~location ~invariant in + entry :: acc + ) acc invs + | `Bot | `Top -> (* TODO: 0 for bot (dead code)? *) + acc + ) + else acc ) (Lazy.force location2nodes) entries ) @@ -442,29 +442,29 @@ struct let invariants = if invariant_type_enabled YamlWitnessType.InvariantSet.LocationInvariant.invariant_type then ( LH.fold (fun loc ns acc -> - let fundec = ref None in - let inv = List.fold_left (fun acc n -> - if is_invariant_node n then ( + if List.exists is_invariant_node ns then ( + let fundec = ref None in + let inv = List.fold_left (fun acc n -> fundec := Some (Node.find_fundec n); (* TODO: fix location hack *) let local = try NH.find (Lazy.force nh) n with Not_found -> Spec.D.bot () in let lvals = local_lvals n local in Invariant.(acc || R.ask_local_node n ~local (Invariant {Invariant.default_context with lvals})) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) - ) - else - acc - ) (Invariant.bot ()) ns - in - match inv with - | `Lifted inv -> - let invs = WitnessUtil.InvariantExp.process_exp inv in - List.fold_left (fun acc inv -> - let location_function = (Option.get !fundec).svar.vname in - let location = Entry.location ~location:loc ~location_function in - let invariant = CilType.Exp.show inv in - let invariant = Entry.location_invariant' ~location ~invariant in - invariant :: acc - ) acc invs - | `Bot | `Top -> (* TODO: 0 for bot (dead code)? *) + ) (Invariant.bot ()) ns + in + match inv with + | `Lifted inv -> + let invs = WitnessUtil.InvariantExp.process_exp inv in + List.fold_left (fun acc inv -> + let location_function = (Option.get !fundec).svar.vname in + let location = Entry.location ~location:loc ~location_function in + let invariant = CilType.Exp.show inv in + let invariant = Entry.location_invariant' ~location ~invariant in + invariant :: acc + ) acc invs + | `Bot | `Top -> (* TODO: 0 for bot (dead code)? *) + acc + ) + else acc ) (Lazy.force location2nodes) invariants ) @@ -476,28 +476,28 @@ struct let invariants = if invariant_type_enabled YamlWitnessType.InvariantSet.LoopInvariant.invariant_type then ( LH.fold (fun loc ns acc -> - let fundec = ref None in - let inv = List.fold_left (fun acc n -> - if WitnessInvariant.emit_loop_head && WitnessUtil.NH.mem WitnessInvariant.loop_heads n then ( + if WitnessInvariant.emit_loop_head && List.exists (WitnessUtil.NH.mem WitnessInvariant.loop_heads) ns then ( + let fundec = ref None in + let inv = List.fold_left (fun acc n -> fundec := Some (Node.find_fundec n); (* TODO: fix location hack *) let local = try NH.find (Lazy.force nh) n with Not_found -> Spec.D.bot () in Invariant.(acc || R.ask_local_node n ~local (Invariant Invariant.default_context)) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) - ) - else - acc - ) (Invariant.bot ()) ns - in - match inv with - | `Lifted inv -> - let invs = WitnessUtil.InvariantExp.process_exp inv in - List.fold_left (fun acc inv -> - let location_function = (Option.get !fundec).svar.vname in - let location = Entry.location ~location:loc ~location_function in - let invariant = CilType.Exp.show inv in - let invariant = Entry.loop_invariant' ~location ~invariant in - invariant :: acc - ) acc invs - | `Bot | `Top -> (* TODO: 0 for bot (dead code)? *) + ) (Invariant.bot ()) ns + in + match inv with + | `Lifted inv -> + let invs = WitnessUtil.InvariantExp.process_exp inv in + List.fold_left (fun acc inv -> + let location_function = (Option.get !fundec).svar.vname in + let location = Entry.location ~location:loc ~location_function in + let invariant = CilType.Exp.show inv in + let invariant = Entry.loop_invariant' ~location ~invariant in + invariant :: acc + ) acc invs + | `Bot | `Top -> (* TODO: 0 for bot (dead code)? *) + acc + ) + else acc ) (Lazy.force location2nodes) invariants ) From 0fb479f506c99e1e415c08d157ae09a8a768fa4c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 17 Nov 2023 15:16:48 +0200 Subject: [PATCH 1010/1312] Refactor YAML witness fundec lookup --- src/witness/yamlWitness.ml | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index 1f83c8625e..1f106c936e 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -246,9 +246,7 @@ struct if entry_type_enabled YamlWitnessType.LocationInvariant.entry_type then ( LH.fold (fun loc ns acc -> if List.exists is_invariant_node ns then ( - let fundec = ref None in let inv = List.fold_left (fun acc n -> - fundec := Some (Node.find_fundec n); (* TODO: fix location hack *) let local = try NH.find (Lazy.force nh) n with Not_found -> Spec.D.bot () in let lvals = local_lvals n local in Invariant.(acc || R.ask_local_node n ~local (Invariant {Invariant.default_context with lvals})) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) @@ -256,10 +254,11 @@ struct in match inv with | `Lifted inv -> + let fundec = Node.find_fundec (List.hd ns) in (* TODO: fix location hack *) + let location_function = fundec.svar.vname in + let location = Entry.location ~location:loc ~location_function in let invs = WitnessUtil.InvariantExp.process_exp inv in List.fold_left (fun acc inv -> - let location_function = (Option.get !fundec).svar.vname in - let location = Entry.location ~location:loc ~location_function in let invariant = Entry.invariant (CilType.Exp.show inv) in let entry = Entry.location_invariant ~task ~location ~invariant in entry :: acc @@ -280,19 +279,18 @@ struct if entry_type_enabled YamlWitnessType.LoopInvariant.entry_type then ( LH.fold (fun loc ns acc -> if WitnessInvariant.emit_loop_head && List.exists (WitnessUtil.NH.mem WitnessInvariant.loop_heads) ns then ( - let fundec = ref None in let inv = List.fold_left (fun acc n -> - fundec := Some (Node.find_fundec n); (* TODO: fix location hack *) let local = try NH.find (Lazy.force nh) n with Not_found -> Spec.D.bot () in Invariant.(acc || R.ask_local_node n ~local (Invariant Invariant.default_context)) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) ) (Invariant.bot ()) ns in match inv with | `Lifted inv -> + let fundec = Node.find_fundec (List.hd ns) in (* TODO: fix location hack *) + let location_function = fundec.svar.vname in + let location = Entry.location ~location:loc ~location_function in let invs = WitnessUtil.InvariantExp.process_exp inv in List.fold_left (fun acc inv -> - let location_function = (Option.get !fundec).svar.vname in - let location = Entry.location ~location:loc ~location_function in let invariant = Entry.invariant (CilType.Exp.show inv) in let entry = Entry.loop_invariant ~task ~location ~invariant in entry :: acc @@ -443,9 +441,7 @@ struct if invariant_type_enabled YamlWitnessType.InvariantSet.LocationInvariant.invariant_type then ( LH.fold (fun loc ns acc -> if List.exists is_invariant_node ns then ( - let fundec = ref None in let inv = List.fold_left (fun acc n -> - fundec := Some (Node.find_fundec n); (* TODO: fix location hack *) let local = try NH.find (Lazy.force nh) n with Not_found -> Spec.D.bot () in let lvals = local_lvals n local in Invariant.(acc || R.ask_local_node n ~local (Invariant {Invariant.default_context with lvals})) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) @@ -453,10 +449,11 @@ struct in match inv with | `Lifted inv -> + let fundec = Node.find_fundec (List.hd ns) in (* TODO: fix location hack *) + let location_function = fundec.svar.vname in + let location = Entry.location ~location:loc ~location_function in let invs = WitnessUtil.InvariantExp.process_exp inv in List.fold_left (fun acc inv -> - let location_function = (Option.get !fundec).svar.vname in - let location = Entry.location ~location:loc ~location_function in let invariant = CilType.Exp.show inv in let invariant = Entry.location_invariant' ~location ~invariant in invariant :: acc @@ -477,19 +474,18 @@ struct if invariant_type_enabled YamlWitnessType.InvariantSet.LoopInvariant.invariant_type then ( LH.fold (fun loc ns acc -> if WitnessInvariant.emit_loop_head && List.exists (WitnessUtil.NH.mem WitnessInvariant.loop_heads) ns then ( - let fundec = ref None in let inv = List.fold_left (fun acc n -> - fundec := Some (Node.find_fundec n); (* TODO: fix location hack *) let local = try NH.find (Lazy.force nh) n with Not_found -> Spec.D.bot () in Invariant.(acc || R.ask_local_node n ~local (Invariant Invariant.default_context)) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) ) (Invariant.bot ()) ns in match inv with | `Lifted inv -> + let fundec = Node.find_fundec (List.hd ns) in (* TODO: fix location hack *) + let location_function = fundec.svar.vname in + let location = Entry.location ~location:loc ~location_function in let invs = WitnessUtil.InvariantExp.process_exp inv in List.fold_left (fun acc inv -> - let location_function = (Option.get !fundec).svar.vname in - let location = Entry.location ~location:loc ~location_function in let invariant = CilType.Exp.show inv in let invariant = Entry.loop_invariant' ~location ~invariant in invariant :: acc From 3c89ece9f7630beda3a057b1705a28ea0496dc65 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 17 Nov 2023 15:32:41 +0200 Subject: [PATCH 1011/1312] Fix YamlWitness indentation --- src/witness/yamlWitness.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index 1f106c936e..ee370f2b6a 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -445,7 +445,7 @@ struct let local = try NH.find (Lazy.force nh) n with Not_found -> Spec.D.bot () in let lvals = local_lvals n local in Invariant.(acc || R.ask_local_node n ~local (Invariant {Invariant.default_context with lvals})) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) - ) (Invariant.bot ()) ns + ) (Invariant.bot ()) ns in match inv with | `Lifted inv -> From 9966b873a5d07f5a2c094245312a914ba2130c9d Mon Sep 17 00:00:00 2001 From: oliver Date: Fri, 17 Nov 2023 14:49:33 +0100 Subject: [PATCH 1012/1312] update dune-project and documentation --- docs/developer-guide/debugging.md | 5 +++-- dune-project | 4 +++- goblint.opam.locked | 12 ++++++------ 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/docs/developer-guide/debugging.md b/docs/developer-guide/debugging.md index 7875a9b01e..5c1db12227 100644 --- a/docs/developer-guide/debugging.md +++ b/docs/developer-guide/debugging.md @@ -85,7 +85,7 @@ The configuration file has to be named `launch.json` and must reside in the `./. "configurations": [ { "name": "Goblint", - "type": "ocamlearlybird", + "type": "ocaml.earlybird", "request": "launch", "program": "${workspaceFolder}/goblint.byte", "arguments": [ @@ -97,7 +97,8 @@ The configuration file has to be named `launch.json` and must reside in the `./. ] } ``` -Note that the individual arguments to Goblint should be passed here as separate strings that do not contain spaces. +Note that the individual arguments to Goblint should be passed here as separate strings that do not contain spaces. Finally, to enable breakpoints uncomment `(map_workspace_root false)` in the dune-project file. + ### Running Goblint in the VS Code Debugger diff --git a/dune-project b/dune-project index 4a9cd8e3c1..05c7d9418c 100644 --- a/dune-project +++ b/dune-project @@ -1,4 +1,4 @@ -(lang dune 3.6) +(lang dune 3.7) (using dune_site 0.1) (cram enable) (name goblint) @@ -64,3 +64,5 @@ (share lib) (share conf)) ) + +; (map_workspace_root false) ;uncomment to enable breakpoints diff --git a/goblint.opam.locked b/goblint.opam.locked index 2744d2fe92..0abe989955 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -50,12 +50,12 @@ depends: [ "cpu" {= "2.0.0"} "csexp" {= "1.5.1"} "ctypes" {= "0.20.1"} - "dune" {= "3.6.1"} - "dune-build-info" {= "3.6.1"} - "dune-configurator" {= "3.6.1"} - "dune-private-libs" {= "3.6.1"} - "dune-site" {= "3.6.1"} - "dyn" {= "3.6.1"} + "dune" {= "3.7.1"} + "dune-build-info" {= "3.7.1"} + "dune-configurator" {= "3.7.1"} + "dune-private-libs" {= "3.7.1"} + "dune-site" {= "3.7.1"} + "dyn" {= "3.7.1"} "fileutils" {= "0.6.4"} "fmt" {= "0.9.0"} "fpath" {= "0.7.3"} From b57e80d2f4a82102af284c6c6f3a26eba35d24b5 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 17 Nov 2023 15:54:27 +0200 Subject: [PATCH 1013/1312] Bump YAML entry size for large unrolled invariants --- src/witness/yamlWitness.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index ee370f2b6a..c80611c83f 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -148,7 +148,7 @@ let yaml_entries_to_file ?(invariants=0) yaml_entries file = (* Yaml_unix.to_file_exn file yaml *) (* to_file/to_string uses a fixed-size buffer... *) (* estimate how big it should be + extra in case empty *) - let text = match Yaml.to_string ~len:((List.length yaml_entries + invariants) * 4096 + 2048) yaml with + let text = match Yaml.to_string ~len:((List.length yaml_entries + invariants) * 8192 + 2048) yaml with | Ok text -> text | Error (`Msg m) -> failwith ("Yaml.to_string: " ^ m) in From f70948296572a81d36f734522c1441db3fec19bb Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 17 Nov 2023 16:07:37 +0200 Subject: [PATCH 1014/1312] Make YAML output buffer sizing exponential --- src/util/std/gobYaml.ml | 11 +++++++++++ src/witness/yamlWitness.ml | 13 ++++++------- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/util/std/gobYaml.ml b/src/util/std/gobYaml.ml index a4f3e597aa..131daaaebb 100644 --- a/src/util/std/gobYaml.ml +++ b/src/util/std/gobYaml.ml @@ -1,3 +1,14 @@ +let to_string' ?(len=65535 * 4) ?encoding ?scalar_style ?layout_style v = + assert (len >= 1); + let rec aux len = + match Yaml.to_string ~len ?encoding ?scalar_style ?layout_style v with + | Ok _ as o -> o + | Error (`Msg ("scalar failed" | "doc_end failed")) when len < Sys.max_string_length / 2 -> + aux (len * 2) + | Error (`Msg _) as e -> e + in + aux len + include Yaml.Util include GobResult.Syntax diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index c80611c83f..635ba4ad72 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -143,12 +143,11 @@ struct } end -let yaml_entries_to_file ?(invariants=0) yaml_entries file = +let yaml_entries_to_file yaml_entries file = let yaml = `A yaml_entries in (* Yaml_unix.to_file_exn file yaml *) (* to_file/to_string uses a fixed-size buffer... *) - (* estimate how big it should be + extra in case empty *) - let text = match Yaml.to_string ~len:((List.length yaml_entries + invariants) * 8192 + 2048) yaml with + let text = match GobYaml.to_string' yaml with | Ok text -> text | Error (`Msg m) -> failwith ("Yaml.to_string: " ^ m) in @@ -432,7 +431,7 @@ struct in (* Generate invariant set *) - let (entries, invariants) = + let entries = if entry_type_enabled YamlWitnessType.InvariantSet.entry_type then ( let invariants = [] in @@ -503,10 +502,10 @@ struct let invariants = List.rev invariants in let entry = Entry.invariant_set ~task ~invariants in - (entry :: entries, List.length invariants) + entry :: entries ) else - (entries, 0) + entries in let yaml_entries = List.rev_map YamlWitnessType.Entry.to_yaml entries in (* reverse to make entries in file in the same order as generation messages *) @@ -515,7 +514,7 @@ struct (Pretty.dprintf "total generation entries: %d" (List.length yaml_entries), None); ]; - yaml_entries_to_file ~invariants yaml_entries (Fpath.v (GobConfig.get_string "witness.yaml.path")) + yaml_entries_to_file yaml_entries (Fpath.v (GobConfig.get_string "witness.yaml.path")) let write () = Timing.wrap "yaml witness" write () From 26d8012e63f2c161a21146ce6e22480d1e4e930b Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Fri, 17 Nov 2023 15:18:32 +0100 Subject: [PATCH 1015/1312] Autotuner: Remove activateTerminationAnalysis, as there is already other code for it. --- src/autoTune.ml | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/autoTune.ml b/src/autoTune.ml index 645fcfcf84..cc1af59940 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -466,13 +466,6 @@ let wideningOption factors file = print_endline "Enabled widening thresholds"; } -let activateTerminationAnalysis () = - enableAnalyses ["termination"; "apron"]; - set_string "sem.int.signed_overflow" "assume_none"; - set_string "ana.apron.domain" "polyhedra"; - set_bool "ana.int.interval_threshold_widening" true; - print_endline "Enabled termination analysis" - let estimateComplexity factors file = let pathsEstimate = factors.loops + factors.controlFlowStatements / 90 in let operationEstimate = factors.instructions + (factors.expressions / 60) in @@ -536,14 +529,10 @@ let chooseConfig file = let options = [] in let options = if isActivated "congruence" then (congruenceOption factors file)::options else options in - (* Termination analysis uses apron in a different configuration. *) let options = if isActivated "octagon" && not (isTerminationTask ()) then (apronOctagonOption factors file)::options else options in let options = if isActivated "wideningThresholds" then (wideningOption factors file)::options else options in - List.iter (fun o -> o.activate ()) @@ chooseFromOptions (totalTarget - fileCompplexity) options; - - if isActivated "termination" && isTerminationTask () then - activateTerminationAnalysis () + List.iter (fun o -> o.activate ()) @@ chooseFromOptions (totalTarget - fileCompplexity) options let reset_lazy () = ResettableLazy.reset functionCallMaps From c8c49a56b60cc90de5f3a0fbed97ed2de8832db1 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 17 Nov 2023 15:35:41 +0100 Subject: [PATCH 1016/1312] Be more precise for `<<` of Intervals --- src/cdomains/intDomain.ml | 20 +++++++++++++++- .../39-signed-overflows/06-shiftleft.c | 23 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 tests/regression/39-signed-overflows/06-shiftleft.c diff --git a/src/cdomains/intDomain.ml b/src/cdomains/intDomain.ml index 3bc84ae676..49065b9cc5 100644 --- a/src/cdomains/intDomain.ml +++ b/src/cdomains/intDomain.ml @@ -610,6 +610,11 @@ module IntervalArith(Ints_t : IntOps.IntOps) = struct let x2y2 = (Ints_t.mul x2 y2) in (min4 x1y1 x1y2 x2y1 x2y2, max4 x1y1 x1y2 x2y1 x2y2) + let shiftleft (x1,x2) (y1,y2) = + let y1p = Ints_t.shift_left Ints_t.one y1 in + let y2p = Ints_t.shift_left Ints_t.one y2 in + mul (x1, x2) (y1p, y2p) + let div (x1, x2) (y1, y2) = let x1y1n = (Ints_t.div x1 y1) in let x1y2n = (Ints_t.div x1 y2) in @@ -851,7 +856,6 @@ struct let bitnot = bit1 (fun _ik -> Ints_t.bitnot) let shift_right = bitcomp (fun _ik x y -> Ints_t.shift_right x (Ints_t.to_int y)) - let shift_left = bitcomp (fun _ik x y -> Ints_t.shift_left x (Ints_t.to_int y)) let neg ?no_ov ik = function None -> (None,{underflow=false; overflow=false}) | Some x -> norm ik @@ Some (IArith.neg x) @@ -864,6 +868,20 @@ struct let mul ?no_ov = binary_op_with_norm IArith.mul let sub ?no_ov = binary_op_with_norm IArith.sub + let shift_left ik a b = + match is_bot a, is_bot b with + | true, true -> (bot_of ik,{underflow=false; overflow=false}) + | true, _ + | _ , true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show a) (show b))) + | _ -> + match a, maximal b, minimal b with + | Some a, Some bl, Some bu when (Ints_t.compare bl Ints_t.zero >= 0) -> + (try + let r = IArith.shiftleft a (Ints_t.to_int bl, Ints_t.to_int bu) in + norm ik @@ Some r + with Z.Overflow -> (top_of ik,{underflow=false; overflow=true})) + | _ -> (top_of ik,{underflow=true; overflow=true}) + let rem ik x y = match x, y with | None, None -> None | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) diff --git a/tests/regression/39-signed-overflows/06-shiftleft.c b/tests/regression/39-signed-overflows/06-shiftleft.c new file mode 100644 index 0000000000..987775c6f7 --- /dev/null +++ b/tests/regression/39-signed-overflows/06-shiftleft.c @@ -0,0 +1,23 @@ +// PARAM: --enable ana.int.interval +#include +int main() +{ + int r; + int zero_or_one = 0; + int top; + char c; + r = c << 1U; //NOWARN + + r = c << 128U; //WARN + r = r << 1U; //WARN + r = 8 << -2; //WARN + + if(top) { zero_or_one = 1; } + + r = 8 << zero_or_one; + + __goblint_check(r >= 8); + __goblint_check(r <= 16); + + return 0; +} From 566348216fff4b119559d907df9b8e2e005fa023 Mon Sep 17 00:00:00 2001 From: stilscher <66023521+stilscher@users.noreply.github.com> Date: Fri, 17 Nov 2023 16:14:36 +0100 Subject: [PATCH 1017/1312] improve values of parameters in abs call --- src/analyses/baseInvariant.ml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index 72e00efbb1..73a41bcea1 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -726,6 +726,8 @@ struct | `Lifted (Isless (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))) st | `Lifted (Islessequal (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Le, xFloat, yFloat, (typeOf xFloat))) st | `Lifted (Islessgreater (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (LOr, (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))), (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))), (TInt (IBool, [])))) st + | `Lifted (Abs xInt) -> + inv_exp (Int (ID.of_bool ik tv)) (BinOp (LAnd, (BinOp (Le, xInt, Lval x, TInt (IInt,[]))), (BinOp (Le, (UnOp (Neg, Lval x, (typeOf xInt))), xInt, TInt (IInt,[]))), (TInt (IInt, [])))) st | _ -> update_lval c x c' ID.pretty end | None -> update_lval c x c' ID.pretty From 28dca49ca7256db0af6415df2dfcf7345f502ef0 Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Fri, 17 Nov 2023 17:46:03 +0100 Subject: [PATCH 1018/1312] Add specification to activated settings for autotuner. --- conf/svcomp.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/conf/svcomp.json b/conf/svcomp.json index 107c59994c..cc6d1e303a 100644 --- a/conf/svcomp.json +++ b/conf/svcomp.json @@ -72,7 +72,8 @@ "wideningThresholds", "loopUnrollHeuristic", "memsafetySpecification", - "termination" + "termination", + "specification" ] } }, From a4adabd819b4e116a220976223efba85296fb833 Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Fri, 17 Nov 2023 17:52:31 +0100 Subject: [PATCH 1019/1312] Call Autotune.focusOnSpecification before preprocessing, as terminatino analysis needs to be activated before preprocessing to work. --- src/autoTune.ml | 7 +++++-- src/goblint.ml | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/autoTune.ml b/src/autoTune.ml index cc1af59940..4b959749a7 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -496,6 +496,9 @@ let isActivated a = get_bool "ana.autotune.enabled" && List.mem a @@ get_string_ let isTerminationTask () = List.mem Svcomp.Specification.Termination (Svcomp.Specification.of_option ()) +let specificationIsActivated () = + isActivated "specification" && get_string "ana.specification" <> "" + let chooseConfig file = let factors = collectFactors visitCilFileSameGlobals file in let fileCompplexity = estimateComplexity factors file in @@ -515,8 +518,8 @@ let chooseConfig file = if isActivated "mallocWrappers" then findMallocWrappers (); - if isActivated "specification" && get_string "ana.specification" <> "" then - focusOnSpecification (); + (* if specificationIsActivated () then + focusOnSpecification (); *) if isActivated "enums" && hasEnums file then set_bool "ana.int.enums" true; diff --git a/src/goblint.ml b/src/goblint.ml index 4ea3a3d242..6c5a2df33d 100644 --- a/src/goblint.ml +++ b/src/goblint.ml @@ -38,6 +38,8 @@ let main () = print_endline (GobUnix.localtime ()); print_endline GobSys.command_line; ); + (* When analyzing a termination specification, activate the termination analysis before pre-processing. *) + if get_bool "ana.autotune.enabled" && AutoTune.specificationIsActivated () then AutoTune.focusOnSpecification (); let file = lazy (Fun.protect ~finally:GoblintDir.finalize preprocess_parse_merge) in if get_bool "server.enabled" then ( let file = From 41ee060fc81f1719b7834f53cf6213c85df358ef Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Fri, 17 Nov 2023 18:37:45 +0100 Subject: [PATCH 1020/1312] Do loop unrolling for loops with boundedness check. --- src/util/loopUnrolling.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util/loopUnrolling.ml b/src/util/loopUnrolling.ml index 4ce8fc06b4..e1a8ad542b 100644 --- a/src/util/loopUnrolling.ml +++ b/src/util/loopUnrolling.ml @@ -320,6 +320,7 @@ class loopUnrollingCallVisitor = object | Unlock _ | ThreadCreate _ | Assert _ + | Bounded _ | ThreadJoin _ -> raise Found; | _ -> From 688cff1c28936295f3c084dd5d60f36e70dbd5a1 Mon Sep 17 00:00:00 2001 From: stilscher <66023521+stilscher@users.noreply.github.com> Date: Sat, 18 Nov 2023 11:26:38 +0100 Subject: [PATCH 1021/1312] trying to fix that value is not improved for parameter of abs call --- src/analyses/baseInvariant.ml | 71 +++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 29 deletions(-) diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index 73a41bcea1..b82ab88806 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -562,7 +562,7 @@ struct | TFloat (fk, _) -> fk | _ -> failwith "value which was expected to be a float is of different type?!" in - let rec inv_exp c_typed exp (st:D.t): D.t = + let rec inv_exp ?(query_special=true) c_typed exp (st:D.t) : D.t = (* trying to improve variables in an expression so it is bottom means dead code *) if VD.is_bot_value c_typed then contra st else @@ -578,12 +578,12 @@ struct | Some false -> ID.of_bool ikind false | _ -> ID.top_of ikind in - inv_exp (Int c') e st - | UnOp (Neg, e, _), Float c -> inv_exp (Float (unop_FD Neg c)) e st - | UnOp ((BNot|Neg) as op, e, _), Int c -> inv_exp (Int (unop_ID op c)) e st + inv_exp ~query_special (Int c') e st + | UnOp (Neg, e, _), Float c -> inv_exp ~query_special (Float (unop_FD Neg c)) e st + | UnOp ((BNot|Neg) as op, e, _), Int c -> inv_exp ~query_special (Int (unop_ID op c)) e st (* no equivalent for Float, as VD.is_safe_cast fails for all float types anyways *) | BinOp((Eq | Ne) as op, CastE (t1, e1), CastE (t2, e2), t), Int c when typeSig (Cilfacade.typeOf e1) = typeSig (Cilfacade.typeOf e2) && VD.is_safe_cast t1 (Cilfacade.typeOf e1) && VD.is_safe_cast t2 (Cilfacade.typeOf e2) -> - inv_exp (Int c) (BinOp (op, e1, e2, t)) st + inv_exp ~query_special (Int c) (BinOp (op, e1, e2, t)) st | BinOp (LOr, arg1, arg2, typ) as exp, Int c -> (* copied & modified from eval_rv_base... *) let (let*) = Option.bind in @@ -631,7 +631,7 @@ struct in let definite_ad = to_definite_ad vs in let c' = VD.Address definite_ad in - Some (inv_exp c' e st) + Some (inv_exp ~query_special c' e st) | Int i -> let ik = ID.ikind i in let module BISet = IntDomain.BISet in @@ -648,20 +648,20 @@ struct in let int_id = to_int_id vs in let c' = VD.Int int_id in - Some (inv_exp c' e st) + Some (inv_exp ~query_special c' e st) | _ -> None in begin match eqs_st with | Some st -> st | None when ID.to_bool c = Some true -> - begin match inv_exp (Int c) arg1 st with + begin match inv_exp ~query_special (Int c) arg1 st with | st1 -> - begin match inv_exp (Int c) arg2 st with + begin match inv_exp ~query_special (Int c) arg2 st with | st2 -> D.join st1 st2 | exception Analyses.Deadcode -> st1 end - | exception Analyses.Deadcode -> inv_exp (Int c) arg2 st (* Deadcode falls through *) + | exception Analyses.Deadcode -> inv_exp ~query_special (Int c) arg2 st (* Deadcode falls through *) end | None -> st (* TODO: not bothering to fall back, no other case can refine LOr anyway *) @@ -676,15 +676,15 @@ struct let ikres = Cilfacade.get_ikind_exp e in (* might be different from argument types, e.g. for LT, GT, EQ, ... *) let a', b' = inv_bin_int (a, b) ikind (c_int ikres) op in if M.tracing then M.tracel "inv" "binop: %a, c: %a, a': %a, b': %a\n" d_exp e ID.pretty (c_int ikind) ID.pretty a' ID.pretty b'; - let st' = inv_exp (Int a') e1 st in - let st'' = inv_exp (Int b') e2 st' in + let st' = inv_exp ~query_special (Int a') e1 st in + let st'' = inv_exp ~query_special (Int b') e2 st' in st'' | Float a, Float b -> let fkind = Cilfacade.get_fkind_exp e1 in (* both operands have the same type *) let a', b' = inv_bin_float (a, b) (c_float fkind) op in if M.tracing then M.tracel "inv" "binop: %a, c: %a, a': %a, b': %a\n" d_exp e FD.pretty (c_float fkind) FD.pretty a' FD.pretty b'; - let st' = inv_exp (Float a') e1 st in - let st'' = inv_exp (Float b') e2 st' in + let st' = inv_exp ~query_special (Float a') e1 st in + let st'' = inv_exp ~query_special (Float b') e2 st' in st'' (* Mixed Float and Int cases should never happen, as there are no binary operators with one float and one int parameter ?!*) | Int _, Float _ | Float _, Int _ -> failwith "ill-typed program"; @@ -712,22 +712,22 @@ struct begin match t with | TInt (ik, _) -> begin match x with - | ((Var v), offs) -> + | ((Var v), offs) when query_special -> if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs))); let tv_opt = ID.to_bool c in begin match tv_opt with | Some tv -> begin match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs)) with - | `Lifted (Isfinite xFloat) when tv -> inv_exp (Float (FD.finite (unroll_fk_of_exp xFloat))) xFloat st - | `Lifted (Isnan xFloat) when tv -> inv_exp (Float (FD.nan_of (unroll_fk_of_exp xFloat))) xFloat st + | `Lifted (Isfinite xFloat) when tv -> inv_exp ~query_special:false (Float (FD.finite (unroll_fk_of_exp xFloat))) xFloat st + | `Lifted (Isnan xFloat) when tv -> inv_exp ~query_special:false (Float (FD.nan_of (unroll_fk_of_exp xFloat))) xFloat st (* should be correct according to C99 standard*) - | `Lifted (Isgreater (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))) st - | `Lifted (Isgreaterequal (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Ge, xFloat, yFloat, (typeOf xFloat))) st - | `Lifted (Isless (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))) st - | `Lifted (Islessequal (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Le, xFloat, yFloat, (typeOf xFloat))) st - | `Lifted (Islessgreater (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (LOr, (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))), (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))), (TInt (IBool, [])))) st + | `Lifted (Isgreater (xFloat, yFloat)) -> inv_exp ~query_special:false (Int (ID.of_bool ik tv)) (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))) st + | `Lifted (Isgreaterequal (xFloat, yFloat)) -> inv_exp ~query_special:false (Int (ID.of_bool ik tv)) (BinOp (Ge, xFloat, yFloat, (typeOf xFloat))) st + | `Lifted (Isless (xFloat, yFloat)) -> inv_exp ~query_special:false (Int (ID.of_bool ik tv)) (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))) st + | `Lifted (Islessequal (xFloat, yFloat)) -> inv_exp ~query_special:false (Int (ID.of_bool ik tv)) (BinOp (Le, xFloat, yFloat, (typeOf xFloat))) st + | `Lifted (Islessgreater (xFloat, yFloat)) -> inv_exp ~query_special:false (Int (ID.of_bool ik tv)) (BinOp (LOr, (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))), (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))), (TInt (IBool, [])))) st | `Lifted (Abs xInt) -> - inv_exp (Int (ID.of_bool ik tv)) (BinOp (LAnd, (BinOp (Le, xInt, Lval x, TInt (IInt,[]))), (BinOp (Le, (UnOp (Neg, Lval x, (typeOf xInt))), xInt, TInt (IInt,[]))), (TInt (IInt, [])))) st + inv_exp ~query_special:false (Int (ID.of_bool ik tv)) (BinOp (LAnd, (BinOp (Le, xInt, Lval x, TInt (IInt,[]))), (BinOp (Le, (UnOp (Neg, Lval x, (typeOf xInt))), xInt, TInt (IInt,[]))), (TInt (IInt, [])))) st | _ -> update_lval c x c' ID.pretty end | None -> update_lval c x c' ID.pretty @@ -749,17 +749,17 @@ struct begin match t with | TFloat (fk, _) -> begin match x with - | ((Var v), offs) -> + | ((Var v), offs) when query_special -> if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs))); begin match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs)) with - | `Lifted (Ceil (ret_fk, xFloat)) -> inv_exp (Float (FD.inv_ceil (FD.cast_to ret_fk c))) xFloat st - | `Lifted (Floor (ret_fk, xFloat)) -> inv_exp (Float (FD.inv_floor (FD.cast_to ret_fk c))) xFloat st + | `Lifted (Ceil (ret_fk, xFloat)) -> inv_exp ~query_special:false (Float (FD.inv_ceil (FD.cast_to ret_fk c))) xFloat st + | `Lifted (Floor (ret_fk, xFloat)) -> inv_exp ~query_special:false (Float (FD.inv_floor (FD.cast_to ret_fk c))) xFloat st | `Lifted (Fabs (ret_fk, xFloat)) -> let inv = FD.inv_fabs (FD.cast_to ret_fk c) in if FD.is_bot inv then raise Analyses.Deadcode else - inv_exp (Float inv) xFloat st + inv_exp ~query_special:false (Float inv) xFloat st | _ -> update_lval c x c' FD.pretty end | _ -> update_lval c x c' FD.pretty @@ -779,7 +779,7 @@ struct | TFloat (FLongDouble as fk, _), FDouble | TFloat (fk, _), FLongDouble | TFloat (FDouble as fk, _), FDouble - | TFloat (FFloat as fk, _), FFloat -> inv_exp (Float (FD.cast_to fk c)) e st + | TFloat (FFloat as fk, _), FFloat -> inv_exp ~query_special (Float (FD.cast_to fk c)) e st | _ -> fallback ("CastE: incompatible types") st) | CastE ((TInt (ik, _)) as t, e), Int c | CastE ((TEnum ({ekind = ik; _ }, _)) as t, e), Int c -> (* Can only meet the t part of an Lval in e with c (unless we meet with all overflow possibilities)! Since there is no good way to do this, we only continue if e has no values outside of t. *) @@ -792,7 +792,20 @@ struct (* let c' = ID.cast_to ik_e c in *) let c' = ID.cast_to ik_e (ID.meet c (ID.cast_to ik (ID.top_of ik_e))) in (* TODO: cast without overflow, is this right for normal invariant? *) if M.tracing then M.tracel "inv" "cast: %a from %a to %a: i = %a; cast c = %a to %a = %a\n" d_exp e d_ikind ik_e d_ikind ik ID.pretty i ID.pretty c d_ikind ik_e ID.pretty c'; - inv_exp (Int c') e st + inv_exp ~query_special (Int c') e st + | x -> fallback (GobPretty.sprintf "CastE: e did evaluate to Int, but the type did not match %a" CilType.Typ.pretty t) st + else + fallback (GobPretty.sprintf "CastE: %a evaluates to %a which is bigger than the type it is cast to which is %a" d_plainexp e ID.pretty i CilType.Typ.pretty t) st + | Float i -> + let i = FD.to_int ik i in + if ID.leq i (ID.cast_to ik i) then + match unrollType (Cilfacade.typeOf e) with + | TInt(ik_e, _) + | TEnum ({ekind = ik_e; _ }, _) -> + (* let c' = ID.cast_to ik_e c in *) + let c' = ID.cast_to ik_e (ID.meet c (ID.cast_to ik (ID.top_of ik_e))) in (* TODO: cast without overflow, is this right for normal invariant? *) + if M.tracing then M.tracel "inv" "cast: %a from %a to %a: i = %a; cast c = %a to %a = %a\n" d_exp e d_ikind ik_e d_ikind ik ID.pretty i ID.pretty c d_ikind ik_e ID.pretty c'; + inv_exp ~query_special (Int c') e st | x -> fallback (GobPretty.sprintf "CastE: e did evaluate to Int, but the type did not match %a" CilType.Typ.pretty t) st else fallback (GobPretty.sprintf "CastE: %a evaluates to %a which is bigger than the type it is cast to which is %a" d_plainexp e ID.pretty i CilType.Typ.pretty t) st From 88957a8005c2eff0d51c8bd1402c9d91e2e2a84e Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sat, 18 Nov 2023 11:32:52 +0100 Subject: [PATCH 1022/1312] Add test which can be run in isolation --- tests/regression/39-signed-overflows/06-abs.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 tests/regression/39-signed-overflows/06-abs.c diff --git a/tests/regression/39-signed-overflows/06-abs.c b/tests/regression/39-signed-overflows/06-abs.c new file mode 100644 index 0000000000..b08a0f2209 --- /dev/null +++ b/tests/regression/39-signed-overflows/06-abs.c @@ -0,0 +1,15 @@ +//PARAM: --enable ana.int.interval --set ana.activated[+] tmpSpecial +#include +int main() { + int data; + if (data > (-0x7fffffff - 1)) + { + if (abs(data) < 100) + { + int result = data * data; + } + + int result = data * data; + } + return 8; +} \ No newline at end of file From 2ea8db4b64f1a55e25b2ae226d5d82815766a80c Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sat, 18 Nov 2023 12:21:30 +0100 Subject: [PATCH 1023/1312] Wir sind alle Pfuscher vor dem HERRN --- src/analyses/base.ml | 46 +++++++++++++++---- src/analyses/baseInvariant.ml | 6 +-- tests/regression/39-signed-overflows/06-abs.c | 2 - .../39-signed-overflows/07-abs-sqrt.c | 10 ++++ 4 files changed, 50 insertions(+), 14 deletions(-) create mode 100644 tests/regression/39-signed-overflows/07-abs-sqrt.c diff --git a/src/analyses/base.ml b/src/analyses/base.ml index f25f0a9693..8b9b232913 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1753,15 +1753,43 @@ struct let branch ctx (exp:exp) (tv:bool) : store = let valu = eval_rv (Analyses.ask_of_ctx ctx) ctx.global ctx.local exp in let refine () = - let res = invariant ctx (Analyses.ask_of_ctx ctx) ctx.global ctx.local exp tv in - if M.tracing then M.tracec "branch" "EqualSet result for expression %a is %a\n" d_exp exp Queries.ES.pretty (ctx.ask (Queries.EqualSet exp)); - if M.tracing then M.tracec "branch" "CondVars result for expression %a is %a\n" d_exp exp Queries.ES.pretty (ctx.ask (Queries.CondVars exp)); - if M.tracing then M.traceu "branch" "Invariant enforced!\n"; - match ctx.ask (Queries.CondVars exp) with - | s when Queries.ES.cardinal s = 1 -> - let e = Queries.ES.choose s in - invariant ctx (Analyses.ask_of_ctx ctx) ctx.global res e tv - | _ -> res + let refine0 = + let res = invariant ctx (Analyses.ask_of_ctx ctx) ctx.global ctx.local exp tv in + if M.tracing then M.tracec "branch" "EqualSet result for expression %a is %a\n" d_exp exp Queries.ES.pretty (ctx.ask (Queries.EqualSet exp)); + if M.tracing then M.tracec "branch" "CondVars result for expression %a is %a\n" d_exp exp Queries.ES.pretty (ctx.ask (Queries.CondVars exp)); + if M.tracing then M.traceu "branch" "Invariant enforced!\n"; + match ctx.ask (Queries.CondVars exp) with + | s when Queries.ES.cardinal s = 1 -> + let e = Queries.ES.choose s in + invariant ctx (Analyses.ask_of_ctx ctx) ctx.global res e tv + | _ -> res + in + match exp with + | BinOp (Lt, CastE(t,Lval (Var v, NoOffset)), e,_) when tv -> + (match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil NoOffset)) with + | `Lifted (Abs arg) -> + (* e.g. |arg| < 40 *) + let v = eval_rv (Analyses.ask_of_ctx ctx) ctx.global ctx.local e in + (* arg <= e (arg <= 40) *) + let le = BinOp (Le, CastE(t,arg), e, intType) in + (* arg >= -e (arg >= -40) *) + let gt = BinOp(Ge, CastE(t,arg), UnOp (Neg, e, Cilfacade.typeOf e), intType) in + let one = invariant ctx (Analyses.ask_of_ctx ctx) ctx.global refine0 le tv in + invariant ctx (Analyses.ask_of_ctx ctx) ctx.global one gt tv + | _ -> refine0) + | BinOp (Lt, Lval (Var v, NoOffset), e, _) when tv -> + (match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil NoOffset)) with + | `Lifted (Abs arg) -> + (* e.g. |arg| < 40 *) + let v = eval_rv (Analyses.ask_of_ctx ctx) ctx.global ctx.local e in + (* arg <= e (arg <= 40) *) + let le = BinOp (Le, arg, e, intType) in + (* arg >= -e (arg >= -40) *) + let gt = BinOp(Ge, arg, UnOp (Neg, e, Cilfacade.typeOf e), intType) in + let one = invariant ctx (Analyses.ask_of_ctx ctx) ctx.global refine0 le tv in + invariant ctx (Analyses.ask_of_ctx ctx) ctx.global one gt tv + | _ -> refine0) + | _ -> refine0 in if M.tracing then M.traceli "branch" ~subsys:["invariant"] "Evaluating branch for expression %a with value %a\n" d_exp exp VD.pretty valu; (* First we want to see, if we can determine a dead branch: *) diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index b82ab88806..397bd1623e 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -712,7 +712,7 @@ struct begin match t with | TInt (ik, _) -> begin match x with - | ((Var v), offs) when query_special -> + | ((Var v), offs) -> if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs))); let tv_opt = ID.to_bool c in begin match tv_opt with @@ -730,7 +730,7 @@ struct inv_exp ~query_special:false (Int (ID.of_bool ik tv)) (BinOp (LAnd, (BinOp (Le, xInt, Lval x, TInt (IInt,[]))), (BinOp (Le, (UnOp (Neg, Lval x, (typeOf xInt))), xInt, TInt (IInt,[]))), (TInt (IInt, [])))) st | _ -> update_lval c x c' ID.pretty end - | None -> update_lval c x c' ID.pretty + | _ -> update_lval c x c' ID.pretty end | _ -> update_lval c x c' ID.pretty end @@ -749,7 +749,7 @@ struct begin match t with | TFloat (fk, _) -> begin match x with - | ((Var v), offs) when query_special -> + | ((Var v), offs) -> if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs))); begin match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs)) with | `Lifted (Ceil (ret_fk, xFloat)) -> inv_exp ~query_special:false (Float (FD.inv_ceil (FD.cast_to ret_fk c))) xFloat st diff --git a/tests/regression/39-signed-overflows/06-abs.c b/tests/regression/39-signed-overflows/06-abs.c index b08a0f2209..e87e25d9fe 100644 --- a/tests/regression/39-signed-overflows/06-abs.c +++ b/tests/regression/39-signed-overflows/06-abs.c @@ -8,8 +8,6 @@ int main() { { int result = data * data; } - - int result = data * data; } return 8; } \ No newline at end of file diff --git a/tests/regression/39-signed-overflows/07-abs-sqrt.c b/tests/regression/39-signed-overflows/07-abs-sqrt.c new file mode 100644 index 0000000000..0f8ce396f2 --- /dev/null +++ b/tests/regression/39-signed-overflows/07-abs-sqrt.c @@ -0,0 +1,10 @@ +//PARAM: --enable ana.int.interval --enable ana.float.interval --set ana.activated[+] tmpSpecial +#include +int main() { + int data; + if (data > (-0x7fffffff - 1) && abs(data) < (long)sqrt((double)0x7fffffff)) + { + int result = data * data; + } + return 8; +} \ No newline at end of file From 379733825fce688cec97a63a2fa32c951e7d2c10 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sat, 18 Nov 2023 12:28:22 +0100 Subject: [PATCH 1024/1312] Reset baseInvariant to master --- src/analyses/baseInvariant.ml | 69 ++++++++++++++--------------------- 1 file changed, 27 insertions(+), 42 deletions(-) diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index 397bd1623e..72e00efbb1 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -562,7 +562,7 @@ struct | TFloat (fk, _) -> fk | _ -> failwith "value which was expected to be a float is of different type?!" in - let rec inv_exp ?(query_special=true) c_typed exp (st:D.t) : D.t = + let rec inv_exp c_typed exp (st:D.t): D.t = (* trying to improve variables in an expression so it is bottom means dead code *) if VD.is_bot_value c_typed then contra st else @@ -578,12 +578,12 @@ struct | Some false -> ID.of_bool ikind false | _ -> ID.top_of ikind in - inv_exp ~query_special (Int c') e st - | UnOp (Neg, e, _), Float c -> inv_exp ~query_special (Float (unop_FD Neg c)) e st - | UnOp ((BNot|Neg) as op, e, _), Int c -> inv_exp ~query_special (Int (unop_ID op c)) e st + inv_exp (Int c') e st + | UnOp (Neg, e, _), Float c -> inv_exp (Float (unop_FD Neg c)) e st + | UnOp ((BNot|Neg) as op, e, _), Int c -> inv_exp (Int (unop_ID op c)) e st (* no equivalent for Float, as VD.is_safe_cast fails for all float types anyways *) | BinOp((Eq | Ne) as op, CastE (t1, e1), CastE (t2, e2), t), Int c when typeSig (Cilfacade.typeOf e1) = typeSig (Cilfacade.typeOf e2) && VD.is_safe_cast t1 (Cilfacade.typeOf e1) && VD.is_safe_cast t2 (Cilfacade.typeOf e2) -> - inv_exp ~query_special (Int c) (BinOp (op, e1, e2, t)) st + inv_exp (Int c) (BinOp (op, e1, e2, t)) st | BinOp (LOr, arg1, arg2, typ) as exp, Int c -> (* copied & modified from eval_rv_base... *) let (let*) = Option.bind in @@ -631,7 +631,7 @@ struct in let definite_ad = to_definite_ad vs in let c' = VD.Address definite_ad in - Some (inv_exp ~query_special c' e st) + Some (inv_exp c' e st) | Int i -> let ik = ID.ikind i in let module BISet = IntDomain.BISet in @@ -648,20 +648,20 @@ struct in let int_id = to_int_id vs in let c' = VD.Int int_id in - Some (inv_exp ~query_special c' e st) + Some (inv_exp c' e st) | _ -> None in begin match eqs_st with | Some st -> st | None when ID.to_bool c = Some true -> - begin match inv_exp ~query_special (Int c) arg1 st with + begin match inv_exp (Int c) arg1 st with | st1 -> - begin match inv_exp ~query_special (Int c) arg2 st with + begin match inv_exp (Int c) arg2 st with | st2 -> D.join st1 st2 | exception Analyses.Deadcode -> st1 end - | exception Analyses.Deadcode -> inv_exp ~query_special (Int c) arg2 st (* Deadcode falls through *) + | exception Analyses.Deadcode -> inv_exp (Int c) arg2 st (* Deadcode falls through *) end | None -> st (* TODO: not bothering to fall back, no other case can refine LOr anyway *) @@ -676,15 +676,15 @@ struct let ikres = Cilfacade.get_ikind_exp e in (* might be different from argument types, e.g. for LT, GT, EQ, ... *) let a', b' = inv_bin_int (a, b) ikind (c_int ikres) op in if M.tracing then M.tracel "inv" "binop: %a, c: %a, a': %a, b': %a\n" d_exp e ID.pretty (c_int ikind) ID.pretty a' ID.pretty b'; - let st' = inv_exp ~query_special (Int a') e1 st in - let st'' = inv_exp ~query_special (Int b') e2 st' in + let st' = inv_exp (Int a') e1 st in + let st'' = inv_exp (Int b') e2 st' in st'' | Float a, Float b -> let fkind = Cilfacade.get_fkind_exp e1 in (* both operands have the same type *) let a', b' = inv_bin_float (a, b) (c_float fkind) op in if M.tracing then M.tracel "inv" "binop: %a, c: %a, a': %a, b': %a\n" d_exp e FD.pretty (c_float fkind) FD.pretty a' FD.pretty b'; - let st' = inv_exp ~query_special (Float a') e1 st in - let st'' = inv_exp ~query_special (Float b') e2 st' in + let st' = inv_exp (Float a') e1 st in + let st'' = inv_exp (Float b') e2 st' in st'' (* Mixed Float and Int cases should never happen, as there are no binary operators with one float and one int parameter ?!*) | Int _, Float _ | Float _, Int _ -> failwith "ill-typed program"; @@ -718,19 +718,17 @@ struct begin match tv_opt with | Some tv -> begin match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs)) with - | `Lifted (Isfinite xFloat) when tv -> inv_exp ~query_special:false (Float (FD.finite (unroll_fk_of_exp xFloat))) xFloat st - | `Lifted (Isnan xFloat) when tv -> inv_exp ~query_special:false (Float (FD.nan_of (unroll_fk_of_exp xFloat))) xFloat st + | `Lifted (Isfinite xFloat) when tv -> inv_exp (Float (FD.finite (unroll_fk_of_exp xFloat))) xFloat st + | `Lifted (Isnan xFloat) when tv -> inv_exp (Float (FD.nan_of (unroll_fk_of_exp xFloat))) xFloat st (* should be correct according to C99 standard*) - | `Lifted (Isgreater (xFloat, yFloat)) -> inv_exp ~query_special:false (Int (ID.of_bool ik tv)) (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))) st - | `Lifted (Isgreaterequal (xFloat, yFloat)) -> inv_exp ~query_special:false (Int (ID.of_bool ik tv)) (BinOp (Ge, xFloat, yFloat, (typeOf xFloat))) st - | `Lifted (Isless (xFloat, yFloat)) -> inv_exp ~query_special:false (Int (ID.of_bool ik tv)) (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))) st - | `Lifted (Islessequal (xFloat, yFloat)) -> inv_exp ~query_special:false (Int (ID.of_bool ik tv)) (BinOp (Le, xFloat, yFloat, (typeOf xFloat))) st - | `Lifted (Islessgreater (xFloat, yFloat)) -> inv_exp ~query_special:false (Int (ID.of_bool ik tv)) (BinOp (LOr, (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))), (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))), (TInt (IBool, [])))) st - | `Lifted (Abs xInt) -> - inv_exp ~query_special:false (Int (ID.of_bool ik tv)) (BinOp (LAnd, (BinOp (Le, xInt, Lval x, TInt (IInt,[]))), (BinOp (Le, (UnOp (Neg, Lval x, (typeOf xInt))), xInt, TInt (IInt,[]))), (TInt (IInt, [])))) st + | `Lifted (Isgreater (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))) st + | `Lifted (Isgreaterequal (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Ge, xFloat, yFloat, (typeOf xFloat))) st + | `Lifted (Isless (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))) st + | `Lifted (Islessequal (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Le, xFloat, yFloat, (typeOf xFloat))) st + | `Lifted (Islessgreater (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (LOr, (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))), (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))), (TInt (IBool, [])))) st | _ -> update_lval c x c' ID.pretty end - | _ -> update_lval c x c' ID.pretty + | None -> update_lval c x c' ID.pretty end | _ -> update_lval c x c' ID.pretty end @@ -752,14 +750,14 @@ struct | ((Var v), offs) -> if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs))); begin match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs)) with - | `Lifted (Ceil (ret_fk, xFloat)) -> inv_exp ~query_special:false (Float (FD.inv_ceil (FD.cast_to ret_fk c))) xFloat st - | `Lifted (Floor (ret_fk, xFloat)) -> inv_exp ~query_special:false (Float (FD.inv_floor (FD.cast_to ret_fk c))) xFloat st + | `Lifted (Ceil (ret_fk, xFloat)) -> inv_exp (Float (FD.inv_ceil (FD.cast_to ret_fk c))) xFloat st + | `Lifted (Floor (ret_fk, xFloat)) -> inv_exp (Float (FD.inv_floor (FD.cast_to ret_fk c))) xFloat st | `Lifted (Fabs (ret_fk, xFloat)) -> let inv = FD.inv_fabs (FD.cast_to ret_fk c) in if FD.is_bot inv then raise Analyses.Deadcode else - inv_exp ~query_special:false (Float inv) xFloat st + inv_exp (Float inv) xFloat st | _ -> update_lval c x c' FD.pretty end | _ -> update_lval c x c' FD.pretty @@ -779,7 +777,7 @@ struct | TFloat (FLongDouble as fk, _), FDouble | TFloat (fk, _), FLongDouble | TFloat (FDouble as fk, _), FDouble - | TFloat (FFloat as fk, _), FFloat -> inv_exp ~query_special (Float (FD.cast_to fk c)) e st + | TFloat (FFloat as fk, _), FFloat -> inv_exp (Float (FD.cast_to fk c)) e st | _ -> fallback ("CastE: incompatible types") st) | CastE ((TInt (ik, _)) as t, e), Int c | CastE ((TEnum ({ekind = ik; _ }, _)) as t, e), Int c -> (* Can only meet the t part of an Lval in e with c (unless we meet with all overflow possibilities)! Since there is no good way to do this, we only continue if e has no values outside of t. *) @@ -792,20 +790,7 @@ struct (* let c' = ID.cast_to ik_e c in *) let c' = ID.cast_to ik_e (ID.meet c (ID.cast_to ik (ID.top_of ik_e))) in (* TODO: cast without overflow, is this right for normal invariant? *) if M.tracing then M.tracel "inv" "cast: %a from %a to %a: i = %a; cast c = %a to %a = %a\n" d_exp e d_ikind ik_e d_ikind ik ID.pretty i ID.pretty c d_ikind ik_e ID.pretty c'; - inv_exp ~query_special (Int c') e st - | x -> fallback (GobPretty.sprintf "CastE: e did evaluate to Int, but the type did not match %a" CilType.Typ.pretty t) st - else - fallback (GobPretty.sprintf "CastE: %a evaluates to %a which is bigger than the type it is cast to which is %a" d_plainexp e ID.pretty i CilType.Typ.pretty t) st - | Float i -> - let i = FD.to_int ik i in - if ID.leq i (ID.cast_to ik i) then - match unrollType (Cilfacade.typeOf e) with - | TInt(ik_e, _) - | TEnum ({ekind = ik_e; _ }, _) -> - (* let c' = ID.cast_to ik_e c in *) - let c' = ID.cast_to ik_e (ID.meet c (ID.cast_to ik (ID.top_of ik_e))) in (* TODO: cast without overflow, is this right for normal invariant? *) - if M.tracing then M.tracel "inv" "cast: %a from %a to %a: i = %a; cast c = %a to %a = %a\n" d_exp e d_ikind ik_e d_ikind ik ID.pretty i ID.pretty c d_ikind ik_e ID.pretty c'; - inv_exp ~query_special (Int c') e st + inv_exp (Int c') e st | x -> fallback (GobPretty.sprintf "CastE: e did evaluate to Int, but the type did not match %a" CilType.Typ.pretty t) st else fallback (GobPretty.sprintf "CastE: %a evaluates to %a which is bigger than the type it is cast to which is %a" d_plainexp e ID.pretty i CilType.Typ.pretty t) st From 6895ac0cf305747f617d8b4c42f191484f6bc072 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sat, 18 Nov 2023 12:41:37 +0100 Subject: [PATCH 1025/1312] Make bodge a bit nicer --- src/analyses/base.ml | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 8b9b232913..8497f38615 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1764,30 +1764,26 @@ struct invariant ctx (Analyses.ask_of_ctx ctx) ctx.global res e tv | _ -> res in + (* bodge for abs(...); To be removed once we have a clean solution *) + let refineAbs absargexp valexp = + (* e.g. |arg| < 40 *) + (* arg <= e (arg <= 40) *) + let le = BinOp (Le, absargexp, valexp, intType) in + (* arg >= -e (arg >= -40) *) + let gt = BinOp(Ge, absargexp, UnOp (Neg, valexp, Cilfacade.typeOf valexp), intType) in + let one = invariant ctx (Analyses.ask_of_ctx ctx) ctx.global refine0 le tv in + invariant ctx (Analyses.ask_of_ctx ctx) ctx.global one gt tv + in match exp with - | BinOp (Lt, CastE(t,Lval (Var v, NoOffset)), e,_) when tv -> + | BinOp (Lt, CastE(t, Lval (Var v, NoOffset)), e,_) when tv -> (match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil NoOffset)) with | `Lifted (Abs arg) -> - (* e.g. |arg| < 40 *) - let v = eval_rv (Analyses.ask_of_ctx ctx) ctx.global ctx.local e in - (* arg <= e (arg <= 40) *) - let le = BinOp (Le, CastE(t,arg), e, intType) in - (* arg >= -e (arg >= -40) *) - let gt = BinOp(Ge, CastE(t,arg), UnOp (Neg, e, Cilfacade.typeOf e), intType) in - let one = invariant ctx (Analyses.ask_of_ctx ctx) ctx.global refine0 le tv in - invariant ctx (Analyses.ask_of_ctx ctx) ctx.global one gt tv + refineAbs (CastE (t, arg)) e | _ -> refine0) | BinOp (Lt, Lval (Var v, NoOffset), e, _) when tv -> (match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil NoOffset)) with | `Lifted (Abs arg) -> - (* e.g. |arg| < 40 *) - let v = eval_rv (Analyses.ask_of_ctx ctx) ctx.global ctx.local e in - (* arg <= e (arg <= 40) *) - let le = BinOp (Le, arg, e, intType) in - (* arg >= -e (arg >= -40) *) - let gt = BinOp(Ge, arg, UnOp (Neg, e, Cilfacade.typeOf e), intType) in - let one = invariant ctx (Analyses.ask_of_ctx ctx) ctx.global refine0 le tv in - invariant ctx (Analyses.ask_of_ctx ctx) ctx.global one gt tv + refineAbs arg e | _ -> refine0) | _ -> refine0 in From ba6726a4515ca4873c1abb87b58f7d9a7f535da8 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sat, 18 Nov 2023 12:50:57 +0100 Subject: [PATCH 1026/1312] Handle <= as well --- src/analyses/base.ml | 17 +++++++++-------- tests/regression/39-signed-overflows/06-abs.c | 11 ++++++++++- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 8497f38615..4911b0d033 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1765,25 +1765,26 @@ struct | _ -> res in (* bodge for abs(...); To be removed once we have a clean solution *) - let refineAbs absargexp valexp = - (* e.g. |arg| < 40 *) + let refineAbs op absargexp valexp = + let flip op = match op with | Le -> Ge | Lt -> Gt | _ -> failwith "impossible" in + (* e.g. |arg| <= 40 *) (* arg <= e (arg <= 40) *) - let le = BinOp (Le, absargexp, valexp, intType) in + let le = BinOp (op, absargexp, valexp, intType) in (* arg >= -e (arg >= -40) *) - let gt = BinOp(Ge, absargexp, UnOp (Neg, valexp, Cilfacade.typeOf valexp), intType) in + let gt = BinOp(flip op, absargexp, UnOp (Neg, valexp, Cilfacade.typeOf valexp), intType) in let one = invariant ctx (Analyses.ask_of_ctx ctx) ctx.global refine0 le tv in invariant ctx (Analyses.ask_of_ctx ctx) ctx.global one gt tv in match exp with - | BinOp (Lt, CastE(t, Lval (Var v, NoOffset)), e,_) when tv -> + | BinOp ((Lt|Le) as op, CastE(t, Lval (Var v, NoOffset)), e,_) when tv -> (match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil NoOffset)) with | `Lifted (Abs arg) -> - refineAbs (CastE (t, arg)) e + refineAbs op (CastE (t, arg)) e | _ -> refine0) - | BinOp (Lt, Lval (Var v, NoOffset), e, _) when tv -> + | BinOp ((Lt|Le) as op, Lval (Var v, NoOffset), e, _) when tv -> (match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil NoOffset)) with | `Lifted (Abs arg) -> - refineAbs arg e + refineAbs op arg e | _ -> refine0) | _ -> refine0 in diff --git a/tests/regression/39-signed-overflows/06-abs.c b/tests/regression/39-signed-overflows/06-abs.c index e87e25d9fe..e56cc9ff7d 100644 --- a/tests/regression/39-signed-overflows/06-abs.c +++ b/tests/regression/39-signed-overflows/06-abs.c @@ -6,7 +6,16 @@ int main() { { if (abs(data) < 100) { - int result = data * data; + __goblint_check(data < 100); + __goblint_check(-100 < data); + int result = data * data; //NOWARN + } + + if(abs(data) <= 100) + { + __goblint_check(data <= 100); + __goblint_check(-100 <= data); + int result = data * data; //NOWARN } } return 8; From 8ea9ffa726f13f0d764cf2dbd7ee6a1d18270573 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sat, 18 Nov 2023 12:51:32 +0100 Subject: [PATCH 1027/1312] 39/07: Add NOWARN annotation --- tests/regression/39-signed-overflows/07-abs-sqrt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression/39-signed-overflows/07-abs-sqrt.c b/tests/regression/39-signed-overflows/07-abs-sqrt.c index 0f8ce396f2..13ed863e51 100644 --- a/tests/regression/39-signed-overflows/07-abs-sqrt.c +++ b/tests/regression/39-signed-overflows/07-abs-sqrt.c @@ -4,7 +4,7 @@ int main() { int data; if (data > (-0x7fffffff - 1) && abs(data) < (long)sqrt((double)0x7fffffff)) { - int result = data * data; + int result = data * data; //NOWARN } return 8; } \ No newline at end of file From 7289ec341760ab06ce518c7520b076ca50688bb3 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 18 Nov 2023 14:38:13 +0100 Subject: [PATCH 1028/1312] Use solely local state for multi-threaded valid-memcleanup --- src/analyses/memLeak.ml | 61 ++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 34 deletions(-) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index c64bb95697..64fe7ab957 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -6,6 +6,7 @@ open MessageCategory open AnalysisStateUtil module ToppedVarInfoSet = SetDomain.ToppedSet(CilType.Varinfo)(struct let topname = "All Heap Variables" end) +module ThreadsToHeapVarsMap = MapDomain.MapBot(ThreadIdDomain.Thread)(ToppedVarInfoSet) module WasMallocCalled = BoolDomain.MustBool module Spec : Analyses.MCPSpec = struct @@ -13,16 +14,9 @@ struct let name () = "memLeak" - (* module D = ToppedVarInfoSet *) - module D = Lattice.Prod(ToppedVarInfoSet)(WasMallocCalled) + module D = Lattice.Prod(ThreadsToHeapVarsMap)(WasMallocCalled) module C = D module P = IdentityP (D) - module G = ToppedVarInfoSet - module V = - struct - include ThreadIdDomain.Thread - include StdV - end let context _ d = d @@ -35,21 +29,18 @@ struct M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Program aborted while running in multi-threaded mode. A memory leak might occur" ) - (* If [is_return] is set to [true], then a thread return occurred, else a thread join *) + (* If [is_return] is set to [true], then a thread return occurred, else a thread exit *) let warn_for_thread_return_or_exit current_thread ctx is_return = - let global_state = ctx.global current_thread in - if not (G.is_empty global_state) then ( + let state = ctx.local in + let heap_vars_of_curr_tid = ThreadsToHeapVarsMap.find current_thread (fst state) in + if not (ToppedVarInfoSet.is_empty heap_vars_of_curr_tid) then ( set_mem_safety_flag InvalidMemcleanup; - M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory may be leaked at thread %s" (if is_return then "return" else "join") + M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory may be leaked at thread %s for thread %a" (if is_return then "return" else "exit") ThreadIdDomain.Thread.pretty current_thread ) - (* if not (ToppedVarInfoSet.is_empty (fst state)) && snd state then ( - set_mem_safety_flag InvalidMemcleanup; - M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory may be leaked at thread %s" (if is_return then "return" else "join") - ) *) let check_for_mem_leak ?(assert_exp_imprecise = false) ?(exp = None) ctx = let state = ctx.local in - if not (ToppedVarInfoSet.is_empty (fst state)) then + if not (ThreadsToHeapVarsMap.for_all (fun tid heap_vars -> ToppedVarInfoSet.is_empty heap_vars) (fst state)) then match assert_exp_imprecise, exp with | true, Some exp -> set_mem_safety_flag InvalidMemTrack; @@ -58,14 +49,15 @@ struct | _ -> set_mem_safety_flag InvalidMemTrack; set_mem_safety_flag InvalidMemcleanup; - M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory leak detected for heap variables: %a" D.pretty state + M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory leak detected for heap variables" (* TRANSFER FUNCTIONS *) let return ctx (exp:exp option) (f:fundec) : D.t = (* Check for a valid-memcleanup violation in a multi-threaded setting *) if (ctx.ask (Queries.MayBeThreadReturn)) then ( match ctx.ask (Queries.CurrentThreadId) with - | `Lifted tid -> warn_for_thread_return_or_exit tid ctx true + | `Lifted tid -> + warn_for_thread_return_or_exit tid ctx true | _ -> () ); (* Returning from "main" is one possible program exit => need to check for memory leaks *) @@ -83,34 +75,34 @@ struct | `Lifted var -> begin match ctx.ask (Queries.CurrentThreadId) with | `Lifted tid -> - let current_globals = ctx.global tid in - let globals_to_side_effect = G.add var current_globals in - ctx.sideg tid globals_to_side_effect; - | _ -> () - end; - (ToppedVarInfoSet.add var (fst state), true) + ((ThreadsToHeapVarsMap.add tid (ToppedVarInfoSet.singleton var) (fst state)), true) + | _ -> (fst state, true) + end | _ -> (fst state, true) end | Free ptr -> begin match ctx.ask (Queries.MayPointTo ptr) with - | ad when not (Queries.AD.is_top ad) && Queries.AD.cardinal ad = 1 -> + (* TODO: The cardinality of 1 seems to lead to the situation where only free() calls in main() are detected here (affects 76/08 and 76/09) *) + (* | ad when not (Queries.AD.is_top ad) && Queries.AD.cardinal ad = 1 -> *) + | ad when not (Queries.AD.is_top ad) -> (* Note: Need to always set "ana.malloc.unique_address_count" to a value > 0 *) begin match Queries.AD.choose ad with | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsAllocVar v) && ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsMultiple v) -> begin match ctx.ask (Queries.CurrentThreadId) with | `Lifted tid -> - let current_globals = ctx.global tid in - let globals_to_side_effect = G.remove v current_globals in - ctx.sideg tid globals_to_side_effect - | _ -> () - end; - (ToppedVarInfoSet.remove v (fst state), snd state) (* Unique pointed to heap vars *) + let heap_vars_of_tid = ThreadsToHeapVarsMap.find tid (fst state) in + let heap_vars_of_tid_without_v = ToppedVarInfoSet.remove v heap_vars_of_tid in + let new_fst_state = ThreadsToHeapVarsMap.add tid heap_vars_of_tid_without_v (fst state) in + (new_fst_state, snd state) + | _ -> state + end | _ -> state end | _ -> state end | Abort -> - (* Upon a call to the "Abort" special function, we give up and conservatively warn *) + check_for_mem_leak ctx; + (* Upon a call to the "Abort" special function in the multi-threaded case, we give up and conservatively warn *) warn_for_multi_threaded_due_to_abort ctx; state | Assert { exp; _ } -> @@ -131,7 +123,8 @@ struct state | ThreadExit _ -> begin match ctx.ask (Queries.CurrentThreadId) with - | `Lifted tid -> warn_for_thread_return_or_exit tid ctx false + | `Lifted tid -> + warn_for_thread_return_or_exit tid ctx false | _ -> () end; state From 6cc01b5177b6bc31899c290b6ae65660bf4aa805 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 18 Nov 2023 15:22:47 +0100 Subject: [PATCH 1029/1312] Use `unrollType` and `GVarDecl` for global vars Also use `Queries.AD.fold` where applicable and prepend to accumulators --- src/analyses/memLeak.ml | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index 51a5a2ff75..c09db2d44f 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -21,15 +21,20 @@ struct (* HELPER FUNCTIONS *) let get_global_vars () = - (* Filtering by GVar seems to account for declarations, as well as definitions of global vars *) - List.filter_map (function GVar (v, _, _) -> Some v | _ -> None) !Cilfacade.current_file.globals + List.filter_map (function GVar (v, _, _) | GVarDecl (v, _) -> Some v | _ -> None) !Cilfacade.current_file.globals let get_global_struct_ptr_vars () = - List.filter_map (function GVar (v, _, _) -> Some v | _ -> None) !Cilfacade.current_file.globals - |> List.filter (fun v -> match v.vtype with TPtr (TComp _, _) | TPtr ((TNamed ({ttype = TComp _; _}, _)), _) -> true | _ -> false) + get_global_vars () + |> List.filter (fun v -> + match unrollType v.vtype with + | TPtr (TComp _, _) + | TPtr ((TNamed ({ttype = TComp _; _}, _)), _) -> true + | TComp (_, _) + | (TNamed ({ttype = TComp _; _}, _)) -> false + | _ -> false) let get_global_struct_non_ptr_vars () = - List.filter_map (function GVar (v, _, _) -> Some v | _ -> None) !Cilfacade.current_file.globals + get_global_vars () |> List.filter (fun v -> match v.vtype with TComp (_, _) | (TNamed ({ttype = TComp _; _}, _)) -> true | _ -> false) let get_reachable_mem_from_globals (global_vars:varinfo list) ctx = @@ -55,12 +60,13 @@ struct begin match ValueDomain.Structs.get s f with | Queries.VD.Address a -> let reachable_from_addr_set = - List.fold_left (fun acc addr -> + Queries.AD.fold (fun addr acc -> match addr with - | Queries.AD.Addr.Addr (v, _) -> List.append acc (v :: get_reachable_mem_from_str_ptr_globals [v] ctx) + | Queries.AD.Addr.Addr (v, _) -> (v :: get_reachable_mem_from_str_ptr_globals [v] ctx) @ acc | _ -> acc - ) [] (Queries.AD.elements a) - in List.append acc reachable_from_addr_set + ) a [] + in + reachable_from_addr_set @ acc | _ -> acc end else acc @@ -109,14 +115,14 @@ struct let reachable_from_addr_set = List.fold_left (fun acc_addr addr -> match addr with - | Queries.AD.Addr.Addr (v, _) -> List.append acc_addr (v :: get_reachable_mem_from_str_ptr_globals [v] ctx) + | Queries.AD.Addr.Addr (v, _) -> (v :: get_reachable_mem_from_str_ptr_globals [v] ctx) @ acc_addr | _ -> acc_addr ) [] (Queries.AD.elements a) - in List.append acc_field reachable_from_addr_set + in reachable_from_addr_set @ acc_field | _ -> acc_field ) [] fields in - List.append acc_struct reachable_from_fields + reachable_from_fields @ acc_struct ) [] let warn_for_multi_threaded ctx = From 80492ccd1dcad21e49a949a989d1cee42bbb6585 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 18 Nov 2023 15:26:40 +0100 Subject: [PATCH 1030/1312] Check that addresses in struct fields are singletons and not top --- src/analyses/memLeak.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index c09db2d44f..7e77d62a4e 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -58,7 +58,7 @@ struct List.fold_left (fun acc f -> if isPointerType f.ftype then begin match ValueDomain.Structs.get s f with - | Queries.VD.Address a -> + | Queries.VD.Address a when not (Queries.AD.is_top a) && Queries.AD.cardinal a = 1 -> let reachable_from_addr_set = Queries.AD.fold (fun addr acc -> match addr with From 1f4d8be9e8fed3fe55b1561e0fac4cfc1923db25 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 19 Nov 2023 01:30:44 +0100 Subject: [PATCH 1031/1312] Avoid creating unneeded Task module which may not be set in `is_error_function'` --- src/witness/svcomp.ml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/witness/svcomp.ml b/src/witness/svcomp.ml index 404626bb07..6d22a51166 100644 --- a/src/witness/svcomp.ml +++ b/src/witness/svcomp.ml @@ -17,7 +17,6 @@ let task: (module Task) option ref = ref None let is_error_function' f spec = - let module Task = (val (Option.get !task)) in List.exists (function | Specification.UnreachCall f_spec -> f.vname = f_spec | _ -> false From 116916a5e48b2e56331da426664bd94246ddfa4b Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 19 Nov 2023 16:13:56 +0100 Subject: [PATCH 1032/1312] unlocked: Only test termination in config with Apron (References #1093) --- .github/workflows/unlocked.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/unlocked.yml b/.github/workflows/unlocked.yml index 22e1417ea4..990b7cfb49 100644 --- a/.github/workflows/unlocked.yml +++ b/.github/workflows/unlocked.yml @@ -93,6 +93,7 @@ jobs: run: ruby scripts/update_suite.rb group apron-mukherjee -s - name: Test apron termination regression # skipped by default but CI has apron, so explicitly test group (which ignores skipping -- it's now a feature!) + if: ${{ matrix.apron }} run: ruby scripts/update_suite.rb group termination -s - name: Test regression cram From 065221367db8e46f59f33bd18b07f8802af283e2 Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Sun, 19 Nov 2023 16:47:03 +0100 Subject: [PATCH 1033/1312] Autotune termination spec before preprocessing, but not others. --- src/autoTune.ml | 21 ++++++++++++++------- src/goblint.ml | 2 +- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/autoTune.ml b/src/autoTune.ml index 5e0034e6eb..70f61d5172 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -237,12 +237,8 @@ let focusOnMemSafetySpecification (spec: Svcomp.Specification.t) = let focusOnMemSafetySpecification () = List.iter focusOnMemSafetySpecification (Svcomp.Specification.of_option ()) -let focusOnSpecification (spec: Svcomp.Specification.t) = +let focusOnTermination (spec: Svcomp.Specification.t) = match spec with - | UnreachCall s -> () - | NoDataRace -> (*enable all thread analyses*) - print_endline @@ "Specification: NoDataRace -> enabling thread analyses \"" ^ (String.concat ", " notNeccessaryThreadAnalyses) ^ "\""; - enableAnalyses notNeccessaryThreadAnalyses; | Termination -> let terminationAnas = ["termination"; "threadflag"; "apron"] in print_endline @@ "Specification: Termination -> enabling termination analyses \"" ^ (String.concat ", " terminationAnas) ^ "\""; @@ -251,6 +247,17 @@ let focusOnSpecification (spec: Svcomp.Specification.t) = set_bool "ana.int.interval" true; set_string "ana.apron.domain" "polyhedra"; (* TODO: Needed? *) () + | _ -> () + +let focusOnTermination () = + List.iter focusOnTermination (Svcomp.Specification.of_option ()) + +let focusOnSpecification (spec: Svcomp.Specification.t) = + match spec with + | UnreachCall s -> () + | NoDataRace -> (*enable all thread analyses*) + print_endline @@ "Specification: NoDataRace -> enabling thread analyses \"" ^ (String.concat ", " notNeccessaryThreadAnalyses) ^ "\""; + enableAnalyses notNeccessaryThreadAnalyses; | NoOverflow -> (*We focus on integer analysis*) set_bool "ana.int.def_exc" true; set_bool "ana.int.interval" true @@ -518,8 +525,8 @@ let chooseConfig file = if isActivated "mallocWrappers" then findMallocWrappers (); - (* if specificationIsActivated () then - focusOnSpecification (); *) + if specificationIsActivated () then + focusOnSpecification (); if isActivated "enums" && hasEnums file then set_bool "ana.int.enums" true; diff --git a/src/goblint.ml b/src/goblint.ml index 6c5a2df33d..9b95c95da4 100644 --- a/src/goblint.ml +++ b/src/goblint.ml @@ -39,7 +39,7 @@ let main () = print_endline GobSys.command_line; ); (* When analyzing a termination specification, activate the termination analysis before pre-processing. *) - if get_bool "ana.autotune.enabled" && AutoTune.specificationIsActivated () then AutoTune.focusOnSpecification (); + if get_bool "ana.autotune.enabled" && AutoTune.specificationIsActivated () then AutoTune.focusOnTermination (); let file = lazy (Fun.protect ~finally:GoblintDir.finalize preprocess_parse_merge) in if get_bool "server.enabled" then ( let file = From 9d8629dc30b557e319838070e260508d4def8cf5 Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Sun, 19 Nov 2023 16:57:56 +0100 Subject: [PATCH 1034/1312] Use autotune.activated termination as separate switch whether termination analysis should be activated (instead of using specification). --- conf/svcomp.json | 3 +-- src/autoTune.ml | 3 +++ src/goblint.ml | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/conf/svcomp.json b/conf/svcomp.json index cc6d1e303a..107c59994c 100644 --- a/conf/svcomp.json +++ b/conf/svcomp.json @@ -72,8 +72,7 @@ "wideningThresholds", "loopUnrollHeuristic", "memsafetySpecification", - "termination", - "specification" + "termination" ] } }, diff --git a/src/autoTune.ml b/src/autoTune.ml index 70f61d5172..346e9d7b6f 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -506,6 +506,9 @@ let isTerminationTask () = List.mem Svcomp.Specification.Termination (Svcomp.Spe let specificationIsActivated () = isActivated "specification" && get_string "ana.specification" <> "" +let specificationTerminationIsActivated () = + isActivated "termination" + let chooseConfig file = let factors = collectFactors visitCilFileSameGlobals file in let fileCompplexity = estimateComplexity factors file in diff --git a/src/goblint.ml b/src/goblint.ml index 9b95c95da4..25e809f9e9 100644 --- a/src/goblint.ml +++ b/src/goblint.ml @@ -39,7 +39,7 @@ let main () = print_endline GobSys.command_line; ); (* When analyzing a termination specification, activate the termination analysis before pre-processing. *) - if get_bool "ana.autotune.enabled" && AutoTune.specificationIsActivated () then AutoTune.focusOnTermination (); + if get_bool "ana.autotune.enabled" && AutoTune.specificationTerminationIsActivated () then AutoTune.focusOnTermination (); let file = lazy (Fun.protect ~finally:GoblintDir.finalize preprocess_parse_merge) in if get_bool "server.enabled" then ( let file = From 720cfeebd1987bda44e4b7f4a2d545be6530025d Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 19 Nov 2023 17:32:05 +0100 Subject: [PATCH 1035/1312] IsMallocCalled should be `may` --- src/analyses/memLeak.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index 64fe7ab957..336943d407 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -7,7 +7,7 @@ open AnalysisStateUtil module ToppedVarInfoSet = SetDomain.ToppedSet(CilType.Varinfo)(struct let topname = "All Heap Variables" end) module ThreadsToHeapVarsMap = MapDomain.MapBot(ThreadIdDomain.Thread)(ToppedVarInfoSet) -module WasMallocCalled = BoolDomain.MustBool +module WasMallocCalled = BoolDomain.MayBool module Spec : Analyses.MCPSpec = struct include Analyses.IdentitySpec From f2ca6d146e1271709259be24d5b20f9e61aab7dd Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 19 Nov 2023 17:33:24 +0100 Subject: [PATCH 1036/1312] Use `unrollType` for non-pointer global struct vars --- src/analyses/memLeak.ml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index 7e77d62a4e..ab25d49cc6 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -35,7 +35,11 @@ struct let get_global_struct_non_ptr_vars () = get_global_vars () - |> List.filter (fun v -> match v.vtype with TComp (_, _) | (TNamed ({ttype = TComp _; _}, _)) -> true | _ -> false) + |> List.filter (fun v -> + match unrollType v.vtype with + | TComp (_, _) + | (TNamed ({ttype = TComp _; _}, _)) -> true + | _ -> false) let get_reachable_mem_from_globals (global_vars:varinfo list) ctx = global_vars From 0e09d099d5facf7354014319805eb53e186119d2 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 19 Nov 2023 17:36:22 +0100 Subject: [PATCH 1037/1312] Don't forget to prepend to `acc` when collecting globally reachable mem --- src/analyses/memLeak.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index ab25d49cc6..3079faae1f 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -91,8 +91,8 @@ struct in global_struct_ptr_vars |> List.fold_left (fun acc var -> - if ctx.ask (Queries.IsHeapVar var) then eval_value_of_heap_var var - else if not (ctx.ask (Queries.IsAllocVar var)) && isPointerType var.vtype then get_pts_of_non_heap_ptr_var var + if ctx.ask (Queries.IsHeapVar var) then (eval_value_of_heap_var var) @ acc + else if not (ctx.ask (Queries.IsAllocVar var)) && isPointerType var.vtype then (get_pts_of_non_heap_ptr_var var) @ acc else acc ) [] From af9ddc766c5b72e7bbd9d3177e1cd028e52cfc52 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 19 Nov 2023 17:36:26 +0100 Subject: [PATCH 1038/1312] Add unsound example --- tests/regression/76-memleak/10-leak-later.c | 25 +++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 tests/regression/76-memleak/10-leak-later.c diff --git a/tests/regression/76-memleak/10-leak-later.c b/tests/regression/76-memleak/10-leak-later.c new file mode 100644 index 0000000000..6e6e51bbdc --- /dev/null +++ b/tests/regression/76-memleak/10-leak-later.c @@ -0,0 +1,25 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak +#include +#include + +int *g; +int *m1; +int *m2; + +void *f1(void *arg) { + int top; + + // Thread t1 leaks m0 here + exit(2); //WARN +} + +int main(int argc, char const *argv[]) { + pthread_t t1; + pthread_create(&t1, NULL, f1, NULL); + + int* m0 = malloc(sizeof(int)); + free(m0); + + // main thread is not leaking anything + return 0; +} From ada84914a0da7614e023d6c0bf2ca86725d6551a Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 19 Nov 2023 18:14:50 +0100 Subject: [PATCH 1039/1312] Make sound by accounting for alloc in global invariant --- src/analyses/memLeak.ml | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index b1b57b6694..d2d3ce0d97 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -14,16 +14,19 @@ struct let name () = "memLeak" - module D = Lattice.Prod(ThreadsToHeapVarsMap)(WasMallocCalled) + module D = ThreadsToHeapVarsMap module C = D module P = IdentityP (D) + module V = UnitV + module G = WasMallocCalled + let context _ d = d (* HELPER FUNCTIONS *) let warn_for_multi_threaded_due_to_abort ctx = - let state = ctx.local in - if not (ctx.ask (Queries.MustBeSingleThreaded { since_start = true })) && snd state then ( + let malloc_called = ctx.global () in + if not (ctx.ask (Queries.MustBeSingleThreaded { since_start = true })) && malloc_called then ( set_mem_safety_flag InvalidMemTrack; set_mem_safety_flag InvalidMemcleanup; M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Program aborted while running in multi-threaded mode. A memory leak might occur" @@ -32,7 +35,7 @@ struct (* If [is_return] is set to [true], then a thread return occurred, else a thread exit *) let warn_for_thread_return_or_exit current_thread ctx is_return = let state = ctx.local in - let heap_vars_of_curr_tid = ThreadsToHeapVarsMap.find current_thread (fst state) in + let heap_vars_of_curr_tid = ThreadsToHeapVarsMap.find current_thread state in if not (ToppedVarInfoSet.is_empty heap_vars_of_curr_tid) then ( set_mem_safety_flag InvalidMemcleanup; M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory may be leaked at thread %s for thread %a" (if is_return then "return" else "exit") ThreadIdDomain.Thread.pretty current_thread @@ -40,7 +43,7 @@ struct let check_for_mem_leak ?(assert_exp_imprecise = false) ?(exp = None) ctx = let state = ctx.local in - if not (ThreadsToHeapVarsMap.for_all (fun tid heap_vars -> ToppedVarInfoSet.is_empty heap_vars) (fst state)) then + if not (ThreadsToHeapVarsMap.for_all (fun tid heap_vars -> ToppedVarInfoSet.is_empty heap_vars) state) then match assert_exp_imprecise, exp with | true, Some exp -> set_mem_safety_flag InvalidMemTrack; @@ -71,14 +74,15 @@ struct | Malloc _ | Calloc _ | Realloc _ -> + ctx.sideg () true; begin match ctx.ask (Queries.AllocVar {on_stack = false}) with | `Lifted var -> begin match ctx.ask (Queries.CurrentThreadId) with | `Lifted tid -> - ((ThreadsToHeapVarsMap.add tid (ToppedVarInfoSet.singleton var) (fst state)), true) - | _ -> (fst state, true) + (ThreadsToHeapVarsMap.add tid (ToppedVarInfoSet.singleton var) state) + | _ -> state end - | _ -> (fst state, true) + | _ -> state end | Free ptr -> begin match ctx.ask (Queries.MayPointTo ptr) with @@ -90,10 +94,10 @@ struct | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsAllocVar v) && ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsMultiple v) -> begin match ctx.ask (Queries.CurrentThreadId) with | `Lifted tid -> - let heap_vars_of_tid = ThreadsToHeapVarsMap.find tid (fst state) in + let heap_vars_of_tid = ThreadsToHeapVarsMap.find tid state in let heap_vars_of_tid_without_v = ToppedVarInfoSet.remove v heap_vars_of_tid in - let new_fst_state = ThreadsToHeapVarsMap.add tid heap_vars_of_tid_without_v (fst state) in - (new_fst_state, snd state) + let new_fst_state = ThreadsToHeapVarsMap.add tid heap_vars_of_tid_without_v state in + new_fst_state | _ -> state end | _ -> state From e7d630231d2bfce552cbb262bf3f8a4882ddd1ff Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 19 Nov 2023 18:49:03 +0100 Subject: [PATCH 1040/1312] Cleanup --- src/analyses/memLeak.ml | 53 +++++++------------ .../08-invalid-memcleanup-multi-threaded.c | 2 +- ...-invalid-memcleanup-multi-threaded-abort.c | 2 +- 3 files changed, 20 insertions(+), 37 deletions(-) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index d2d3ce0d97..0f16cec4ab 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -6,7 +6,6 @@ open MessageCategory open AnalysisStateUtil module ToppedVarInfoSet = SetDomain.ToppedSet(CilType.Varinfo)(struct let topname = "All Heap Variables" end) -module ThreadsToHeapVarsMap = MapDomain.MapBot(ThreadIdDomain.Thread)(ToppedVarInfoSet) module WasMallocCalled = BoolDomain.MayBool module Spec : Analyses.MCPSpec = struct @@ -14,7 +13,7 @@ struct let name () = "memLeak" - module D = ThreadsToHeapVarsMap + module D = ToppedVarInfoSet module C = D module P = IdentityP (D) @@ -33,22 +32,20 @@ struct ) (* If [is_return] is set to [true], then a thread return occurred, else a thread exit *) - let warn_for_thread_return_or_exit current_thread ctx is_return = - let state = ctx.local in - let heap_vars_of_curr_tid = ThreadsToHeapVarsMap.find current_thread state in - if not (ToppedVarInfoSet.is_empty heap_vars_of_curr_tid) then ( + let warn_for_thread_return_or_exit ctx is_return = + if not (ToppedVarInfoSet.is_empty ctx.local) then ( set_mem_safety_flag InvalidMemcleanup; - M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory may be leaked at thread %s for thread %a" (if is_return then "return" else "exit") ThreadIdDomain.Thread.pretty current_thread + let current_thread = ctx.ask (Queries.CurrentThreadId) in + M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory may be leaked at thread %s for thread %a" (if is_return then "return" else "exit") ThreadIdDomain.ThreadLifted.pretty current_thread ) let check_for_mem_leak ?(assert_exp_imprecise = false) ?(exp = None) ctx = - let state = ctx.local in - if not (ThreadsToHeapVarsMap.for_all (fun tid heap_vars -> ToppedVarInfoSet.is_empty heap_vars) state) then + if not (ToppedVarInfoSet.is_empty ctx.local) then match assert_exp_imprecise, exp with | true, Some exp -> set_mem_safety_flag InvalidMemTrack; set_mem_safety_flag InvalidMemcleanup; - M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "assert expression %a is unknown. Memory leak might possibly occur for heap variables: %a" d_exp exp D.pretty state + M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "assert expression %a is unknown. Memory leak might possibly occur for heap variables: %a" d_exp exp D.pretty ctx.local | _ -> set_mem_safety_flag InvalidMemTrack; set_mem_safety_flag InvalidMemcleanup; @@ -58,10 +55,7 @@ struct let return ctx (exp:exp option) (f:fundec) : D.t = (* Check for a valid-memcleanup violation in a multi-threaded setting *) if (ctx.ask (Queries.MayBeThreadReturn)) then ( - match ctx.ask (Queries.CurrentThreadId) with - | `Lifted tid -> - warn_for_thread_return_or_exit tid ctx true - | _ -> () + warn_for_thread_return_or_exit ctx true ); (* Returning from "main" is one possible program exit => need to check for memory leaks *) if f.svar.vname = "main" then check_for_mem_leak ctx; @@ -74,35 +68,22 @@ struct | Malloc _ | Calloc _ | Realloc _ -> - ctx.sideg () true; + (ctx.sideg () true; begin match ctx.ask (Queries.AllocVar {on_stack = false}) with | `Lifted var -> - begin match ctx.ask (Queries.CurrentThreadId) with - | `Lifted tid -> - (ThreadsToHeapVarsMap.add tid (ToppedVarInfoSet.singleton var) state) - | _ -> state - end + ToppedVarInfoSet.add var state | _ -> state - end + end) | Free ptr -> begin match ctx.ask (Queries.MayPointTo ptr) with - (* TODO: The cardinality of 1 seems to lead to the situation where only free() calls in main() are detected here (affects 76/08 and 76/09) *) - (* | ad when not (Queries.AD.is_top ad) && Queries.AD.cardinal ad = 1 -> *) - | ad when not (Queries.AD.is_top ad) -> + | ad when (not (Queries.AD.is_top ad)) && Queries.AD.cardinal ad = 1 -> (* Note: Need to always set "ana.malloc.unique_address_count" to a value > 0 *) begin match Queries.AD.choose ad with | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsAllocVar v) && ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsMultiple v) -> - begin match ctx.ask (Queries.CurrentThreadId) with - | `Lifted tid -> - let heap_vars_of_tid = ThreadsToHeapVarsMap.find tid state in - let heap_vars_of_tid_without_v = ToppedVarInfoSet.remove v heap_vars_of_tid in - let new_fst_state = ThreadsToHeapVarsMap.add tid heap_vars_of_tid_without_v state in - new_fst_state - | _ -> state - end - | _ -> state + ToppedVarInfoSet.remove v ctx.local + | _ -> ctx.local end - | _ -> state + | _ -> ctx.local end | Abort -> check_for_mem_leak ctx; @@ -128,7 +109,7 @@ struct | ThreadExit _ -> begin match ctx.ask (Queries.CurrentThreadId) with | `Lifted tid -> - warn_for_thread_return_or_exit tid ctx false + warn_for_thread_return_or_exit ctx false | _ -> () end; state @@ -136,6 +117,8 @@ struct let startstate v = D.bot () let exitstate v = D.top () + + let threadenter ctx ~multiple lval f args = [D.bot ()] end let _ = diff --git a/tests/regression/76-memleak/08-invalid-memcleanup-multi-threaded.c b/tests/regression/76-memleak/08-invalid-memcleanup-multi-threaded.c index 50b17fa65d..513a36db95 100644 --- a/tests/regression/76-memleak/08-invalid-memcleanup-multi-threaded.c +++ b/tests/regression/76-memleak/08-invalid-memcleanup-multi-threaded.c @@ -4,7 +4,6 @@ int *g; int *m1; -int *m2; void *f1(void *arg) { m1 = malloc(sizeof(int)); @@ -13,6 +12,7 @@ void *f1(void *arg) { } void *f2(void *arg) { + int *m2; m2 = malloc(sizeof(int)); free(m2); // No leak for thread t2, since it calls free before exiting pthread_exit(NULL); //NOWARN diff --git a/tests/regression/76-memleak/09-invalid-memcleanup-multi-threaded-abort.c b/tests/regression/76-memleak/09-invalid-memcleanup-multi-threaded-abort.c index 9aef45198e..977510b9bb 100644 --- a/tests/regression/76-memleak/09-invalid-memcleanup-multi-threaded-abort.c +++ b/tests/regression/76-memleak/09-invalid-memcleanup-multi-threaded-abort.c @@ -4,7 +4,6 @@ int *g; int *m1; -int *m2; void *f1(void *arg) { m1 = malloc(sizeof(int)); @@ -13,6 +12,7 @@ void *f1(void *arg) { } void *f2(void *arg) { + int *m2; m2 = malloc(sizeof(int)); free(m2); // No leak for thread t2, since it calls free before exiting pthread_exit(NULL); //NOWARN From e6cee270129731e462b31a0a75cb360d02b784c6 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 19 Nov 2023 19:01:11 +0100 Subject: [PATCH 1041/1312] Fix `memtrack` for multi-threaded case --- src/analyses/memLeak.ml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index 0f16cec4ab..8a067cc80d 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -34,6 +34,7 @@ struct (* If [is_return] is set to [true], then a thread return occurred, else a thread exit *) let warn_for_thread_return_or_exit ctx is_return = if not (ToppedVarInfoSet.is_empty ctx.local) then ( + set_mem_safety_flag InvalidMemTrack; set_mem_safety_flag InvalidMemcleanup; let current_thread = ctx.ask (Queries.CurrentThreadId) in M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory may be leaked at thread %s for thread %a" (if is_return then "return" else "exit") ThreadIdDomain.ThreadLifted.pretty current_thread @@ -53,8 +54,9 @@ struct (* TRANSFER FUNCTIONS *) let return ctx (exp:exp option) (f:fundec) : D.t = - (* Check for a valid-memcleanup violation in a multi-threaded setting *) - if (ctx.ask (Queries.MayBeThreadReturn)) then ( + (* Check for a valid-memcleanup and memtrack violation in a multi-threaded setting *) + (* The check for multi-threadedness is to ensure that valid-memtrack and valid-memclenaup are treated separately for single-threaded programs *) + if (ctx.ask (Queries.MayBeThreadReturn) && not (ctx.ask (Queries.MustBeSingleThreaded { since_start = true }))) then ( warn_for_thread_return_or_exit ctx true ); (* Returning from "main" is one possible program exit => need to check for memory leaks *) From 97eb7156d4a71fce2e84153b1a11906db5e9f2de Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 19 Nov 2023 19:23:20 +0100 Subject: [PATCH 1042/1312] Account for failing assertions in the multi-threaded case as well --- src/analyses/memLeak.ml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index 8a067cc80d..8d83bcee83 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -98,12 +98,16 @@ struct | a when Queries.ID.is_bot a -> M.warn ~category:Assert "assert expression %a is bottom" d_exp exp | a -> begin match Queries.ID.to_bool a with - | Some b -> + | Some b -> ( (* If we know for sure that the expression in "assert" is false => need to check for memory leaks *) - if b = false then - check_for_mem_leak ctx - else () - | None -> check_for_mem_leak ctx ~assert_exp_imprecise:true ~exp:(Some exp) + if b = false then ( + warn_for_multi_threaded_due_to_abort ctx; + check_for_mem_leak ctx + ) + else ()) + | None -> + (warn_for_multi_threaded_due_to_abort ctx; + check_for_mem_leak ctx ~assert_exp_imprecise:true ~exp:(Some exp)) end in warn_for_assert_exp; From 987795ec92c6e03fc9b0b659dbc396b73df41098 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 19 Nov 2023 19:26:39 +0100 Subject: [PATCH 1043/1312] Add a few more test cases --- .../76-memleak/11-leak-later-nested.c | 34 +++++++++++++++++++ .../76-memleak/12-multi-threaded-assert.c | 34 +++++++++++++++++++ .../13-assert-unknown-multi-threaded.c | 20 +++++++++++ 3 files changed, 88 insertions(+) create mode 100644 tests/regression/76-memleak/11-leak-later-nested.c create mode 100644 tests/regression/76-memleak/12-multi-threaded-assert.c create mode 100644 tests/regression/76-memleak/13-assert-unknown-multi-threaded.c diff --git a/tests/regression/76-memleak/11-leak-later-nested.c b/tests/regression/76-memleak/11-leak-later-nested.c new file mode 100644 index 0000000000..952dc66334 --- /dev/null +++ b/tests/regression/76-memleak/11-leak-later-nested.c @@ -0,0 +1,34 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak +#include +#include + +int *g; +int *m1; +int *m2; + +void *f2(void *arg) { + // Thread t2 leaks m0 and t1_ptr here + quick_exit(2); //WARN +} + +void *f1(void *arg) { + pthread_t t2; + pthread_create(&t2, NULL, f2, NULL); + + int *t1_ptr = malloc(sizeof(int)); + + pthread_join(t2, NULL); + // t1_ptr is leaked, since t2 calls quick_exit() potentially before this program point + free(t1_ptr); +} + +int main(int argc, char const *argv[]) { + pthread_t t1; + pthread_create(&t1, NULL, f1, NULL); + + int* m0 = malloc(sizeof(int)); + free(m0); + + // main thread is not leaking anything + return 0; +} diff --git a/tests/regression/76-memleak/12-multi-threaded-assert.c b/tests/regression/76-memleak/12-multi-threaded-assert.c new file mode 100644 index 0000000000..309a5dde75 --- /dev/null +++ b/tests/regression/76-memleak/12-multi-threaded-assert.c @@ -0,0 +1,34 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak --disable warn.assert +#include +#include +#include + +int *g; +int *m1; +int *m2; + +void *f2(void *arg) { + // Thread t2 leaks m0 and t1_ptr here + assert(0); //WARN +} + +void *f1(void *arg) { + pthread_t t2; + pthread_create(&t2, NULL, f2, NULL); + + int *t1_ptr = malloc(sizeof(int)); + assert(1); //NOWARN + pthread_join(t2, NULL); + free(t1_ptr); +} + +int main(int argc, char const *argv[]) { + pthread_t t1; + pthread_create(&t1, NULL, f1, NULL); + + int* m0 = malloc(sizeof(int)); + free(m0); + + // main thread is not leaking anything + return 0; +} diff --git a/tests/regression/76-memleak/13-assert-unknown-multi-threaded.c b/tests/regression/76-memleak/13-assert-unknown-multi-threaded.c new file mode 100644 index 0000000000..95eb291887 --- /dev/null +++ b/tests/regression/76-memleak/13-assert-unknown-multi-threaded.c @@ -0,0 +1,20 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak --disable warn.assert +#include +#include +#include + +void *f1(void *arg) { + int top; + assert(top); //WARN +} + +int main(int argc, char const *argv[]) { + pthread_t t1; + pthread_create(&t1, NULL, f1, NULL); + + int* m0 = malloc(sizeof(int)); + free(m0); + + // main thread is not leaking anything + return 0; +} From f6cef727d38bd2deb0c766d2f8bc6c726024bbbc Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 19 Nov 2023 19:27:55 +0100 Subject: [PATCH 1044/1312] `<<` Fix wrong order of `minimal`/`maximal` --- src/cdomains/intDomain.ml | 2 +- tests/regression/39-signed-overflows/06-shiftleft.c | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/cdomains/intDomain.ml b/src/cdomains/intDomain.ml index 49065b9cc5..5a80d67bfe 100644 --- a/src/cdomains/intDomain.ml +++ b/src/cdomains/intDomain.ml @@ -874,7 +874,7 @@ struct | true, _ | _ , true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show a) (show b))) | _ -> - match a, maximal b, minimal b with + match a, minimal b, maximal b with | Some a, Some bl, Some bu when (Ints_t.compare bl Ints_t.zero >= 0) -> (try let r = IArith.shiftleft a (Ints_t.to_int bl, Ints_t.to_int bu) in diff --git a/tests/regression/39-signed-overflows/06-shiftleft.c b/tests/regression/39-signed-overflows/06-shiftleft.c index 987775c6f7..aff853ee36 100644 --- a/tests/regression/39-signed-overflows/06-shiftleft.c +++ b/tests/regression/39-signed-overflows/06-shiftleft.c @@ -19,5 +19,8 @@ int main() __goblint_check(r >= 8); __goblint_check(r <= 16); + int regval; + unsigned long bla = (unsigned long )((1 << ((int )regval >> 6)) << 20); //WARN + return 0; } From 8d55024d0580b5a4aec7bd32103d0b3f0ab84d72 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 19 Nov 2023 19:45:27 +0100 Subject: [PATCH 1045/1312] Add options to produce warnings only for memory leaks due to `memcleanup` or `memtrack` violations --- src/common/util/options.schema.json | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/common/util/options.schema.json b/src/common/util/options.schema.json index 9a6a66ee6b..5923612b5b 100644 --- a/src/common/util/options.schema.json +++ b/src/common/util/options.schema.json @@ -2164,6 +2164,25 @@ "description": "Output messages in deterministic order. Useful for cram testing.", "type": "boolean", "default": false + }, + "memleak": { + "title": "warn.memleak", + "type":"object", + "properties": { + "memcleanup": { + "title": "warn.memleak.memcleanup", + "description": "Enable memory leak warnings only for violations of the SV-COMP \"valid-memcleanup\" category", + "type": "boolean", + "default": false + }, + "memtrack": { + "title": "warn.memleak.memtrack", + "description": "Enable memory leak warnings only for violations of the SV-COMP \"valid-memtrack\" category", + "type": "boolean", + "default": false + } + }, + "additionalProperties": false } }, "additionalProperties": false From 85e6fdbfcba3d42bf35b44f1c41171e958890451 Mon Sep 17 00:00:00 2001 From: oliver Date: Mon, 20 Nov 2023 01:55:30 +0100 Subject: [PATCH 1046/1312] bump dune dependencies and rebuild goblint.opam --- goblint.opam | 2 +- goblint.opam.locked | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/goblint.opam b/goblint.opam index bf51924626..34912fde26 100644 --- a/goblint.opam +++ b/goblint.opam @@ -19,7 +19,7 @@ homepage: "https://goblint.in.tum.de" doc: "https://goblint.readthedocs.io/en/latest/" bug-reports: "https://github.com/goblint/analyzer/issues" depends: [ - "dune" {>= "3.6"} + "dune" {>= "3.7"} "ocaml" {>= "4.10"} "goblint-cil" {>= "2.0.2"} "batteries" {>= "3.5.0"} diff --git a/goblint.opam.locked b/goblint.opam.locked index 0abe989955..6e15ac8900 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -76,7 +76,7 @@ depends: [ "ocamlfind" {= "1.9.5"} "odoc" {= "2.2.0" & with-doc} "odoc-parser" {= "2.0.0" & with-doc} - "ordering" {= "3.6.1"} + "ordering" {= "3.7.1"} "ounit2" {= "2.2.6" & with-test} "pp" {= "1.1.2"} "ppx_derivers" {= "1.2.1"} @@ -93,7 +93,7 @@ depends: [ "sexplib0" {= "v0.15.1"} "sha" {= "1.15.2"} "stdlib-shims" {= "0.3.0"} - "stdune" {= "3.6.1"} + "stdune" {= "3.7.1"} "stringext" {= "1.6.0"} "topkg" {= "1.0.6"} "tyxml" {= "4.5.0" & with-doc} From ecd48aa318c0720c2f2b8b63a524635170df804e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 20 Nov 2023 10:46:35 +0200 Subject: [PATCH 1047/1312] Make SV-COMP validation strict --- conf/svcomp24-validate.json | 1 + src/common/util/options.schema.json | 6 ++++++ src/witness/witness.ml | 13 ++++++++++--- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/conf/svcomp24-validate.json b/conf/svcomp24-validate.json index ce11af12f6..2d7e988fdf 100644 --- a/conf/svcomp24-validate.json +++ b/conf/svcomp24-validate.json @@ -114,6 +114,7 @@ }, "yaml": { "enabled": false, + "strict": true, "format-version": "2.0", "entry-types": [ "location_invariant", diff --git a/src/common/util/options.schema.json b/src/common/util/options.schema.json index 328b4f277f..0732debcc9 100644 --- a/src/common/util/options.schema.json +++ b/src/common/util/options.schema.json @@ -2487,6 +2487,12 @@ "type": "string", "default": "" }, + "strict": { + "title": "witness.yaml.strict", + "description": "", + "type": "boolean", + "default": false + }, "unassume": { "title": "witness.yaml.unassume", "description": "YAML witness input path", diff --git a/src/witness/witness.ml b/src/witness/witness.ml index 235461c348..9d6a1ebe02 100644 --- a/src/witness/witness.ml +++ b/src/witness/witness.ml @@ -665,11 +665,18 @@ struct | Some false -> print_svcomp_result "ERROR (verify)" | _ -> if get_string "witness.yaml.validate" <> "" then ( - if !YamlWitness.cnt_refuted > 0 then + match get_bool "witness.yaml.strict" with + | true when !YamlWitness.cnt_error > 0 -> + print_svcomp_result "ERROR (witness error)" + | true when !YamlWitness.cnt_unsupported > 0 -> + print_svcomp_result "ERROR (witness unsupported)" + | true when !YamlWitness.cnt_disabled > 0 -> + print_svcomp_result "ERROR (witness disabled)" + | _ when !YamlWitness.cnt_refuted > 0 -> print_svcomp_result (Result.to_string (False None)) - else if !YamlWitness.cnt_unconfirmed > 0 then + | _ when !YamlWitness.cnt_unconfirmed > 0 -> print_svcomp_result (Result.to_string Unknown) - else + | _ -> write entrystates ) else From 9d77dec344287d4ad42e723abfa5777dc3d01afc Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 20 Nov 2023 11:59:44 +0200 Subject: [PATCH 1048/1312] Move abs refine to BaseInvariant --- src/analyses/base.ml | 43 ++++++++--------------------------- src/analyses/baseInvariant.ml | 27 ++++++++++++++++++++++ 2 files changed, 36 insertions(+), 34 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 4911b0d033..f25f0a9693 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1753,40 +1753,15 @@ struct let branch ctx (exp:exp) (tv:bool) : store = let valu = eval_rv (Analyses.ask_of_ctx ctx) ctx.global ctx.local exp in let refine () = - let refine0 = - let res = invariant ctx (Analyses.ask_of_ctx ctx) ctx.global ctx.local exp tv in - if M.tracing then M.tracec "branch" "EqualSet result for expression %a is %a\n" d_exp exp Queries.ES.pretty (ctx.ask (Queries.EqualSet exp)); - if M.tracing then M.tracec "branch" "CondVars result for expression %a is %a\n" d_exp exp Queries.ES.pretty (ctx.ask (Queries.CondVars exp)); - if M.tracing then M.traceu "branch" "Invariant enforced!\n"; - match ctx.ask (Queries.CondVars exp) with - | s when Queries.ES.cardinal s = 1 -> - let e = Queries.ES.choose s in - invariant ctx (Analyses.ask_of_ctx ctx) ctx.global res e tv - | _ -> res - in - (* bodge for abs(...); To be removed once we have a clean solution *) - let refineAbs op absargexp valexp = - let flip op = match op with | Le -> Ge | Lt -> Gt | _ -> failwith "impossible" in - (* e.g. |arg| <= 40 *) - (* arg <= e (arg <= 40) *) - let le = BinOp (op, absargexp, valexp, intType) in - (* arg >= -e (arg >= -40) *) - let gt = BinOp(flip op, absargexp, UnOp (Neg, valexp, Cilfacade.typeOf valexp), intType) in - let one = invariant ctx (Analyses.ask_of_ctx ctx) ctx.global refine0 le tv in - invariant ctx (Analyses.ask_of_ctx ctx) ctx.global one gt tv - in - match exp with - | BinOp ((Lt|Le) as op, CastE(t, Lval (Var v, NoOffset)), e,_) when tv -> - (match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil NoOffset)) with - | `Lifted (Abs arg) -> - refineAbs op (CastE (t, arg)) e - | _ -> refine0) - | BinOp ((Lt|Le) as op, Lval (Var v, NoOffset), e, _) when tv -> - (match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil NoOffset)) with - | `Lifted (Abs arg) -> - refineAbs op arg e - | _ -> refine0) - | _ -> refine0 + let res = invariant ctx (Analyses.ask_of_ctx ctx) ctx.global ctx.local exp tv in + if M.tracing then M.tracec "branch" "EqualSet result for expression %a is %a\n" d_exp exp Queries.ES.pretty (ctx.ask (Queries.EqualSet exp)); + if M.tracing then M.tracec "branch" "CondVars result for expression %a is %a\n" d_exp exp Queries.ES.pretty (ctx.ask (Queries.CondVars exp)); + if M.tracing then M.traceu "branch" "Invariant enforced!\n"; + match ctx.ask (Queries.CondVars exp) with + | s when Queries.ES.cardinal s = 1 -> + let e = Queries.ES.choose s in + invariant ctx (Analyses.ask_of_ctx ctx) ctx.global res e tv + | _ -> res in if M.tracing then M.traceli "branch" ~subsys:["invariant"] "Evaluating branch for expression %a with value %a\n" d_exp exp VD.pretty valu; (* First we want to see, if we can determine a dead branch: *) diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index 72e00efbb1..a99e25e7c6 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -821,4 +821,31 @@ struct FD.top_of fk in inv_exp (Float ftv) exp st + + let invariant ctx a gs st exp tv: D.t = + let refine0 = invariant ctx a gs st exp tv in + (* bodge for abs(...); To be removed once we have a clean solution *) + let refineAbs op absargexp valexp = + let flip op = match op with | Le -> Ge | Lt -> Gt | _ -> failwith "impossible" in + (* e.g. |arg| <= 40 *) + (* arg <= e (arg <= 40) *) + let le = BinOp (op, absargexp, valexp, intType) in + (* arg >= -e (arg >= -40) *) + let gt = BinOp(flip op, absargexp, UnOp (Neg, valexp, Cilfacade.typeOf valexp), intType) in + let one = invariant ctx (Analyses.ask_of_ctx ctx) ctx.global refine0 le tv in + invariant ctx (Analyses.ask_of_ctx ctx) ctx.global one gt tv + in + match exp with + | BinOp ((Lt|Le) as op, CastE(t, Lval (Var v, NoOffset)), e,_) when tv -> + (match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil NoOffset)) with + | `Lifted (Abs arg) -> + refineAbs op (CastE (t, arg)) e + | _ -> refine0) + | BinOp ((Lt|Le) as op, Lval (Var v, NoOffset), e, _) when tv -> + (match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil NoOffset)) with + | `Lifted (Abs arg) -> + refineAbs op arg e + | _ -> refine0) + | _ -> refine0 + end From 0cb1b7e1135fee97db9d4d7da6a90c1bc8261fa5 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 20 Nov 2023 12:04:21 +0200 Subject: [PATCH 1049/1312] Fix BaseInvariant abs indentation --- src/analyses/baseInvariant.ml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index a99e25e7c6..86ec0d3bf7 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -837,15 +837,15 @@ struct in match exp with | BinOp ((Lt|Le) as op, CastE(t, Lval (Var v, NoOffset)), e,_) when tv -> - (match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil NoOffset)) with - | `Lifted (Abs arg) -> - refineAbs op (CastE (t, arg)) e - | _ -> refine0) + begin match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil NoOffset)) with + | `Lifted (Abs arg) -> refineAbs op (CastE (t, arg)) e + | _ -> refine0 + end | BinOp ((Lt|Le) as op, Lval (Var v, NoOffset), e, _) when tv -> - (match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil NoOffset)) with - | `Lifted (Abs arg) -> - refineAbs op arg e - | _ -> refine0) + begin match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil NoOffset)) with + | `Lifted (Abs arg) -> refineAbs op arg e + | _ -> refine0 + end | _ -> refine0 end From 19190ca63d43aaf14ae462823b3878ce65b25606 Mon Sep 17 00:00:00 2001 From: stilscher <66023521+stilscher@users.noreply.github.com> Date: Mon, 20 Nov 2023 11:09:26 +0100 Subject: [PATCH 1050/1312] remove special cases handled by general case --- src/analyses/base.ml | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index f25f0a9693..2a729c685e 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2339,13 +2339,9 @@ struct | None, _ -> ID.top_of ik | Some mx, Some mm when Z.equal mx mm -> ID.top_of ik | _, _ -> - match ID.le xcast (ID.of_int ik Z.zero) with - | d when d = ID.of_int ik Z.zero -> xcast (* x positive *) - | d when d = ID.of_int ik Z.one -> ID.neg xcast (* x negative *) - | _ -> (* both possible *) - let x1 = ID.neg (ID.meet (ID.ending ik Z.zero) xcast) in - let x2 = ID.meet (ID.starting ik Z.zero) xcast in - ID.join x1 x2 + let x1 = ID.neg (ID.meet (ID.ending ik Z.zero) xcast) in + let x2 = ID.meet (ID.starting ik Z.zero) xcast in + ID.join x1 x2 ) | _ -> failwith ("non-integer argument in call to function "^f.vname) end From 6797cbb9ec941646316ffb689a12135bf1282b6a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 20 Nov 2023 12:21:32 +0200 Subject: [PATCH 1051/1312] Add ana.autotune.activated schema --- src/common/util/options.schema.json | 32 +++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/src/common/util/options.schema.json b/src/common/util/options.schema.json index 745aecfb57..2b1e738196 100644 --- a/src/common/util/options.schema.json +++ b/src/common/util/options.schema.json @@ -542,9 +542,37 @@ "title": "ana.autotune.activated", "description": "Lists of activated tuning options.", "type": "array", - "items": { "type": "string" }, + "items": { + "type": "string", + "enum": [ + "congruence", + "singleThreaded", + "specification", + "mallocWrappers", + "noRecursiveIntervals", + "enums", + "loopUnrollHeuristic", + "forceLoopUnrollForFewLoops", + "arrayDomain", + "octagon", + "wideningThresholds", + "memsafetySpecification", + "termination" + ] + }, "default": [ - "congruence", "singleThreaded", "specification", "mallocWrappers", "noRecursiveIntervals", "enums", "loopUnrollHeuristic", "arrayDomain", "octagon", "wideningThresholds", "memsafetySpecification" + "congruence", + "singleThreaded", + "specification", + "mallocWrappers", + "noRecursiveIntervals", + "enums", + "loopUnrollHeuristic", + "arrayDomain", + "octagon", + "wideningThresholds", + "memsafetySpecification", + "termination" ] } }, From 3af192da0600613de1f789e827d98b87d13c41ef Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 20 Nov 2023 13:10:40 +0200 Subject: [PATCH 1052/1312] Deactivate mhp and region for single-threaded programs --- src/autoTune.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/autoTune.ml b/src/autoTune.ml index 346e9d7b6f..d9a866ffc2 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -184,7 +184,7 @@ let enableAnalyses anas = (*escape is also still enabled, because otherwise we get a warning*) (*does not consider dynamic calls!*) -let notNeccessaryThreadAnalyses = ["race"; "deadlock"; "maylocks"; "symb_locks"; "thread"; "threadid"; "threadJoins"; "threadreturn"] +let notNeccessaryThreadAnalyses = ["race"; "deadlock"; "maylocks"; "symb_locks"; "thread"; "threadid"; "threadJoins"; "threadreturn"; "mhp"; "region"] let reduceThreadAnalyses () = let isThreadCreate = function | LibraryDesc.ThreadCreate _ -> true From 0ee71a02f9fe08381c94eb6704bbc42055a778fa Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 20 Nov 2023 13:48:28 +0200 Subject: [PATCH 1053/1312] Update SV-COMP releasing guide for 2024 --- docs/developer-guide/releasing.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/developer-guide/releasing.md b/docs/developer-guide/releasing.md index 69ffcb2461..fc5f5f68a1 100644 --- a/docs/developer-guide/releasing.md +++ b/docs/developer-guide/releasing.md @@ -97,16 +97,17 @@ This ensures that the environment and the archive have all the correct system libraries. -6. Commit and push the archive to an SV-COMP archives repository branch (but don't open a MR yet): (SV-COMP 2023). -7. Check pushed archive via CoveriTeam-Remote: . +6. Create (or add new version) Zenodo artifact and upload the archive. - 1. Clone coveriteam repository. - 2. Locally modify `actors/goblint.yml` archive location to the raw URL of the pushed archive. - 3. Run Goblint on some sv-benchmarks and properties via CoveriTeam. +7. Open MR with Zenodo version DOI to the [fm-tools](https://gitlab.com/sosy-lab/benchmarking/fm-tools) repository. - This ensures that Goblint runs on SoSy-Lab servers. + ### After all preruns From 5784ffeb5f0e26b4da059a0d762480a0ce999505 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 20 Nov 2023 14:44:56 +0200 Subject: [PATCH 1054/1312] Update Gobview submodule for dune 3.7.1 lock --- gobview | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gobview b/gobview index 42b07f8253..b4467d820f 160000 --- a/gobview +++ b/gobview @@ -1 +1 @@ -Subproject commit 42b07f825316052ec030370daf0d00ebe28ec092 +Subproject commit b4467d820f28bac578fc0baf7f81393c67f6b82b From 631f4fc69cf667a957a7575f653210a3702135c6 Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Mon, 20 Nov 2023 16:25:47 +0100 Subject: [PATCH 1055/1312] Do not set overflow flag on cast. --- src/cdomains/intDomain.ml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/cdomains/intDomain.ml b/src/cdomains/intDomain.ml index 3bc84ae676..0a98e57a29 100644 --- a/src/cdomains/intDomain.ml +++ b/src/cdomains/intDomain.ml @@ -3410,14 +3410,14 @@ module IntDomTupleImpl = struct | Some(_, {underflow; overflow}) -> not (underflow || overflow) | _ -> false - let check_ov ik intv intv_set = + let check_ov ~cast ik intv intv_set = let no_ov = (no_overflow ik intv) || (no_overflow ik intv_set) in if not no_ov && ( BatOption.is_some intv || BatOption.is_some intv_set) then ( let (_,{underflow=underflow_intv; overflow=overflow_intv}) = match intv with None -> (I2.bot (), {underflow= true; overflow = true}) | Some x -> x in let (_,{underflow=underflow_intv_set; overflow=overflow_intv_set}) = match intv_set with None -> (I5.bot (), {underflow= true; overflow = true}) | Some x -> x in let underflow = underflow_intv && underflow_intv_set in let overflow = overflow_intv && overflow_intv_set in - set_overflow_flag ~cast:false ~underflow ~overflow ik; + set_overflow_flag ~cast ~underflow ~overflow ik; ); no_ov @@ -3426,7 +3426,7 @@ module IntDomTupleImpl = struct let map x = Option.map fst x in let intv = f p2 @@ r.fi2_ovc (module I2) in let intv_set = f p5 @@ r.fi2_ovc (module I5) in - ignore (check_ov ik intv intv_set); + ignore (check_ov ~cast:false ik intv intv_set); map @@ f p1 @@ r.fi2_ovc (module I1), map @@ f p2 @@ r.fi2_ovc (module I2), map @@ f p3 @@ r.fi2_ovc (module I3), map @@ f p4 @@ r.fi2_ovc (module I4), map @@ f p5 @@ r.fi2_ovc (module I5) let create2_ovc ik r x = (* use where values are introduced *) @@ -3607,7 +3607,7 @@ module IntDomTupleImpl = struct let map f ?no_ov = function Some x -> Some (f ?no_ov x) | _ -> None in let intv = map (r.f1_ovc (module I2)) b in let intv_set = map (r.f1_ovc (module I5)) e in - let no_ov = check_ov ik intv intv_set in + let no_ov = check_ov ~cast ik intv intv_set in let no_ov = no_ov || should_ignore_overflow ik in refine ik ( map (fun ?no_ov x -> r.f1_ovc ?no_ov (module I1) x |> fst) a @@ -3617,10 +3617,10 @@ module IntDomTupleImpl = struct , BatOption.map fst intv_set ) (* map2 with overflow check *) - let map2ovc ik r (xa, xb, xc, xd, xe) (ya, yb, yc, yd, ye) = + let map2ovc ?(cast=false) ik r (xa, xb, xc, xd, xe) (ya, yb, yc, yd, ye) = let intv = opt_map2 (r.f2_ovc (module I2)) xb yb in let intv_set = opt_map2 (r.f2_ovc (module I5)) xe ye in - let no_ov = check_ov ik intv intv_set in + let no_ov = check_ov ~cast ik intv intv_set in let no_ov = no_ov || should_ignore_overflow ik in refine ik ( opt_map2 (fun ?no_ov x y -> r.f2_ovc ?no_ov (module I1) x y |> fst) xa ya From 4f7eb52a54b75e01740f065eb4e3eccf783adabb Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Mon, 20 Nov 2023 16:33:28 +0100 Subject: [PATCH 1056/1312] Add test case for cast. --- tests/regression/29-svcomp/32-no-ov.c | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 tests/regression/29-svcomp/32-no-ov.c diff --git a/tests/regression/29-svcomp/32-no-ov.c b/tests/regression/29-svcomp/32-no-ov.c new file mode 100644 index 0000000000..0167098c29 --- /dev/null +++ b/tests/regression/29-svcomp/32-no-ov.c @@ -0,0 +1,7 @@ +// PARAM: --enable ana.int.interval --enable ana.sv-comp.enabled --enable ana.sv-comp.functions --set ana.specification "CHECK( init(main()), LTL(G ! overflow) )" + +int main(){ + // This is not an overflow, just implementation defined behavior on a cast + int data = ((int)(rand() & 1 ? (((unsigned)rand()<<30) ^ ((unsigned)rand()<<15) ^ rand()) : -(((unsigned)rand()<<30) ^ ((unsigned)rand()<<15) ^ rand()) - 1)); + return 0; +} \ No newline at end of file From 5a8863089b4526f51f997b58c22bba77427ac4e3 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 20 Nov 2023 16:58:57 +0100 Subject: [PATCH 1057/1312] Unify naming --- src/cdomains/intDomain.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cdomains/intDomain.ml b/src/cdomains/intDomain.ml index 5a80d67bfe..fde09a797a 100644 --- a/src/cdomains/intDomain.ml +++ b/src/cdomains/intDomain.ml @@ -610,7 +610,7 @@ module IntervalArith(Ints_t : IntOps.IntOps) = struct let x2y2 = (Ints_t.mul x2 y2) in (min4 x1y1 x1y2 x2y1 x2y2, max4 x1y1 x1y2 x2y1 x2y2) - let shiftleft (x1,x2) (y1,y2) = + let shift_left (x1,x2) (y1,y2) = let y1p = Ints_t.shift_left Ints_t.one y1 in let y2p = Ints_t.shift_left Ints_t.one y2 in mul (x1, x2) (y1p, y2p) @@ -877,7 +877,7 @@ struct match a, minimal b, maximal b with | Some a, Some bl, Some bu when (Ints_t.compare bl Ints_t.zero >= 0) -> (try - let r = IArith.shiftleft a (Ints_t.to_int bl, Ints_t.to_int bu) in + let r = IArith.shift_left a (Ints_t.to_int bl, Ints_t.to_int bu) in norm ik @@ Some r with Z.Overflow -> (top_of ik,{underflow=false; overflow=true})) | _ -> (top_of ik,{underflow=true; overflow=true}) From c9be89e68cd01a189f336f350cf8cae12c505b43 Mon Sep 17 00:00:00 2001 From: stilscher <66023521+stilscher@users.noreply.github.com> Date: Mon, 20 Nov 2023 19:40:40 +0100 Subject: [PATCH 1058/1312] add support for labs and llabs --- src/analyses/base.ml | 2 +- src/analyses/baseInvariant.ml | 4 ++-- src/analyses/libraryDesc.ml | 4 ++-- src/analyses/libraryFunctions.ml | 4 +++- .../regression/39-signed-overflows/08-labs.c | 22 +++++++++++++++++++ .../39-signed-overflows/09-labs-sqrt.c | 10 +++++++++ 6 files changed, 40 insertions(+), 6 deletions(-) create mode 100644 tests/regression/39-signed-overflows/08-labs.c create mode 100644 tests/regression/39-signed-overflows/09-labs-sqrt.c diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 2a729c685e..84be8c7a19 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2375,7 +2375,7 @@ struct | Fmax (fd, x ,y) -> Float (apply_binary fd FD.fmax x y) | Fmin (fd, x ,y) -> Float (apply_binary fd FD.fmin x y) | Sqrt (fk, x) -> Float (apply_unary fk FD.sqrt x) - | Abs x -> Int (ID.cast_to IInt (apply_abs IInt x)) + | Abs (ik, x) -> Int (ID.cast_to ik (apply_abs ik x)) end in begin match lv with diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index 86ec0d3bf7..f391231628 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -838,12 +838,12 @@ struct match exp with | BinOp ((Lt|Le) as op, CastE(t, Lval (Var v, NoOffset)), e,_) when tv -> begin match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil NoOffset)) with - | `Lifted (Abs arg) -> refineAbs op (CastE (t, arg)) e + | `Lifted (Abs (ik, arg)) -> refineAbs op (CastE (t, arg)) e | _ -> refine0 end | BinOp ((Lt|Le) as op, Lval (Var v, NoOffset), e, _) when tv -> begin match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil NoOffset)) with - | `Lifted (Abs arg) -> refineAbs op arg e + | `Lifted (Abs (ik, arg)) -> refineAbs op arg e | _ -> refine0 end | _ -> refine0 diff --git a/src/analyses/libraryDesc.ml b/src/analyses/libraryDesc.ml index 8cd3dfa1ba..45887b9c6b 100644 --- a/src/analyses/libraryDesc.ml +++ b/src/analyses/libraryDesc.ml @@ -27,7 +27,7 @@ type math = | Islessequal of (Basetype.CilExp.t * Basetype.CilExp.t) | Islessgreater of (Basetype.CilExp.t * Basetype.CilExp.t) | Isunordered of (Basetype.CilExp.t * Basetype.CilExp.t) - | Abs of Basetype.CilExp.t + | Abs of (CilType.Ikind.t * Basetype.CilExp.t) | Ceil of (CilType.Fkind.t * Basetype.CilExp.t) | Floor of (CilType.Fkind.t * Basetype.CilExp.t) | Fabs of (CilType.Fkind.t * Basetype.CilExp.t) @@ -160,7 +160,7 @@ module MathPrintable = struct | Islessequal (exp1, exp2) -> Pretty.dprintf "isLessEqual(%a, %a)" d_exp exp1 d_exp exp2 | Islessgreater (exp1, exp2) -> Pretty.dprintf "isLessGreater(%a, %a)" d_exp exp1 d_exp exp2 | Isunordered (exp1, exp2) -> Pretty.dprintf "isUnordered(%a, %a)" d_exp exp1 d_exp exp2 - | Abs exp -> Pretty.dprintf "abs(%a)" d_exp exp + | Abs (ik, exp) -> Pretty.dprintf "(%a )abs(%a)" d_ikind ik d_exp exp | Ceil (fk, exp) -> Pretty.dprintf "(%a )ceil(%a)" d_fkind fk d_exp exp | Floor (fk, exp) -> Pretty.dprintf "(%a )floor(%a)" d_fkind fk d_exp exp | Fabs (fk, exp) -> Pretty.dprintf "(%a )fabs(%a)" d_fkind fk d_exp exp diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index ae33f57f70..838d1817a9 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -130,7 +130,9 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("wcstombs", unknown ~attrs:[ThreadUnsafe] [drop "dst" [w]; drop "src" [r]; drop "size" []]); ("wcsrtombs", unknown ~attrs:[ThreadUnsafe] [drop "dst" [w]; drop "src" [r_deep; w]; drop "size" []; drop "ps" [r_deep; w_deep]]); ("mbstowcs", unknown [drop "dest" [w]; drop "src" [r]; drop "n" []]); - ("abs", special [__ "j" []] @@ fun j -> Math { fun_args = (Abs j) }); + ("abs", special [__ "j" []] @@ fun j -> Math { fun_args = (Abs (IInt, j)) }); + ("labs", special [__ "j" []] @@ fun j -> Math { fun_args = (Abs (ILong, j)) }); + ("llabs", special [__ "j" []] @@ fun j -> Math { fun_args = (Abs (ILongLong, j)) }); ("localtime_r", unknown [drop "timep" [r]; drop "result" [w]]); ("strpbrk", unknown [drop "s" [r]; drop "accept" [r]]); ("_setjmp", special [__ "env" [w]] @@ fun env -> Setjmp { env }); (* only has one underscore *) diff --git a/tests/regression/39-signed-overflows/08-labs.c b/tests/regression/39-signed-overflows/08-labs.c new file mode 100644 index 0000000000..a9c6773d11 --- /dev/null +++ b/tests/regression/39-signed-overflows/08-labs.c @@ -0,0 +1,22 @@ +//PARAM: --enable ana.int.interval --set ana.activated[+] tmpSpecial +#include +int main() { + long data; + if (data > (-0xffffffff - 1)) + { + if (labs(data) < 100) + { + __goblint_check(data < 100); + __goblint_check(-100 < data); + int result = data * data; //NOWARN + } + + if(labs(data) <= 100) + { + __goblint_check(data <= 100); + __goblint_check(-100 <= data); + int result = data * data; //NOWARN + } + } + return 8; +} diff --git a/tests/regression/39-signed-overflows/09-labs-sqrt.c b/tests/regression/39-signed-overflows/09-labs-sqrt.c new file mode 100644 index 0000000000..3a4b20a82b --- /dev/null +++ b/tests/regression/39-signed-overflows/09-labs-sqrt.c @@ -0,0 +1,10 @@ +//PARAM: --enable ana.int.interval --enable ana.float.interval --set ana.activated[+] tmpSpecial +#include +int main() { + int data; + if (data > (-0x7fffffff - 1) && llabs(data) < (long)sqrt((double)0x7fffffff)) + { + int result = data * data; //NOWARN + } + return 8; +} From 75b3b83e1500f515ca6f3c4fecb3fe4908507fca Mon Sep 17 00:00:00 2001 From: stilscher <66023521+stilscher@users.noreply.github.com> Date: Mon, 20 Nov 2023 20:19:07 +0100 Subject: [PATCH 1059/1312] add autotuner for math functions (activates tmpSpecial and float domain) --- src/autoTune.ml | 14 ++++++++++++++ src/common/util/options.schema.json | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/autoTune.ml b/src/autoTune.ml index 7ddc1aee43..77d91f9652 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -458,6 +458,17 @@ let wideningOption factors file = print_endline "Enabled widening thresholds"; } +let activateTmpSpecialAnalysis () = + let isMathFun = function + | LibraryDesc.Math _ -> true + | _ -> false + in + let hasMathFunctions = hasFunction isMathFun in + if hasMathFunctions then ( + print_endline @@ "math function -> enabling tmpSpecial analysis and floating-point domain"; + enableAnalyses ["tmpSpecial"]; + set_bool "ana.float.interval" true; + ) let estimateComplexity factors file = let pathsEstimate = factors.loops + factors.controlFlowStatements / 90 in @@ -518,6 +529,9 @@ let chooseConfig file = if isActivated "arrayDomain" then selectArrayDomains file; + if isActivated "tmpSpecialAnalysis" then + activateTmpSpecialAnalysis (); + let options = [] in let options = if isActivated "congruence" then (congruenceOption factors file)::options else options in let options = if isActivated "octagon" then (apronOctagonOption factors file)::options else options in diff --git a/src/common/util/options.schema.json b/src/common/util/options.schema.json index 4e282b19a4..b392f56cd6 100644 --- a/src/common/util/options.schema.json +++ b/src/common/util/options.schema.json @@ -544,7 +544,7 @@ "type": "array", "items": { "type": "string" }, "default": [ - "congruence", "singleThreaded", "specification", "mallocWrappers", "noRecursiveIntervals", "enums", "loopUnrollHeuristic", "arrayDomain", "octagon", "wideningThresholds", "memsafetySpecification" + "congruence", "singleThreaded", "specification", "mallocWrappers", "noRecursiveIntervals", "enums", "loopUnrollHeuristic", "arrayDomain", "octagon", "wideningThresholds", "memsafetySpecification", "tmpSpecialAnalysis" ] } }, From 464cdd35d3fb7b3358e9be0902035be0358511ea Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Tue, 21 Nov 2023 09:01:33 +0100 Subject: [PATCH 1060/1312] Add cram test for sv-comp no-ov verdict. --- tests/regression/29-svcomp/32-no-ov.t | 16 ++++++++++++++++ tests/regression/29-svcomp/dune | 2 ++ 2 files changed, 18 insertions(+) create mode 100644 tests/regression/29-svcomp/32-no-ov.t create mode 100644 tests/regression/29-svcomp/dune diff --git a/tests/regression/29-svcomp/32-no-ov.t b/tests/regression/29-svcomp/32-no-ov.t new file mode 100644 index 0000000000..92e53a914c --- /dev/null +++ b/tests/regression/29-svcomp/32-no-ov.t @@ -0,0 +1,16 @@ + $ goblint --enable ana.int.interval --enable ana.sv-comp.enabled --enable ana.sv-comp.functions --set ana.specification "CHECK( init(main()), LTL(G ! overflow) )" 32-no-ov.c + SV-COMP specification: CHECK( init(main()), LTL(G ! overflow) ) + [Warning][Integer > Overflow][CWE-190][CWE-191] Unsigned integer overflow and underflow (32-no-ov.c:5:6-5:159) + [Warning][Integer > Overflow][CWE-190][CWE-191] Unsigned integer overflow and underflow (32-no-ov.c:5:6-5:159) + [Warning][Integer > Overflow][CWE-191] Unsigned integer underflow (32-no-ov.c:5:6-5:159) + [Warning][Integer > Overflow][CWE-190] Signed integer overflow (32-no-ov.c:5:6-5:159) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 3 + dead: 0 + total lines: 3 + SV-COMP result: true + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 diff --git a/tests/regression/29-svcomp/dune b/tests/regression/29-svcomp/dune new file mode 100644 index 0000000000..23c0dd3290 --- /dev/null +++ b/tests/regression/29-svcomp/dune @@ -0,0 +1,2 @@ +(cram + (deps (glob_files *.c))) From e19f87e8c0647ebc84db6de4494d07c4817ab4c1 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Tue, 21 Nov 2023 10:16:58 +0200 Subject: [PATCH 1061/1312] Add multiple as argument to threadenter in threadIdDomain --- src/cdomains/threadIdDomain.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cdomains/threadIdDomain.ml b/src/cdomains/threadIdDomain.ml index a22b692921..d0c3f7b61b 100644 --- a/src/cdomains/threadIdDomain.ml +++ b/src/cdomains/threadIdDomain.ml @@ -342,9 +342,9 @@ struct module D = FlagConfiguredTID.D - let threadenter (t, d) node i v = + let threadenter ~multiple (t, d) node i v = match t with - | Thread tid -> List.map lift (FlagConfiguredTID.threadenter (tid, d) node i v) + | Thread tid -> List.map lift (FlagConfiguredTID.threadenter ~multiple (tid, d) node i v) | UnknownThread -> assert false let threadspawn = FlagConfiguredTID.threadspawn From 8b7994869c98119e50e8d19f857baccad7194628 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 21 Nov 2023 11:21:46 +0200 Subject: [PATCH 1062/1312] Fix invariant_set elements schema in YAML witnesses --- src/witness/yamlWitnessType.ml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/witness/yamlWitnessType.ml b/src/witness/yamlWitnessType.ml index f9bcf3235f..de9fa151d8 100644 --- a/src/witness/yamlWitnessType.ml +++ b/src/witness/yamlWitnessType.ml @@ -311,13 +311,16 @@ struct } let to_yaml {invariant_type} = - `O ([ - ("type", `String (InvariantType.invariant_type invariant_type)); - ] @ InvariantType.to_yaml' invariant_type) + `O [ + ("invariant", `O ([ + ("type", `String (InvariantType.invariant_type invariant_type)); + ] @ InvariantType.to_yaml' invariant_type) + ) + ] let of_yaml y = let open GobYaml in - let+ invariant_type = y |> InvariantType.of_yaml in + let+ invariant_type = y |> find "invariant" >>= InvariantType.of_yaml in {invariant_type} end From ca61360dd19e18bba2ddeba89c3f4046cf4764ad Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 21 Nov 2023 10:21:49 +0100 Subject: [PATCH 1063/1312] Add example where better privatization helps --- ...alid-memcleanup-multi-threaded-betterpiv.c | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 tests/regression/76-memleak/14-invalid-memcleanup-multi-threaded-betterpiv.c diff --git a/tests/regression/76-memleak/14-invalid-memcleanup-multi-threaded-betterpiv.c b/tests/regression/76-memleak/14-invalid-memcleanup-multi-threaded-betterpiv.c new file mode 100644 index 0000000000..c701461cb5 --- /dev/null +++ b/tests/regression/76-memleak/14-invalid-memcleanup-multi-threaded-betterpiv.c @@ -0,0 +1,33 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak --set ana.base.privatization mutex-meet-tid --set ana.path_sens[+] threadflag +#include +#include + +int *g; +int *m1; +int *m2; + +void *f1(void *arg) { + m1 = malloc(sizeof(int)); + // Thread t1 leaks m1 here + pthread_exit(NULL); //WARN +} + +void *f2(void *arg) { + m2 = malloc(sizeof(int)); + free(m2); // No leak for thread t2, since it calls free before exiting + pthread_exit(NULL); //NOWARN +} + +int main(int argc, char const *argv[]) { + g = malloc(sizeof(int)); + pthread_t t1; + pthread_create(&t1, NULL, f1, NULL); + + pthread_t t2; + pthread_create(&t2, NULL, f2, NULL); + + free(g); + + // main thread is not leaking anything + return 0; //NOWARN +} From 746014d9f9610afb4750c60fb50770a5d6f9cfb9 Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Tue, 21 Nov 2023 12:01:35 +0100 Subject: [PATCH 1064/1312] Extend test case. --- tests/regression/39-signed-overflows/06-shiftleft.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/regression/39-signed-overflows/06-shiftleft.c b/tests/regression/39-signed-overflows/06-shiftleft.c index aff853ee36..a8cb83381c 100644 --- a/tests/regression/39-signed-overflows/06-shiftleft.c +++ b/tests/regression/39-signed-overflows/06-shiftleft.c @@ -1,5 +1,6 @@ // PARAM: --enable ana.int.interval #include +#include int main() { int r; @@ -19,8 +20,12 @@ int main() __goblint_check(r >= 8); __goblint_check(r <= 16); - int regval; - unsigned long bla = (unsigned long )((1 << ((int )regval >> 6)) << 20); //WARN + int regval = INT_MAX; + int shift = ((int )regval >> 6); //NOWARN + int blub = 1 << shift; //WARN + + int regval2; + unsigned long bla = (unsigned long )((1 << ((int )regval2 >> 6)) << 20); //WARN return 0; } From 4e330e49d44226eaa1fb77d22f767d6af162c01c Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Tue, 21 Nov 2023 12:03:12 +0100 Subject: [PATCH 1065/1312] Remove unnecessary cast and parantheses. --- tests/regression/39-signed-overflows/06-shiftleft.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression/39-signed-overflows/06-shiftleft.c b/tests/regression/39-signed-overflows/06-shiftleft.c index a8cb83381c..7e790306ca 100644 --- a/tests/regression/39-signed-overflows/06-shiftleft.c +++ b/tests/regression/39-signed-overflows/06-shiftleft.c @@ -21,7 +21,7 @@ int main() __goblint_check(r <= 16); int regval = INT_MAX; - int shift = ((int )regval >> 6); //NOWARN + int shift = regval >> 6; //NOWARN int blub = 1 << shift; //WARN int regval2; From f6c9a52fb5c9fae1564ee038fd1c8ef374fa0304 Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Tue, 21 Nov 2023 12:30:35 +0100 Subject: [PATCH 1066/1312] Add tmpSpecialAnalysis to ana.autotune.activated in svcomp.json. --- conf/svcomp.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/conf/svcomp.json b/conf/svcomp.json index 73f99500b9..c915620987 100644 --- a/conf/svcomp.json +++ b/conf/svcomp.json @@ -71,7 +71,8 @@ "octagon", "wideningThresholds", "loopUnrollHeuristic", - "memsafetySpecification" + "memsafetySpecification", + "tmpSpecialAnalysis" ] } }, From ed90e61acff66fa96de9816d27951a65a30169a8 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 21 Nov 2023 15:56:00 +0100 Subject: [PATCH 1067/1312] Fix `BlobSize` for calloc --- src/analyses/base.ml | 13 +++++++++++-- src/analyses/memOutOfBounds.ml | 6 +++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 84be8c7a19..5b9a00313e 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1139,6 +1139,9 @@ struct (* interpreter end *) + let is_not_alloc_var ctx v = + not (ctx.ask (Queries.IsAllocVar v)) + let is_not_heap_alloc_var ctx v = let is_alloc = ctx.ask (Queries.IsAllocVar v) in not is_alloc || (is_alloc && not (ctx.ask (Queries.IsHeapVar v))) @@ -1277,7 +1280,7 @@ struct (* If there's a non-heap var or an offset in the lval set, we answer with bottom *) (* If we're asking for the BlobSize from the base address, then don't check for offsets => we want to avoid getting bot *) if AD.exists (function - | Addr (v,o) -> is_not_heap_alloc_var ctx v || (if not from_base_addr then o <> `NoOffset else false) + | Addr (v,o) -> is_not_alloc_var ctx v || (if not from_base_addr then o <> `NoOffset else false) | _ -> false) a then Queries.Result.bot q else ( @@ -1289,9 +1292,15 @@ struct else a in - let r = get ~full:true (Analyses.ask_of_ctx ctx) ctx.global ctx.local a None in + let r = get ~full:true (Analyses.ask_of_ctx ctx) ctx.global ctx.local a None in (* ignore @@ printf "BlobSize %a = %a\n" d_plainexp e VD.pretty r; *) (match r with + | Array a -> + (* unroll into array for Calloc calls *) + (match ValueDomain.CArrays.get (Queries.to_value_domain_ask (Analyses.ask_of_ctx ctx)) a (None, (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) BI.zero)) with + | Blob (_,s,_) -> `Lifted s + | _ -> Queries.Result.top q + ) | Blob (_,s,_) -> `Lifted s | _ -> Queries.Result.top q) ) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index fc60352298..9dccf77ff9 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -69,17 +69,17 @@ struct in host_contains_a_ptr host || offset_contains_a_ptr offset - let points_to_heap_only ctx ptr = + let points_to_alloc_only ctx ptr = match ctx.ask (Queries.MayPointTo ptr) with | a when not (Queries.AD.is_top a)-> Queries.AD.for_all (function - | Addr (v, o) -> ctx.ask (Queries.IsHeapVar v) + | Addr (v, o) -> ctx.ask (Queries.IsAllocVar v) | _ -> false ) a | _ -> false let get_size_of_ptr_target ctx ptr = - if points_to_heap_only ctx ptr then + if points_to_alloc_only ctx ptr then (* Ask for BlobSize from the base address (the second component being set to true) in order to avoid BlobSize giving us bot *) ctx.ask (Queries.BlobSize {exp = ptr; base_address = true}) else From 9c808c98eb55baf8e0f5b4839ca5cd99ae68747b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 21 Nov 2023 17:22:04 +0200 Subject: [PATCH 1068/1312] Adapt cram test from #1258 to #1252 --- tests/regression/29-svcomp/32-no-ov.t | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/regression/29-svcomp/32-no-ov.t b/tests/regression/29-svcomp/32-no-ov.t index 92e53a914c..85eb90c185 100644 --- a/tests/regression/29-svcomp/32-no-ov.t +++ b/tests/regression/29-svcomp/32-no-ov.t @@ -1,7 +1,7 @@ $ goblint --enable ana.int.interval --enable ana.sv-comp.enabled --enable ana.sv-comp.functions --set ana.specification "CHECK( init(main()), LTL(G ! overflow) )" 32-no-ov.c SV-COMP specification: CHECK( init(main()), LTL(G ! overflow) ) - [Warning][Integer > Overflow][CWE-190][CWE-191] Unsigned integer overflow and underflow (32-no-ov.c:5:6-5:159) - [Warning][Integer > Overflow][CWE-190][CWE-191] Unsigned integer overflow and underflow (32-no-ov.c:5:6-5:159) + [Warning][Integer > Overflow][CWE-190] Unsigned integer overflow (32-no-ov.c:5:6-5:159) + [Warning][Integer > Overflow][CWE-190] Unsigned integer overflow (32-no-ov.c:5:6-5:159) [Warning][Integer > Overflow][CWE-191] Unsigned integer underflow (32-no-ov.c:5:6-5:159) [Warning][Integer > Overflow][CWE-190] Signed integer overflow (32-no-ov.c:5:6-5:159) [Info][Deadcode] Logical lines of code (LLoC) summary: From 2cc915fe1757fcd032d17b8c017f052729430d21 Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Tue, 21 Nov 2023 17:16:47 +0100 Subject: [PATCH 1069/1312] Check at end of main thread that the program is certainly single-threaded. If other threads are not joined, they may be killed by the main thread returning. This will possibly leak memory. --- src/analyses/memLeak.ml | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index 8d83bcee83..4d37992a21 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -22,10 +22,16 @@ struct let context _ d = d + let must_be_single_threaded ~since_start ctx = + ctx.ask (Queries.MustBeSingleThreaded { since_start }) + + let was_malloc_called ctx = + ctx.global () + (* HELPER FUNCTIONS *) let warn_for_multi_threaded_due_to_abort ctx = - let malloc_called = ctx.global () in - if not (ctx.ask (Queries.MustBeSingleThreaded { since_start = true })) && malloc_called then ( + let malloc_called = was_malloc_called ctx in + if not (must_be_single_threaded ctx ~since_start:true) && malloc_called then ( set_mem_safety_flag InvalidMemTrack; set_mem_safety_flag InvalidMemcleanup; M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Program aborted while running in multi-threaded mode. A memory leak might occur" @@ -56,11 +62,18 @@ struct let return ctx (exp:exp option) (f:fundec) : D.t = (* Check for a valid-memcleanup and memtrack violation in a multi-threaded setting *) (* The check for multi-threadedness is to ensure that valid-memtrack and valid-memclenaup are treated separately for single-threaded programs *) - if (ctx.ask (Queries.MayBeThreadReturn) && not (ctx.ask (Queries.MustBeSingleThreaded { since_start = true }))) then ( - warn_for_thread_return_or_exit ctx true + if (ctx.ask (Queries.MayBeThreadReturn) && not (must_be_single_threaded ctx ~since_start:true)) then ( + warn_for_thread_return_or_exit ctx true ); (* Returning from "main" is one possible program exit => need to check for memory leaks *) - if f.svar.vname = "main" then check_for_mem_leak ctx; + if f.svar.vname = "main" then ( + check_for_mem_leak ctx; + if not (must_be_single_threaded ctx ~since_start:false) && was_malloc_called ctx then begin + set_mem_safety_flag InvalidMemTrack; + set_mem_safety_flag InvalidMemcleanup; + M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Possible memory leak: Memory was allocated in a multithreaded program, but not all threads are joined." + end + ); ctx.local let special ctx (lval:lval option) (f:varinfo) (arglist:exp list) : D.t = From 45ec8a663791d3675ca05ac68355ce1b1ea2c8c1 Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Tue, 21 Nov 2023 17:18:30 +0100 Subject: [PATCH 1070/1312] Add test case for memory leaking from a thead that is not joined, add thread_joins to other test cases. --- .../08-invalid-memcleanup-multi-threaded.c | 6 +++++- ...9-invalid-memcleanup-multi-threaded-abort.c | 7 +++++-- ...valid-memcleanup-multi-threaded-betterpiv.c | 5 ++++- .../76-memleak/15-mem-leak-not-joined-thread.c | 18 ++++++++++++++++++ 4 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 tests/regression/76-memleak/15-mem-leak-not-joined-thread.c diff --git a/tests/regression/76-memleak/08-invalid-memcleanup-multi-threaded.c b/tests/regression/76-memleak/08-invalid-memcleanup-multi-threaded.c index 513a36db95..65e6e4e766 100644 --- a/tests/regression/76-memleak/08-invalid-memcleanup-multi-threaded.c +++ b/tests/regression/76-memleak/08-invalid-memcleanup-multi-threaded.c @@ -1,4 +1,4 @@ -//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak --set ana.activated[+] thread --set ana.activated[+] threadid #include #include @@ -7,6 +7,7 @@ int *m1; void *f1(void *arg) { m1 = malloc(sizeof(int)); + free(m1); // Thread t1 leaks m1 here pthread_exit(NULL); //WARN } @@ -28,6 +29,9 @@ int main(int argc, char const *argv[]) { free(g); + pthread_join(t1, NULL); + pthread_join(t2, NULL); + // main thread is not leaking anything return 0; //NOWARN } diff --git a/tests/regression/76-memleak/09-invalid-memcleanup-multi-threaded-abort.c b/tests/regression/76-memleak/09-invalid-memcleanup-multi-threaded-abort.c index 977510b9bb..b991433f4d 100644 --- a/tests/regression/76-memleak/09-invalid-memcleanup-multi-threaded-abort.c +++ b/tests/regression/76-memleak/09-invalid-memcleanup-multi-threaded-abort.c @@ -1,4 +1,4 @@ -//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak --set ana.activated[+] thread --set ana.activated[+] threadid #include #include @@ -25,9 +25,12 @@ int main(int argc, char const *argv[]) { pthread_t t2; pthread_create(&t2, NULL, f2, NULL); - + free(g); + pthread_join(t1, NULL); + pthread_join(t2, NULL); + // main thread is not leaking anything return 0; //NOWARN } diff --git a/tests/regression/76-memleak/14-invalid-memcleanup-multi-threaded-betterpiv.c b/tests/regression/76-memleak/14-invalid-memcleanup-multi-threaded-betterpiv.c index c701461cb5..7ad9194d6e 100644 --- a/tests/regression/76-memleak/14-invalid-memcleanup-multi-threaded-betterpiv.c +++ b/tests/regression/76-memleak/14-invalid-memcleanup-multi-threaded-betterpiv.c @@ -1,4 +1,4 @@ -//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak --set ana.base.privatization mutex-meet-tid --set ana.path_sens[+] threadflag +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak --set ana.base.privatization mutex-meet-tid --set ana.path_sens[+] threadflag --set ana.activated[+] thread --set ana.activated[+] threadid #include #include @@ -28,6 +28,9 @@ int main(int argc, char const *argv[]) { free(g); + pthread_join(t1, NULL); + pthread_join(t2, NULL); + // main thread is not leaking anything return 0; //NOWARN } diff --git a/tests/regression/76-memleak/15-mem-leak-not-joined-thread.c b/tests/regression/76-memleak/15-mem-leak-not-joined-thread.c new file mode 100644 index 0000000000..c60809a9f4 --- /dev/null +++ b/tests/regression/76-memleak/15-mem-leak-not-joined-thread.c @@ -0,0 +1,18 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak --set ana.activated[+] thread --set ana.activated[+] threadid +#include +#include + +int *m1; + +void *f1(void *arg) { + m1 = malloc(sizeof(int)); + while (1); +} + +int main(int argc, char const *argv[]) { + pthread_t t1; + pthread_create(&t1, NULL, f1, NULL); + + // memory from thread f1 which was not joined into main, is not freed + return 0; //WARN +} \ No newline at end of file From 56c4d620be0c7e3a3d2deb92197d3d161a850445 Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Tue, 21 Nov 2023 17:40:44 +0100 Subject: [PATCH 1071/1312] Add test case with pthread_exit called in main, remove threadid analysis from params as it is not needed. --- .../08-invalid-memcleanup-multi-threaded.c | 4 ++-- ...-invalid-memcleanup-multi-threaded-abort.c | 3 +-- ...alid-memcleanup-multi-threaded-betterpiv.c | 2 +- .../15-mem-leak-not-joined-thread.c | 2 +- .../16-no-mem-leak-thread-exit-main.c | 23 +++++++++++++++++++ 5 files changed, 28 insertions(+), 6 deletions(-) create mode 100644 tests/regression/76-memleak/16-no-mem-leak-thread-exit-main.c diff --git a/tests/regression/76-memleak/08-invalid-memcleanup-multi-threaded.c b/tests/regression/76-memleak/08-invalid-memcleanup-multi-threaded.c index 65e6e4e766..89dc7a3416 100644 --- a/tests/regression/76-memleak/08-invalid-memcleanup-multi-threaded.c +++ b/tests/regression/76-memleak/08-invalid-memcleanup-multi-threaded.c @@ -1,4 +1,4 @@ -//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak --set ana.activated[+] thread --set ana.activated[+] threadid +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak --set ana.activated[+] thread #include #include @@ -26,7 +26,7 @@ int main(int argc, char const *argv[]) { pthread_t t2; pthread_create(&t2, NULL, f2, NULL); - + free(g); pthread_join(t1, NULL); diff --git a/tests/regression/76-memleak/09-invalid-memcleanup-multi-threaded-abort.c b/tests/regression/76-memleak/09-invalid-memcleanup-multi-threaded-abort.c index b991433f4d..eaba1e91b5 100644 --- a/tests/regression/76-memleak/09-invalid-memcleanup-multi-threaded-abort.c +++ b/tests/regression/76-memleak/09-invalid-memcleanup-multi-threaded-abort.c @@ -1,5 +1,4 @@ -//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak --set ana.activated[+] thread --set ana.activated[+] threadid -#include +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak --set ana.activated[+] thread #include int *g; diff --git a/tests/regression/76-memleak/14-invalid-memcleanup-multi-threaded-betterpiv.c b/tests/regression/76-memleak/14-invalid-memcleanup-multi-threaded-betterpiv.c index 7ad9194d6e..9f636ab587 100644 --- a/tests/regression/76-memleak/14-invalid-memcleanup-multi-threaded-betterpiv.c +++ b/tests/regression/76-memleak/14-invalid-memcleanup-multi-threaded-betterpiv.c @@ -1,4 +1,4 @@ -//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak --set ana.base.privatization mutex-meet-tid --set ana.path_sens[+] threadflag --set ana.activated[+] thread --set ana.activated[+] threadid +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak --set ana.base.privatization mutex-meet-tid --set ana.path_sens[+] threadflag --set ana.activated[+] thread #include #include diff --git a/tests/regression/76-memleak/15-mem-leak-not-joined-thread.c b/tests/regression/76-memleak/15-mem-leak-not-joined-thread.c index c60809a9f4..21c1992fc3 100644 --- a/tests/regression/76-memleak/15-mem-leak-not-joined-thread.c +++ b/tests/regression/76-memleak/15-mem-leak-not-joined-thread.c @@ -1,4 +1,4 @@ -//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak --set ana.activated[+] thread --set ana.activated[+] threadid +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak --set ana.activated[+] thread #include #include diff --git a/tests/regression/76-memleak/16-no-mem-leak-thread-exit-main.c b/tests/regression/76-memleak/16-no-mem-leak-thread-exit-main.c new file mode 100644 index 0000000000..5fb89113d2 --- /dev/null +++ b/tests/regression/76-memleak/16-no-mem-leak-thread-exit-main.c @@ -0,0 +1,23 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak --set ana.activated[+] thread +#include +#include + +int *m1; + +void *f1(void *arg) { + m1 = malloc(sizeof(int)); + while (1); +} + +int main(int argc, char const *argv[]) { + pthread_t t1; + pthread_create(&t1, NULL, f1, NULL); + + pthread_exit(NULL); + + pthread_join(t1, NULL); + + // A pthread_join called in main will wait for other threads to finish + // Therefore, no memory leak here + return 0; // NOWARN +} \ No newline at end of file From 2fef812ff9a11bebb6ac4b78ca010fde6bbfeea9 Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Tue, 21 Nov 2023 17:57:07 +0100 Subject: [PATCH 1072/1312] Add testcases for thread return and pthread_exit in thread different from main. --- .../15-mem-leak-not-joined-thread.c | 1 + .../16-no-mem-leak-thread-exit-main.c | 3 +-- .../76-memleak/17-mem-leak-thread-return.c | 26 ++++++++++++++++++ .../76-memleak/18-mem-leak-thread-exit.c | 27 +++++++++++++++++++ 4 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 tests/regression/76-memleak/17-mem-leak-thread-return.c create mode 100644 tests/regression/76-memleak/18-mem-leak-thread-exit.c diff --git a/tests/regression/76-memleak/15-mem-leak-not-joined-thread.c b/tests/regression/76-memleak/15-mem-leak-not-joined-thread.c index 21c1992fc3..15f249ffe1 100644 --- a/tests/regression/76-memleak/15-mem-leak-not-joined-thread.c +++ b/tests/regression/76-memleak/15-mem-leak-not-joined-thread.c @@ -7,6 +7,7 @@ int *m1; void *f1(void *arg) { m1 = malloc(sizeof(int)); while (1); + return NULL; } int main(int argc, char const *argv[]) { diff --git a/tests/regression/76-memleak/16-no-mem-leak-thread-exit-main.c b/tests/regression/76-memleak/16-no-mem-leak-thread-exit-main.c index 5fb89113d2..663ea26663 100644 --- a/tests/regression/76-memleak/16-no-mem-leak-thread-exit-main.c +++ b/tests/regression/76-memleak/16-no-mem-leak-thread-exit-main.c @@ -7,6 +7,7 @@ int *m1; void *f1(void *arg) { m1 = malloc(sizeof(int)); while (1); + return NULL; } int main(int argc, char const *argv[]) { @@ -15,8 +16,6 @@ int main(int argc, char const *argv[]) { pthread_exit(NULL); - pthread_join(t1, NULL); - // A pthread_join called in main will wait for other threads to finish // Therefore, no memory leak here return 0; // NOWARN diff --git a/tests/regression/76-memleak/17-mem-leak-thread-return.c b/tests/regression/76-memleak/17-mem-leak-thread-return.c new file mode 100644 index 0000000000..bec64ca22f --- /dev/null +++ b/tests/regression/76-memleak/17-mem-leak-thread-return.c @@ -0,0 +1,26 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak --set ana.activated[+] thread +#include +#include + +int *m1; + +void *f2(void *arg) { + m1 = malloc(sizeof(int)); + while (1); + return NULL; +} + +void *f1(void *arg) { + pthread_t t2; + pthread_create(&t2, NULL, f2, NULL); + + return NULL; +} + +int main(int argc, char const *argv[]) { + pthread_t t1; + pthread_create(&t1, NULL, f1, NULL); + pthread_join(t1, NULL); + + return 0; // WARN +} \ No newline at end of file diff --git a/tests/regression/76-memleak/18-mem-leak-thread-exit.c b/tests/regression/76-memleak/18-mem-leak-thread-exit.c new file mode 100644 index 0000000000..e98ae3f346 --- /dev/null +++ b/tests/regression/76-memleak/18-mem-leak-thread-exit.c @@ -0,0 +1,27 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak --set ana.activated[+] thread +#include +#include + +int *m1; + +void *f2(void *arg) { + m1 = malloc(sizeof(int)); + while (1); + return NULL; +} + +void *f1(void *arg) { + pthread_t t2; + pthread_create(&t2, NULL, f2, NULL); + + pthread_exit(NULL); + return NULL; +} + +int main(int argc, char const *argv[]) { + pthread_t t1; + pthread_create(&t1, NULL, f1, NULL); + pthread_join(t1, NULL); + + return 0; // WARN +} \ No newline at end of file From d1d85b3496228a0979e6f21039409fd3473ef08a Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 21 Nov 2023 18:01:33 +0100 Subject: [PATCH 1073/1312] Add test --- tests/regression/74-invalid_deref/30-calloc.c | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 tests/regression/74-invalid_deref/30-calloc.c diff --git a/tests/regression/74-invalid_deref/30-calloc.c b/tests/regression/74-invalid_deref/30-calloc.c new file mode 100644 index 0000000000..624e9c212d --- /dev/null +++ b/tests/regression/74-invalid_deref/30-calloc.c @@ -0,0 +1,9 @@ +//PARAM: --set ana.activated[+] useAfterFree --set ana.activated[+] threadJoins --set ana.activated[+] memOutOfBounds --enable ana.int.interval --set ana.base.arrays.domain partitioned +#include +#include + +int main(int argc, char **argv) +{ + int* ptrCalloc = calloc(100UL,8UL); + *ptrCalloc = 8; //NOWARN +} From 3485100b77209734c346fc063a7f3fdff59cf8e8 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Tue, 21 Nov 2023 20:11:49 +0200 Subject: [PATCH 1074/1312] Add test for special function lval --- tests/regression/00-sanity/51-base-special-lval.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 tests/regression/00-sanity/51-base-special-lval.c diff --git a/tests/regression/00-sanity/51-base-special-lval.c b/tests/regression/00-sanity/51-base-special-lval.c new file mode 100644 index 0000000000..8f74a1babe --- /dev/null +++ b/tests/regression/00-sanity/51-base-special-lval.c @@ -0,0 +1,13 @@ +// Making sure special function lval is not invalidated recursively +#include + +extern int * anIntPlease(); +int main() { + int x = 0; + int *p = &x; + p = anIntPlease(); + + __goblint_check(x == 0); + + return 0; +} From 60923ea18f414a2d609497f2f1f03b136d9bb3d0 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Tue, 21 Nov 2023 20:15:31 +0200 Subject: [PATCH 1075/1312] Special function lval not invalidated recursively --- src/analyses/base.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 84be8c7a19..8b6350aa2d 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2123,7 +2123,7 @@ struct let invalidate_ret_lv st = match lv with | Some lv -> if M.tracing then M.tracel "invalidate" "Invalidating lhs %a for function call %s\n" d_plainlval lv f.vname; - invalidate ~ctx (Analyses.ask_of_ctx ctx) ctx.global st [Cil.mkAddrOrStartOf lv] + invalidate ~deep:false ~ctx (Analyses.ask_of_ctx ctx) ctx.global st [Cil.mkAddrOrStartOf lv] | None -> st in let addr_type_of_exp exp = @@ -2328,7 +2328,7 @@ struct | _ -> failwith ("non-floating-point argument in call to function "^f.vname) end in - let apply_abs ik x = +let apply_abs ik x = let eval_x = eval_rv (Analyses.ask_of_ctx ctx) gs st x in begin match eval_x with | Int int_x -> From 895bd9fe468a064016f5c130f9f38174f4949369 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Tue, 21 Nov 2023 20:35:29 +0200 Subject: [PATCH 1076/1312] Update cram tests --- tests/regression/04-mutex/49-type-invariants.t | 4 ---- tests/regression/04-mutex/77-type-nested-fields.t | 4 ---- tests/regression/04-mutex/79-type-nested-fields-deep1.t | 4 ---- tests/regression/04-mutex/80-type-nested-fields-deep2.t | 4 ---- tests/regression/04-mutex/90-distribute-fields-type-1.t | 4 ---- tests/regression/04-mutex/91-distribute-fields-type-2.t | 4 ---- tests/regression/04-mutex/92-distribute-fields-type-deep.t | 4 ---- tests/regression/04-mutex/93-distribute-fields-type-global.t | 2 -- 8 files changed, 30 deletions(-) diff --git a/tests/regression/04-mutex/49-type-invariants.t b/tests/regression/04-mutex/49-type-invariants.t index 4c105d1559..4b8118eec1 100644 --- a/tests/regression/04-mutex/49-type-invariants.t +++ b/tests/regression/04-mutex/49-type-invariants.t @@ -14,8 +14,6 @@ live: 7 dead: 0 total lines: 7 - [Info][Unsound] Unknown address in {&tmp} has escaped. (49-type-invariants.c:21:3-21:21) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (49-type-invariants.c:21:3-21:21) [Info][Unsound] Write to unknown address: privatization is unsound. (49-type-invariants.c:21:3-21:21) [Info][Imprecise] INVALIDATING ALL GLOBALS! (49-type-invariants.c:21:3-21:21) [Info][Imprecise] Invalidating expressions: AddrOf(Var(s, NoOffset)) (49-type-invariants.c:21:3-21:21) @@ -39,8 +37,6 @@ live: 7 dead: 0 total lines: 7 - [Info][Unsound] Unknown address in {&tmp} has escaped. (49-type-invariants.c:21:3-21:21) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (49-type-invariants.c:21:3-21:21) [Info][Unsound] Write to unknown address: privatization is unsound. (49-type-invariants.c:21:3-21:21) [Info][Imprecise] INVALIDATING ALL GLOBALS! (49-type-invariants.c:21:3-21:21) [Info][Imprecise] Invalidating expressions: AddrOf(Var(s, NoOffset)) (49-type-invariants.c:21:3-21:21) diff --git a/tests/regression/04-mutex/77-type-nested-fields.t b/tests/regression/04-mutex/77-type-nested-fields.t index bb935cb0ed..68d9cdb779 100644 --- a/tests/regression/04-mutex/77-type-nested-fields.t +++ b/tests/regression/04-mutex/77-type-nested-fields.t @@ -15,11 +15,7 @@ live: 7 dead: 0 total lines: 7 - [Info][Unsound] Unknown address in {&tmp} has escaped. (77-type-nested-fields.c:31:3-31:20) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (77-type-nested-fields.c:31:3-31:20) [Info][Unsound] Write to unknown address: privatization is unsound. (77-type-nested-fields.c:31:3-31:20) - [Info][Unsound] Unknown address in {&tmp} has escaped. (77-type-nested-fields.c:38:3-38:22) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (77-type-nested-fields.c:38:3-38:22) [Info][Unsound] Write to unknown address: privatization is unsound. (77-type-nested-fields.c:38:3-38:22) [Info][Imprecise] INVALIDATING ALL GLOBALS! (77-type-nested-fields.c:31:3-31:20) [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (77-type-nested-fields.c:31:3-31:20) diff --git a/tests/regression/04-mutex/79-type-nested-fields-deep1.t b/tests/regression/04-mutex/79-type-nested-fields-deep1.t index ba1399d225..85f7bfb709 100644 --- a/tests/regression/04-mutex/79-type-nested-fields-deep1.t +++ b/tests/regression/04-mutex/79-type-nested-fields-deep1.t @@ -15,11 +15,7 @@ live: 7 dead: 0 total lines: 7 - [Info][Unsound] Unknown address in {&tmp} has escaped. (79-type-nested-fields-deep1.c:36:3-36:20) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (79-type-nested-fields-deep1.c:36:3-36:20) [Info][Unsound] Write to unknown address: privatization is unsound. (79-type-nested-fields-deep1.c:36:3-36:20) - [Info][Unsound] Unknown address in {&tmp} has escaped. (79-type-nested-fields-deep1.c:43:3-43:24) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (79-type-nested-fields-deep1.c:43:3-43:24) [Info][Unsound] Write to unknown address: privatization is unsound. (79-type-nested-fields-deep1.c:43:3-43:24) [Info][Imprecise] INVALIDATING ALL GLOBALS! (79-type-nested-fields-deep1.c:36:3-36:20) [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (79-type-nested-fields-deep1.c:36:3-36:20) diff --git a/tests/regression/04-mutex/80-type-nested-fields-deep2.t b/tests/regression/04-mutex/80-type-nested-fields-deep2.t index 71bdcfb2e2..a2e9e2ab15 100644 --- a/tests/regression/04-mutex/80-type-nested-fields-deep2.t +++ b/tests/regression/04-mutex/80-type-nested-fields-deep2.t @@ -15,11 +15,7 @@ live: 7 dead: 0 total lines: 7 - [Info][Unsound] Unknown address in {&tmp} has escaped. (80-type-nested-fields-deep2.c:36:3-36:22) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (80-type-nested-fields-deep2.c:36:3-36:22) [Info][Unsound] Write to unknown address: privatization is unsound. (80-type-nested-fields-deep2.c:36:3-36:22) - [Info][Unsound] Unknown address in {&tmp} has escaped. (80-type-nested-fields-deep2.c:43:3-43:24) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (80-type-nested-fields-deep2.c:43:3-43:24) [Info][Unsound] Write to unknown address: privatization is unsound. (80-type-nested-fields-deep2.c:43:3-43:24) [Info][Imprecise] INVALIDATING ALL GLOBALS! (80-type-nested-fields-deep2.c:36:3-36:22) [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (80-type-nested-fields-deep2.c:36:3-36:22) diff --git a/tests/regression/04-mutex/90-distribute-fields-type-1.t b/tests/regression/04-mutex/90-distribute-fields-type-1.t index 46435045b9..a3b5faf083 100644 --- a/tests/regression/04-mutex/90-distribute-fields-type-1.t +++ b/tests/regression/04-mutex/90-distribute-fields-type-1.t @@ -17,11 +17,7 @@ live: 7 dead: 0 total lines: 7 - [Info][Unsound] Unknown address in {&tmp} has escaped. (90-distribute-fields-type-1.c:31:3-31:20) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (90-distribute-fields-type-1.c:31:3-31:20) [Info][Unsound] Write to unknown address: privatization is unsound. (90-distribute-fields-type-1.c:31:3-31:20) - [Info][Unsound] Unknown address in {&tmp} has escaped. (90-distribute-fields-type-1.c:39:3-39:17) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (90-distribute-fields-type-1.c:39:3-39:17) [Info][Unsound] Write to unknown address: privatization is unsound. (90-distribute-fields-type-1.c:39:3-39:17) [Info][Imprecise] INVALIDATING ALL GLOBALS! (90-distribute-fields-type-1.c:31:3-31:20) [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (90-distribute-fields-type-1.c:31:3-31:20) diff --git a/tests/regression/04-mutex/91-distribute-fields-type-2.t b/tests/regression/04-mutex/91-distribute-fields-type-2.t index c7e66c0527..5773245114 100644 --- a/tests/regression/04-mutex/91-distribute-fields-type-2.t +++ b/tests/regression/04-mutex/91-distribute-fields-type-2.t @@ -17,11 +17,7 @@ live: 7 dead: 0 total lines: 7 - [Info][Unsound] Unknown address in {&tmp} has escaped. (91-distribute-fields-type-2.c:32:3-32:17) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (91-distribute-fields-type-2.c:32:3-32:17) [Info][Unsound] Write to unknown address: privatization is unsound. (91-distribute-fields-type-2.c:32:3-32:17) - [Info][Unsound] Unknown address in {&tmp} has escaped. (91-distribute-fields-type-2.c:40:3-40:17) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (91-distribute-fields-type-2.c:40:3-40:17) [Info][Unsound] Write to unknown address: privatization is unsound. (91-distribute-fields-type-2.c:40:3-40:17) [Info][Imprecise] INVALIDATING ALL GLOBALS! (91-distribute-fields-type-2.c:32:3-32:17) [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (91-distribute-fields-type-2.c:32:3-32:17) diff --git a/tests/regression/04-mutex/92-distribute-fields-type-deep.t b/tests/regression/04-mutex/92-distribute-fields-type-deep.t index 4fc1c7e101..798374d63c 100644 --- a/tests/regression/04-mutex/92-distribute-fields-type-deep.t +++ b/tests/regression/04-mutex/92-distribute-fields-type-deep.t @@ -17,11 +17,7 @@ live: 7 dead: 0 total lines: 7 - [Info][Unsound] Unknown address in {&tmp} has escaped. (92-distribute-fields-type-deep.c:36:3-36:20) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (92-distribute-fields-type-deep.c:36:3-36:20) [Info][Unsound] Write to unknown address: privatization is unsound. (92-distribute-fields-type-deep.c:36:3-36:20) - [Info][Unsound] Unknown address in {&tmp} has escaped. (92-distribute-fields-type-deep.c:44:3-44:17) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (92-distribute-fields-type-deep.c:44:3-44:17) [Info][Unsound] Write to unknown address: privatization is unsound. (92-distribute-fields-type-deep.c:44:3-44:17) [Info][Imprecise] INVALIDATING ALL GLOBALS! (92-distribute-fields-type-deep.c:36:3-36:20) [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (92-distribute-fields-type-deep.c:36:3-36:20) diff --git a/tests/regression/04-mutex/93-distribute-fields-type-global.t b/tests/regression/04-mutex/93-distribute-fields-type-global.t index bf34d99936..07999854ff 100644 --- a/tests/regression/04-mutex/93-distribute-fields-type-global.t +++ b/tests/regression/04-mutex/93-distribute-fields-type-global.t @@ -16,8 +16,6 @@ live: 7 dead: 0 total lines: 7 - [Info][Unsound] Unknown address in {&tmp} has escaped. (93-distribute-fields-type-global.c:13:3-13:29) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (93-distribute-fields-type-global.c:13:3-13:29) [Info][Unsound] Write to unknown address: privatization is unsound. (93-distribute-fields-type-global.c:13:3-13:29) [Info][Imprecise] INVALIDATING ALL GLOBALS! (93-distribute-fields-type-global.c:13:3-13:29) [Info][Imprecise] Invalidating expressions: AddrOf(Var(s, NoOffset)) (93-distribute-fields-type-global.c:13:3-13:29) From 645b03cfa678acbf3fdc585d4ee0a7d71e8b9688 Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Tue, 21 Nov 2023 20:31:00 +0100 Subject: [PATCH 1077/1312] ThreadAnalysis: Handle pthread_exit like return from thread. --- src/analyses/threadAnalysis.ml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/analyses/threadAnalysis.ml b/src/analyses/threadAnalysis.ml index 0264f4b700..d9140dbb37 100644 --- a/src/analyses/threadAnalysis.ml +++ b/src/analyses/threadAnalysis.ml @@ -22,7 +22,7 @@ struct module P = IdentityP (D) (* transfer functions *) - let return ctx (exp:exp option) (f:fundec) : D.t = + let return ctx (exp:exp option) _ : D.t = let tid = ThreadId.get_current (Analyses.ask_of_ctx ctx) in begin match tid with | `Lifted tid -> ctx.sideg tid (false, TS.bot (), not (D.is_empty ctx.local)) @@ -64,6 +64,8 @@ struct | [t] -> join_thread ctx.local t (* single thread *) | _ -> ctx.local (* if several possible threads are may-joined, none are must-joined *) | exception SetDomain.Unsupported _ -> ctx.local) + | ThreadExit { ret_val } -> + return ctx (Some ret_val) () | _ -> ctx.local let query ctx (type a) (q: a Queries.t): a Queries.result = From 9153eb3becd5904b99aa8250e50b2cd22f74a128 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 21 Nov 2023 21:04:04 +0100 Subject: [PATCH 1078/1312] Use `AD.fold` instead of `List.fold_left` --- src/analyses/memLeak.ml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index 3079faae1f..f26157fdd0 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -117,12 +117,14 @@ struct match ValueDomain.Structs.get s field with | Queries.VD.Address a -> let reachable_from_addr_set = - List.fold_left (fun acc_addr addr -> + Queries.AD.fold (fun addr acc_addr -> match addr with - | Queries.AD.Addr.Addr (v, _) -> (v :: get_reachable_mem_from_str_ptr_globals [v] ctx) @ acc_addr + | Queries.AD.Addr.Addr (v, _) -> + let reachable_from_v = Queries.AD.of_list (List.map (fun v -> Queries.AD.Addr.Addr (v, `NoOffset)) (get_reachable_mem_from_str_ptr_globals [v] ctx)) in + Queries.AD.join (Queries.AD.add addr reachable_from_v) acc_addr | _ -> acc_addr - ) [] (Queries.AD.elements a) - in reachable_from_addr_set @ acc_field + ) a (Queries.AD.empty ()) + in (Queries.AD.to_var_may reachable_from_addr_set) @ acc_field | _ -> acc_field ) [] fields in From 17ebe80cb217b8d6837f7b892fb75a3e11f0e3b0 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 22 Nov 2023 09:29:30 +0100 Subject: [PATCH 1079/1312] Enable `mutex-meet-tid` for ValidDeref --- src/autoTune.ml | 6 +++++- .../74-invalid_deref/31-multithreaded.c | 21 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 tests/regression/74-invalid_deref/31-multithreaded.c diff --git a/src/autoTune.ml b/src/autoTune.ml index fefdeb32fd..dca3ee405a 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -222,7 +222,11 @@ let focusOnMemSafetySpecification (spec: Svcomp.Specification.t) = print_endline "Setting \"cil.addNestedScopeAttr\" to true"; set_bool "cil.addNestedScopeAttr" true; print_endline @@ "Specification: ValidDeref -> enabling memOutOfBounds analysis \"" ^ (String.concat ", " memOobAna) ^ "\""; - enableAnalyses memOobAna + enableAnalyses memOobAna; + (* Set privatization to mutex-meet-tid *) + set_string "ana.base.privatization" "mutex-meet-tid"; + (* Required for mutex-meet-tid privatization *) + GobConfig.set_auto "ana.path_sens[+]" "threadflag"; | ValidMemtrack | ValidMemcleanup -> (* Enable the memLeak analysis *) let memLeakAna = ["memLeak"] in diff --git a/tests/regression/74-invalid_deref/31-multithreaded.c b/tests/regression/74-invalid_deref/31-multithreaded.c new file mode 100644 index 0000000000..e0dc146ba8 --- /dev/null +++ b/tests/regression/74-invalid_deref/31-multithreaded.c @@ -0,0 +1,21 @@ +//PARAM: --set ana.activated[+] useAfterFree --set ana.activated[+] threadJoins --set ana.path_sens[+] threadflag --set ana.activated[+] memOutOfBounds --enable ana.int.interval --set ana.base.arrays.domain partitioned --set ana.base.privatization mutex-meet-tid +#include + +int data; +int *p = &data, *q; +pthread_mutex_t mutex; +void *t_fun(void *arg) { + pthread_mutex_lock(&mutex); + *p = 8; + pthread_mutex_unlock(&mutex); + return ((void *)0); +} +int main() { + pthread_t id; + pthread_create(&id, ((void *)0), t_fun, ((void *)0)); + q = p; + pthread_mutex_lock(&mutex); + *q = 8; + pthread_mutex_unlock(&mutex); + return 0; +} From cb06f70f99b5c149e4afcb1ba6dcae53beb80cd2 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Wed, 22 Nov 2023 11:31:03 +0200 Subject: [PATCH 1080/1312] Fix indentation --- src/analyses/base.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 8b6350aa2d..98badad489 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2328,7 +2328,7 @@ struct | _ -> failwith ("non-floating-point argument in call to function "^f.vname) end in -let apply_abs ik x = + let apply_abs ik x = let eval_x = eval_rv (Analyses.ask_of_ctx ctx) gs st x in begin match eval_x with | Int int_x -> From c6cb63e48a665e566531213bbc02b4a568f2bf79 Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Wed, 22 Nov 2023 10:53:22 +0100 Subject: [PATCH 1081/1312] Update tests/regression/76-memleak/16-no-mem-leak-thread-exit-main.c Co-authored-by: Simmo Saan --- tests/regression/76-memleak/16-no-mem-leak-thread-exit-main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression/76-memleak/16-no-mem-leak-thread-exit-main.c b/tests/regression/76-memleak/16-no-mem-leak-thread-exit-main.c index 663ea26663..77dd299896 100644 --- a/tests/regression/76-memleak/16-no-mem-leak-thread-exit-main.c +++ b/tests/regression/76-memleak/16-no-mem-leak-thread-exit-main.c @@ -16,7 +16,7 @@ int main(int argc, char const *argv[]) { pthread_exit(NULL); - // A pthread_join called in main will wait for other threads to finish + // A pthread_exit called in main will wait for other threads to finish // Therefore, no memory leak here return 0; // NOWARN } \ No newline at end of file From f12a39216068c87cc0785f1ed13f9573b8f2c08e Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Wed, 22 Nov 2023 11:02:01 +0100 Subject: [PATCH 1082/1312] Remove call to free. --- .../regression/76-memleak/08-invalid-memcleanup-multi-threaded.c | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/regression/76-memleak/08-invalid-memcleanup-multi-threaded.c b/tests/regression/76-memleak/08-invalid-memcleanup-multi-threaded.c index 89dc7a3416..038801f219 100644 --- a/tests/regression/76-memleak/08-invalid-memcleanup-multi-threaded.c +++ b/tests/regression/76-memleak/08-invalid-memcleanup-multi-threaded.c @@ -7,7 +7,6 @@ int *m1; void *f1(void *arg) { m1 = malloc(sizeof(int)); - free(m1); // Thread t1 leaks m1 here pthread_exit(NULL); //WARN } From be9171b2bf6a541358c702a4a62e31a79f3be676 Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Wed, 22 Nov 2023 11:04:44 +0100 Subject: [PATCH 1083/1312] Add annotation of nowarn next to pthread_exit. --- .../regression/76-memleak/16-no-mem-leak-thread-exit-main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/regression/76-memleak/16-no-mem-leak-thread-exit-main.c b/tests/regression/76-memleak/16-no-mem-leak-thread-exit-main.c index 77dd299896..f7340d1d4f 100644 --- a/tests/regression/76-memleak/16-no-mem-leak-thread-exit-main.c +++ b/tests/regression/76-memleak/16-no-mem-leak-thread-exit-main.c @@ -14,9 +14,9 @@ int main(int argc, char const *argv[]) { pthread_t t1; pthread_create(&t1, NULL, f1, NULL); - pthread_exit(NULL); - // A pthread_exit called in main will wait for other threads to finish // Therefore, no memory leak here - return 0; // NOWARN + pthread_exit(NULL); // NOWARN + + return 0; // NOWARN (unreachable) } \ No newline at end of file From 585a65decbf38ddfdc66ce3c544547b47877b274 Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Wed, 22 Nov 2023 11:13:04 +0100 Subject: [PATCH 1084/1312] Check in TheadAnalysis.return whether the return is actually a threadreturn before side-effecting. --- src/analyses/threadAnalysis.ml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/analyses/threadAnalysis.ml b/src/analyses/threadAnalysis.ml index d9140dbb37..01c5dd87fa 100644 --- a/src/analyses/threadAnalysis.ml +++ b/src/analyses/threadAnalysis.ml @@ -22,12 +22,15 @@ struct module P = IdentityP (D) (* transfer functions *) - let return ctx (exp:exp option) _ : D.t = + let handle_thread_return ctx (exp: exp option) = let tid = ThreadId.get_current (Analyses.ask_of_ctx ctx) in - begin match tid with + match tid with | `Lifted tid -> ctx.sideg tid (false, TS.bot (), not (D.is_empty ctx.local)) | _ -> () - end; + + let return ctx (exp:exp option) _ : D.t = + if ctx.ask Queries.MayBeThreadReturn then + handle_thread_return ctx exp; ctx.local let rec is_not_unique ctx tid = @@ -65,7 +68,8 @@ struct | _ -> ctx.local (* if several possible threads are may-joined, none are must-joined *) | exception SetDomain.Unsupported _ -> ctx.local) | ThreadExit { ret_val } -> - return ctx (Some ret_val) () + handle_thread_return ctx (Some ret_val); + ctx.local | _ -> ctx.local let query ctx (type a) (q: a Queries.t): a Queries.result = From c5cda332088a48507f44b3c93733c13539189e04 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 22 Nov 2023 11:22:10 +0100 Subject: [PATCH 1085/1312] Move `AfterConfig.run` to after the autotuner --- src/analyses/base.ml | 8 +++++++- src/maingoblint.ml | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 84be8c7a19..518d4d88c6 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1056,7 +1056,13 @@ struct ); (* Warn if any of the addresses contains a non-local and non-global variable *) if AD.exists (function - | AD.Addr.Addr (v, _) -> not (CPA.mem v st.cpa) && not (is_global a v) + | AD.Addr.Addr (v, _) -> + (M.tracel "wtf" "checking for %a\n" CilType.Varinfo.pretty v; + if v.vglob then + (* this is OK *) + false + else + (not (CPA.mem v st.cpa)) || WeakUpdates.mem v st.weak) | _ -> false ) adr then ( AnalysisStateUtil.set_mem_safety_flag InvalidDeref; diff --git a/src/maingoblint.ml b/src/maingoblint.ml index 82a19aa4ae..79b0d121f6 100644 --- a/src/maingoblint.ml +++ b/src/maingoblint.ml @@ -191,10 +191,10 @@ let handle_flags () = let handle_options () = check_arguments (); - AfterConfig.run (); Sys.set_signal (GobSys.signal_of_string (get_string "dbg.solver-signal")) Signal_ignore; (* Ignore solver-signal before solving (e.g. MyCFG), otherwise exceptions self-signal the default, which crashes instead of printing backtrace. *) if AutoTune.isActivated "memsafetySpecification" && get_string "ana.specification" <> "" then AutoTune.focusOnMemSafetySpecification (); + AfterConfig.run (); Cilfacade.init_options (); handle_flags () From bc7694b68b599662a11cafc0c75c259cafa68a0d Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Wed, 22 Nov 2023 11:27:04 +0100 Subject: [PATCH 1086/1312] Add test case that checking that analysis distinguishes between thread returns and normal returns of a thread. --- .../76-memleak/19-no-mem-leak-return.c | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 tests/regression/76-memleak/19-no-mem-leak-return.c diff --git a/tests/regression/76-memleak/19-no-mem-leak-return.c b/tests/regression/76-memleak/19-no-mem-leak-return.c new file mode 100644 index 0000000000..70e0c66216 --- /dev/null +++ b/tests/regression/76-memleak/19-no-mem-leak-return.c @@ -0,0 +1,32 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak --set ana.activated[+] thread +#include +#include + + +void *f2(void *arg) { + int* m1 = malloc(sizeof(int)); + free(m1); + return NULL; +} + +// We check here that the analysis can distinguish between thread returns and normal returns + +void startf2(pthread_t* t){ + pthread_create(t, NULL, f2, NULL); + return; //NOWARN +} + +void *f1(void *arg) { + pthread_t t2; + startf2(&t2); + pthread_join(t2, NULL); + return NULL; // NOWARN +} + +int main(int argc, char const *argv[]) { + pthread_t t1; + pthread_create(&t1, NULL, f1, NULL); + pthread_join(t1, NULL); + + return 0; // NOWARN +} \ No newline at end of file From 666795faca6dd5a20e01c78daefde8efc7fbe1de Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 22 Nov 2023 11:50:31 +0100 Subject: [PATCH 1087/1312] MemLeak: Do not consider unions --- src/analyses/memLeak.ml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index f26157fdd0..c7a044f8a6 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -27,8 +27,8 @@ struct get_global_vars () |> List.filter (fun v -> match unrollType v.vtype with - | TPtr (TComp _, _) - | TPtr ((TNamed ({ttype = TComp _; _}, _)), _) -> true + | TPtr (TComp (ci,_), _) + | TPtr ((TNamed ({ttype = TComp (ci, _); _}, _)), _) -> ci.cstruct | TComp (_, _) | (TNamed ({ttype = TComp _; _}, _)) -> false | _ -> false) @@ -37,8 +37,8 @@ struct get_global_vars () |> List.filter (fun v -> match unrollType v.vtype with - | TComp (_, _) - | (TNamed ({ttype = TComp _; _}, _)) -> true + | TComp (ci, _) + | (TNamed ({ttype = TComp (ci,_); _}, _)) -> ci.cstruct | _ -> false) let get_reachable_mem_from_globals (global_vars:varinfo list) ctx = From 8ae117253f84b9b419d52a318af06dc7e4518475 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 22 Nov 2023 11:53:08 +0100 Subject: [PATCH 1088/1312] Revert spurious changes to `base.ml` --- src/analyses/base.ml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 518d4d88c6..84be8c7a19 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1056,13 +1056,7 @@ struct ); (* Warn if any of the addresses contains a non-local and non-global variable *) if AD.exists (function - | AD.Addr.Addr (v, _) -> - (M.tracel "wtf" "checking for %a\n" CilType.Varinfo.pretty v; - if v.vglob then - (* this is OK *) - false - else - (not (CPA.mem v st.cpa)) || WeakUpdates.mem v st.weak) + | AD.Addr.Addr (v, _) -> not (CPA.mem v st.cpa) && not (is_global a v) | _ -> false ) adr then ( AnalysisStateUtil.set_mem_safety_flag InvalidDeref; From 4fa70bd813e40e6621e252e4adfdf0b77cedb8c5 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Wed, 22 Nov 2023 13:11:06 +0200 Subject: [PATCH 1089/1312] Add missing fun defs from sv-benchmarks #1239 --- src/analyses/libraryFunctions.ml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 12eda8f728..1a032c84b3 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -37,7 +37,9 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("asctime", unknown ~attrs:[ThreadUnsafe] [drop "time_ptr" [r_deep]]); ("fclose", unknown [drop "stream" [r_deep; w_deep; f_deep]]); ("feof", unknown [drop "stream" [r_deep; w_deep]]); + ("feof_unlocked", unknown [drop "stream" [r_deep; w_deep]]); ("ferror", unknown [drop "stream" [r_deep; w_deep]]); + ("ferror_unlocked", unknown [drop "stream" [r_deep; w_deep]]); ("fflush", unknown [drop "stream" [r_deep; w_deep]]); ("fgetc", unknown [drop "stream" [r_deep; w_deep]]); ("getc", unknown [drop "stream" [r_deep; w_deep]]); @@ -52,9 +54,11 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("putc", unknown [drop "ch" []; drop "stream" [r_deep; w_deep]]); ("fputs", unknown [drop "str" [r]; drop "stream" [r_deep; w_deep]]); ("fread", unknown [drop "buffer" [w]; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); + ("__fread_chk_warn", unknown [drop "buffer" [w]; drop "os" []; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); ("fseek", unknown [drop "stream" [r_deep; w_deep]; drop "offset" []; drop "origin" []]); ("ftell", unknown [drop "stream" [r_deep]]); ("fwrite", unknown [drop "buffer" [r]; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); + ("fwrite_unlocked", unknown [drop "buffer" [r]; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); ("rewind", unknown [drop "stream" [r_deep; w_deep]]); ("setvbuf", unknown [drop "stream" [r_deep; w_deep]; drop "buffer" [r; w]; drop "mode" []; drop "size" []]); (* TODO: if this is used to set an input buffer, the buffer (second argument) would need to remain TOP, *) @@ -119,6 +123,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("mktime", unknown [drop "tm" [r;w]]); ("ctime", unknown ~attrs:[ThreadUnsafe] [drop "rm" [r]]); ("clearerr", unknown [drop "stream" [w]]); + ("clearerr_unlocked", unknown [drop "stream" [w]]); ("setbuf", unknown [drop "stream" [w]; drop "buf" [w]]); ("swprintf", unknown (drop "wcs" [w] :: drop "maxlen" [] :: drop "fmt" [r] :: VarArgs (drop' [r]))); ("assert", special [__ "exp" []] @@ fun exp -> Assert { exp; check = true; refine = get_bool "sem.assert.refine" }); (* only used if assert is used without include, e.g. in transformed files *) @@ -133,6 +138,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("abs", special [__ "j" []] @@ fun j -> Math { fun_args = (Abs (IInt, j)) }); ("labs", special [__ "j" []] @@ fun j -> Math { fun_args = (Abs (ILong, j)) }); ("llabs", special [__ "j" []] @@ fun j -> Math { fun_args = (Abs (ILongLong, j)) }); + ("imaxabs", unknown [drop "j" []]); ("localtime_r", unknown [drop "timep" [r]; drop "result" [w]]); ("strpbrk", unknown [drop "s" [r]; drop "accept" [r]]); ("_setjmp", special [__ "env" [w]] @@ fun env -> Setjmp { env }); (* only has one underscore *) @@ -151,6 +157,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("atomic_load", unknown [drop "obj" [r]]); ("atomic_store", unknown [drop "obj" [w]; drop "desired" []]); ("_Exit", special [drop "status" []] @@ Abort); + ("strcoll", unknown [drop "lhs" [r]; drop "rhs" [r]]); ] (** C POSIX library functions. @@ -300,6 +307,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("sendto", unknown [drop "sockfd" []; drop "buf" [r]; drop "len" []; drop "flags" []; drop "dest_addr" [r_deep]; drop "addrlen" []]); ("strdup", unknown [drop "s" [r]]); ("strndup", unknown [drop "s" [r]; drop "n" []]); + ("__strndup", unknown [drop "s" [r]; drop "n" []]); ("syscall", unknown (drop "number" [] :: VarArgs (drop' [r; w]))); ("sysconf", unknown [drop "name" []]); ("syslog", unknown (drop "priority" [] :: drop "format" [r] :: VarArgs (drop' [r]))); (* TODO: is the VarArgs correct here? *) @@ -408,6 +416,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("srandom", unknown [drop "seed" []]); ("random", special [] Rand); ("posix_memalign", unknown [drop "memptr" [w]; drop "alignment" []; drop "size" []]); (* TODO: Malloc *) + ("stpcpy", unknown [drop "dest" [w]; drop "src" [r]]); ] (** Pthread functions. *) @@ -654,6 +663,8 @@ let linux_userspace_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("fstatfs", unknown [drop "fd" []; drop "buf" [w]]); ("cfmakeraw", unknown [drop "termios" [r; w]]); ("process_vm_readv", unknown [drop "pid" []; drop "local_iov" [w_deep]; drop "liovcnt" []; drop "remote_iov" []; drop "riovcnt" []; drop "flags" []]); + ("__libc_current_sigrtmax", unknown []); + ("__libc_current_sigrtmin", unknown []); ] let big_kernel_lock = AddrOf (Cil.var (Cilfacade.create_var (makeGlobalVar "[big kernel lock]" intType))) From 7159875257d594d248b3b3b7b0560e4c37e91f09 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Wed, 22 Nov 2023 13:47:15 +0200 Subject: [PATCH 1090/1312] Add fun defs for wprintf, iswxdigit and .*wscanf #1239 --- src/analyses/libraryFunctions.ml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 1a032c84b3..20995e2f09 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -125,6 +125,8 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("clearerr", unknown [drop "stream" [w]]); ("clearerr_unlocked", unknown [drop "stream" [w]]); ("setbuf", unknown [drop "stream" [w]; drop "buf" [w]]); + ("wprintf", unknown (drop "fmt" [r] :: VarArgs (drop' [r]))); + ("fwprintf", unknown (drop "stream" [w] :: drop "fmt" [r] :: VarArgs (drop' [r]))); ("swprintf", unknown (drop "wcs" [w] :: drop "maxlen" [] :: drop "fmt" [r] :: VarArgs (drop' [r]))); ("assert", special [__ "exp" []] @@ fun exp -> Assert { exp; check = true; refine = get_bool "sem.assert.refine" }); (* only used if assert is used without include, e.g. in transformed files *) ("difftime", unknown [drop "time1" []; drop "time2" []]); @@ -158,6 +160,9 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("atomic_store", unknown [drop "obj" [w]; drop "desired" []]); ("_Exit", special [drop "status" []] @@ Abort); ("strcoll", unknown [drop "lhs" [r]; drop "rhs" [r]]); + ("wscanf", unknown (drop "fmt" [r] :: VarArgs (drop' [r]))); + ("fwscanf", unknown (drop "stream" [r] :: drop "fmt" [r] :: VarArgs (drop' [r]))); + ("swscanf", unknown (drop "buffer" [r] :: drop "fmt" [r] :: VarArgs (drop' [r]))); ] (** C POSIX library functions. From dd45d1960a6e72083cc2c62f4c857aa24756cb2b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 22 Nov 2023 13:48:28 +0200 Subject: [PATCH 1091/1312] Add initial CHANGELOG for SV-COMP 2024 --- CHANGELOG.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 97cc399133..ab9bb8fef2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +## v2.3.0 (unreleased) +Functionally equivalent to Goblint in SV-COMP 2024. + +### SV-COMP 2024 +* Add termination analysis (#1093). +* Add OOB analysis (#1094, #1197). +* Add memory leak analysis (???, #1246, #1241). +* Improve multi-threaded use-after-free analysis (#1123, ). +* Support MemSafety in SV-COMP (#1201, #1199, #1262). +* YAML witnesses in SV-COMP mode (#1217, #1226, #1225, #1248). +* YAML witness version 2.0 (#1238, #1240). +* SV-COMP multi-property (#1220, #1228). +* Adapt autotuning (#912, #921, #987, #1214, #1234, #1168). +* Support `alloca` (#1179). +* Fix old thread analysis soundness (#1223, #1230). +* Add library functions (#1242, #1244, #1254, #1239). +* Fix some region escape unsoundness (#1247). + ## v2.2.1 * Bump batteries lower bound to 3.5.0. * Fix flaky dead code elimination transformation test. From 949432b389c53939c08693ef1febe14f5c27e09b Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Wed, 22 Nov 2023 13:54:07 +0200 Subject: [PATCH 1092/1312] Add fun def for iswxdigit #1239 --- src/analyses/libraryFunctions.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 20995e2f09..a9cce50b60 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -93,6 +93,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("iswspace", unknown [drop "wc" []]); ("iswalnum", unknown [drop "wc" []]); ("iswprint", unknown [drop "wc" []]); + ("iswxdigit", unknown [drop "ch" []]); ("rename" , unknown [drop "oldpath" [r]; drop "newpath" [r];]); ("perror", unknown [drop "s" [r]]); ("getchar", unknown []); From bf754c06f343cbde55c4bee6a60524840fe34161 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 22 Nov 2023 13:56:56 +0200 Subject: [PATCH 1093/1312] Add initial CHANGELOG for v2.3.0 --- CHANGELOG.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab9bb8fef2..c32bf566d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,19 +1,24 @@ ## v2.3.0 (unreleased) Functionally equivalent to Goblint in SV-COMP 2024. +* Refactor/fix race analysis (#1170, #1198). +* Add library function (#1167, #1174, #1220, #1203, #1205, #1212). +* Refactor/fix `MayPointTo` and `ReachableFrom` queries (#1142, #1176, #1144). +* Add final messages about unsound results (#1190, #1191). + ### SV-COMP 2024 * Add termination analysis (#1093). * Add OOB analysis (#1094, #1197). * Add memory leak analysis (???, #1246, #1241). * Improve multi-threaded use-after-free analysis (#1123, ). -* Support MemSafety in SV-COMP (#1201, #1199, #1262). +* Support MemSafety in SV-COMP (#1201, #1199, #1259, #1262). * YAML witnesses in SV-COMP mode (#1217, #1226, #1225, #1248). * YAML witness version 2.0 (#1238, #1240). * SV-COMP multi-property (#1220, #1228). * Adapt autotuning (#912, #921, #987, #1214, #1234, #1168). * Support `alloca` (#1179). * Fix old thread analysis soundness (#1223, #1230). -* Add library functions (#1242, #1244, #1254, #1239). +* Add library functions (#1242, #1244, #1254, #1239, #1269). * Fix some region escape unsoundness (#1247). ## v2.2.1 From c98025cc96870659b7deff213bd7820c897d9a75 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 22 Nov 2023 14:09:50 +0200 Subject: [PATCH 1094/1312] Move *_unlocked functions to glibc group, fix *wscanf varargs --- src/analyses/libraryFunctions.ml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index a9cce50b60..8152e5b886 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -37,9 +37,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("asctime", unknown ~attrs:[ThreadUnsafe] [drop "time_ptr" [r_deep]]); ("fclose", unknown [drop "stream" [r_deep; w_deep; f_deep]]); ("feof", unknown [drop "stream" [r_deep; w_deep]]); - ("feof_unlocked", unknown [drop "stream" [r_deep; w_deep]]); ("ferror", unknown [drop "stream" [r_deep; w_deep]]); - ("ferror_unlocked", unknown [drop "stream" [r_deep; w_deep]]); ("fflush", unknown [drop "stream" [r_deep; w_deep]]); ("fgetc", unknown [drop "stream" [r_deep; w_deep]]); ("getc", unknown [drop "stream" [r_deep; w_deep]]); @@ -54,11 +52,9 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("putc", unknown [drop "ch" []; drop "stream" [r_deep; w_deep]]); ("fputs", unknown [drop "str" [r]; drop "stream" [r_deep; w_deep]]); ("fread", unknown [drop "buffer" [w]; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); - ("__fread_chk_warn", unknown [drop "buffer" [w]; drop "os" []; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); ("fseek", unknown [drop "stream" [r_deep; w_deep]; drop "offset" []; drop "origin" []]); ("ftell", unknown [drop "stream" [r_deep]]); ("fwrite", unknown [drop "buffer" [r]; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); - ("fwrite_unlocked", unknown [drop "buffer" [r]; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); ("rewind", unknown [drop "stream" [r_deep; w_deep]]); ("setvbuf", unknown [drop "stream" [r_deep; w_deep]; drop "buffer" [r; w]; drop "mode" []; drop "size" []]); (* TODO: if this is used to set an input buffer, the buffer (second argument) would need to remain TOP, *) @@ -123,11 +119,10 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("vsnprintf", unknown [drop "str" [w]; drop "size" []; drop "format" [r]; drop "ap" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) ("mktime", unknown [drop "tm" [r;w]]); ("ctime", unknown ~attrs:[ThreadUnsafe] [drop "rm" [r]]); - ("clearerr", unknown [drop "stream" [w]]); - ("clearerr_unlocked", unknown [drop "stream" [w]]); + ("clearerr", unknown [drop "stream" [w]]); (* TODO: why only w? *) ("setbuf", unknown [drop "stream" [w]; drop "buf" [w]]); ("wprintf", unknown (drop "fmt" [r] :: VarArgs (drop' [r]))); - ("fwprintf", unknown (drop "stream" [w] :: drop "fmt" [r] :: VarArgs (drop' [r]))); + ("fwprintf", unknown (drop "stream" [r_deep; w_deep] :: drop "fmt" [r] :: VarArgs (drop' [r]))); ("swprintf", unknown (drop "wcs" [w] :: drop "maxlen" [] :: drop "fmt" [r] :: VarArgs (drop' [r]))); ("assert", special [__ "exp" []] @@ fun exp -> Assert { exp; check = true; refine = get_bool "sem.assert.refine" }); (* only used if assert is used without include, e.g. in transformed files *) ("difftime", unknown [drop "time1" []; drop "time2" []]); @@ -161,9 +156,9 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("atomic_store", unknown [drop "obj" [w]; drop "desired" []]); ("_Exit", special [drop "status" []] @@ Abort); ("strcoll", unknown [drop "lhs" [r]; drop "rhs" [r]]); - ("wscanf", unknown (drop "fmt" [r] :: VarArgs (drop' [r]))); - ("fwscanf", unknown (drop "stream" [r] :: drop "fmt" [r] :: VarArgs (drop' [r]))); - ("swscanf", unknown (drop "buffer" [r] :: drop "fmt" [r] :: VarArgs (drop' [r]))); + ("wscanf", unknown (drop "fmt" [r] :: VarArgs (drop' [w]))); + ("fwscanf", unknown (drop "stream" [r_deep; w_deep] :: drop "fmt" [r] :: VarArgs (drop' [w]))); + ("swscanf", unknown (drop "buffer" [r] :: drop "fmt" [r] :: VarArgs (drop' [w]))); ] (** C POSIX library functions. @@ -586,6 +581,10 @@ let gcc_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ let glibc_desc_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("fputs_unlocked", unknown [drop "s" [r]; drop "stream" [w]]); + ("feof_unlocked", unknown [drop "stream" [r_deep; w_deep]]); + ("ferror_unlocked", unknown [drop "stream" [r_deep; w_deep]]); + ("fwrite_unlocked", unknown [drop "buffer" [r]; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); + ("clearerr_unlocked", unknown [drop "stream" [w]]); (* TODO: why only w? *) ("futimesat", unknown [drop "dirfd" []; drop "pathname" [r]; drop "times" [r]]); ("error", unknown ((drop "status" []) :: (drop "errnum" []) :: (drop "format" [r]) :: (VarArgs (drop' [r])))); ("warn", unknown (drop "format" [r] :: VarArgs (drop' [r]))); @@ -597,6 +596,7 @@ let glibc_desc_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__fgets_chk", unknown [drop "__s" [w]; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); ("__fread_alias", unknown [drop "__ptr" [w]; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); ("__fread_chk", unknown [drop "__ptr" [w]; drop "__ptrlen" []; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); + ("__fread_chk_warn", unknown [drop "buffer" [w]; drop "os" []; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); ("fread_unlocked", unknown ~attrs:[ThreadUnsafe] [drop "buffer" [w]; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); ("__fread_unlocked_alias", unknown ~attrs:[ThreadUnsafe] [drop "__ptr" [w]; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); ("__fread_unlocked_chk", unknown ~attrs:[ThreadUnsafe] [drop "__ptr" [w]; drop "__ptrlen" []; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); From 06f543a0139b12366591f792642167e0e5ca2285 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 22 Nov 2023 13:21:19 +0100 Subject: [PATCH 1095/1312] Undo setting mutex-meet-tid privatization in autotuner --- src/autoTune.ml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/autoTune.ml b/src/autoTune.ml index dca3ee405a..9627aed85f 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -223,10 +223,6 @@ let focusOnMemSafetySpecification (spec: Svcomp.Specification.t) = set_bool "cil.addNestedScopeAttr" true; print_endline @@ "Specification: ValidDeref -> enabling memOutOfBounds analysis \"" ^ (String.concat ", " memOobAna) ^ "\""; enableAnalyses memOobAna; - (* Set privatization to mutex-meet-tid *) - set_string "ana.base.privatization" "mutex-meet-tid"; - (* Required for mutex-meet-tid privatization *) - GobConfig.set_auto "ana.path_sens[+]" "threadflag"; | ValidMemtrack | ValidMemcleanup -> (* Enable the memLeak analysis *) let memLeakAna = ["memLeak"] in From 9c650571f81b5c4d0b57a466b2adf935b90ddaa4 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 22 Nov 2023 14:30:14 +0200 Subject: [PATCH 1096/1312] Add CHANGELOG for v2.3.0 --- CHANGELOG.md | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c32bf566d2..7300c09206 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,25 +1,14 @@ ## v2.3.0 (unreleased) Functionally equivalent to Goblint in SV-COMP 2024. -* Refactor/fix race analysis (#1170, #1198). -* Add library function (#1167, #1174, #1220, #1203, #1205, #1212). -* Refactor/fix `MayPointTo` and `ReachableFrom` queries (#1142, #1176, #1144). -* Add final messages about unsound results (#1190, #1191). - -### SV-COMP 2024 -* Add termination analysis (#1093). -* Add OOB analysis (#1094, #1197). -* Add memory leak analysis (???, #1246, #1241). -* Improve multi-threaded use-after-free analysis (#1123, ). -* Support MemSafety in SV-COMP (#1201, #1199, #1259, #1262). -* YAML witnesses in SV-COMP mode (#1217, #1226, #1225, #1248). -* YAML witness version 2.0 (#1238, #1240). -* SV-COMP multi-property (#1220, #1228). -* Adapt autotuning (#912, #921, #987, #1214, #1234, #1168). -* Support `alloca` (#1179). -* Fix old thread analysis soundness (#1223, #1230). -* Add library functions (#1242, #1244, #1254, #1239, #1269). -* Fix some region escape unsoundness (#1247). +* Add termination analysis for loops (#1093). +* Add memory out-of-bounds analysis (#1094, #1197). +* Add memory leak analysis (#1127, #1241, #1246). +* Add SV-COMP `termination`, `valid-memsafety` and `valid-memcleanup` properties support (#1220, #1228, #1201, #1199, #1259, #1262). +* Add YAML witness version 2.0 support (#1238, #1240, #1217, #1226, #1225, #1248). +* Add final warnings about unsound results (#1190, #1191). +* Add many library function specifications (#1167, #1174, #1203, #1205, #1212, #1220, #1239, #1242, #1244, #1254, #1269). +* Adapt automatic configuration tuning (#912, #921, #987, #1168, #1214, #1234). ## v2.2.1 * Bump batteries lower bound to 3.5.0. From 6389a7f6a87c6495490858c75791920ac0c2ae7b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 23 Nov 2023 10:28:27 +0200 Subject: [PATCH 1097/1312] Prevent num downgrade in lower-bounds CI --- .github/workflows/unlocked.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/unlocked.yml b/.github/workflows/unlocked.yml index 990b7cfb49..57fa0cb6b5 100644 --- a/.github/workflows/unlocked.yml +++ b/.github/workflows/unlocked.yml @@ -160,7 +160,8 @@ jobs: - name: Downgrade dependencies # must specify ocaml-base-compiler again to prevent it from being downgraded - run: opam install $(opam exec -- opam-0install --prefer-oldest goblint ocaml-variants.4.14.0+options ocaml-option-flambda) + # prevent num downgrade to avoid dune/jbuilder error: https://github.com/ocaml/dune/issues/5280 + run: opam install $(opam exec -- opam-0install --prefer-oldest goblint ocaml-variants.4.14.0+options ocaml-option-flambda num.1.4) - name: Build run: ./make.sh nat From 9bb9ae2c29efcaa1bbc3b989e267f353e9804080 Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Thu, 23 Nov 2023 10:32:51 +0100 Subject: [PATCH 1098/1312] Fix ordering of queries by deduplicating indices. --- src/domains/queries.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 52038fcf77..b9fa28f5be 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -331,8 +331,8 @@ struct | Any (MustTermLoop _) -> 53 | Any MustTermAllLoops -> 54 | Any IsEverMultiThreaded -> 55 - | Any (TmpSpecial _) -> 53 - | Any (IsAllocVar _) -> 54 + | Any (TmpSpecial _) -> 56 + | Any (IsAllocVar _) -> 57 let rec compare a b = let r = Stdlib.compare (order a) (order b) in From f2623868f8f6fbcc9230c62389625f89a1e1c7d5 Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Thu, 23 Nov 2023 14:06:46 +0100 Subject: [PATCH 1099/1312] Fix Not_found exception in autotuner with congruences and termination. --- src/autoTune.ml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/autoTune.ml b/src/autoTune.ml index fefdeb32fd..1fd1fa5ee6 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -99,7 +99,9 @@ let rec setCongruenceRecursive fd depth neigbourFunction = FunctionSet.iter (fun vinfo -> print_endline (" " ^ vinfo.vname); - setCongruenceRecursive (Cilfacade.find_varinfo_fundec vinfo) (depth -1) neigbourFunction + match (Cilfacade.find_varinfo_fundec vinfo) with + | fd -> setCongruenceRecursive fd (depth -1) neigbourFunction + | exception Not_found -> () (* Happens for __goblint_bounded*) ) (FunctionSet.filter (*for extern and builtin functions there is no function definition in CIL*) (fun x -> not (isExtern x.vstorage || BatString.starts_with x.vname "__builtin")) From 5be07e5e96c329ace898bcf973c5d7780bf44da8 Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Thu, 23 Nov 2023 14:22:15 +0100 Subject: [PATCH 1100/1312] Remove unnecessary paranetheses. --- src/autoTune.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/autoTune.ml b/src/autoTune.ml index 1fd1fa5ee6..79f5f51a77 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -99,9 +99,9 @@ let rec setCongruenceRecursive fd depth neigbourFunction = FunctionSet.iter (fun vinfo -> print_endline (" " ^ vinfo.vname); - match (Cilfacade.find_varinfo_fundec vinfo) with + match Cilfacade.find_varinfo_fundec vinfo with | fd -> setCongruenceRecursive fd (depth -1) neigbourFunction - | exception Not_found -> () (* Happens for __goblint_bounded*) + | exception Not_found -> () (* Happens for __goblint_bounded *) ) (FunctionSet.filter (*for extern and builtin functions there is no function definition in CIL*) (fun x -> not (isExtern x.vstorage || BatString.starts_with x.vname "__builtin")) From 9b954b5cd0b14a146267bc80c476d6a81e281643 Mon Sep 17 00:00:00 2001 From: Julian Erhard Date: Thu, 23 Nov 2023 15:00:25 +0100 Subject: [PATCH 1101/1312] Add example where autotuner crashed when trying to activate congruence domain when termination was enabled --- tests/regression/78-termination/51-modulo.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 tests/regression/78-termination/51-modulo.c diff --git a/tests/regression/78-termination/51-modulo.c b/tests/regression/78-termination/51-modulo.c new file mode 100644 index 0000000000..5f5b8f1924 --- /dev/null +++ b/tests/regression/78-termination/51-modulo.c @@ -0,0 +1,14 @@ +// SKIP TERM PARAM: --enable ana.autotune.enabled --enable ana.sv-comp.functions --enable ana.sv-comp.enabled --set ana.autotune.activated "['congruence']" --set ana.specification "CHECK( init(main()), LTL(F end) )" + +// This task previously crashed due to the autotuner +int main() { + int a; + int odd, count = 0; + while(a > 1) { + odd = a % 2; + if(!odd) a = a / 2; + else a = a - 1; + count++; + } + return count; +} From 9f3fcac6baee113bfba38e789085e4537988eb64 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 24 Nov 2023 16:25:20 +0200 Subject: [PATCH 1102/1312] Add ORCiD-s to metadata --- .zenodo.json | 15 ++++++++++----- CITATION.cff | 4 ++++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/.zenodo.json b/.zenodo.json index 5557622f9e..22705c2d9c 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -10,15 +10,18 @@ }, { "name": "Schwarz, Michael", - "affiliation": "Technische Universität München" + "affiliation": "Technische Universität München", + "orcid": "0000-0002-9828-0308" }, { "name": "Erhard, Julian", - "affiliation": "Technische Universität München" + "affiliation": "Technische Universität München", + "orcid": "0000-0002-1729-3925" }, { "name": "Tilscher, Sarah", - "affiliation": "Technische Universität München" + "affiliation": "Technische Universität München", + "orcid": "0009-0009-9644-7475" }, { "name": "Vogler, Ralf", @@ -30,14 +33,16 @@ }, { "name": "Vojdani, Vesal", - "affiliation": "University of Tartu" + "affiliation": "University of Tartu", + "orcid": "0000-0003-4336-7980" } ], "contributors": [ { "name": "Seidl, Helmut", "type": "ProjectLeader", - "affiliation": "Technische Universität München" + "affiliation": "Technische Universität München", + "orcid": "0000-0002-2135-1593" }, { "name": "Schwarz, Martin D.", diff --git a/CITATION.cff b/CITATION.cff index 7a2dcf188d..25d46cf762 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -12,12 +12,15 @@ authors: # same authors as in .zenodo.json and dune-project - given-names: Michael family-names: Schwarz affiliation: "Technische Universität München" + orcid: "https://orcid.org/0000-0002-9828-0308" - given-names: Julian family-names: Erhard affiliation: "Technische Universität München" + orcid: "https://orcid.org/0000-0002-1729-3925" - given-names: Sarah family-names: Tilscher affiliation: "Technische Universität München" + orcid: "https://orcid.org/0009-0009-9644-7475" - given-names: Ralf family-names: Vogler affiliation: "Technische Universität München" @@ -27,6 +30,7 @@ authors: # same authors as in .zenodo.json and dune-project - given-names: Vesal family-names: Vojdani affiliation: "University of Tartu" + orcid: "https://orcid.org/0000-0003-4336-7980" license: MIT repository-code: "https://github.com/goblint/analyzer" From 356136fb5cd85d1af1b38799282f1f64724a2b7a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 24 Nov 2023 16:26:13 +0200 Subject: [PATCH 1103/1312] Finalize CHANGELOG for v2.3.0 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7300c09206..d285480259 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## v2.3.0 (unreleased) +## v2.3.0 Functionally equivalent to Goblint in SV-COMP 2024. * Add termination analysis for loops (#1093). From 07463bf738482fb0273e32af3c976671dad03325 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 24 Nov 2023 16:31:09 +0200 Subject: [PATCH 1104/1312] Disable zenodo-validate in metadata CI --- .github/workflows/metadata.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/metadata.yml b/.github/workflows/metadata.yml index 6c7360f9e3..3a48d52fa0 100644 --- a/.github/workflows/metadata.yml +++ b/.github/workflows/metadata.yml @@ -27,6 +27,9 @@ jobs: args: --validate zenodo-validate: + # Zenodo schema URL is dead + if: ${{ false }} + strategy: matrix: node-version: From ade7968858f8d6ad04ba8d5f788557ace5ddf926 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 24 Nov 2023 16:33:55 +0200 Subject: [PATCH 1105/1312] Replace goblint-cil pin with published 2.0.3 --- dune-project | 2 +- goblint.opam | 5 +++-- goblint.opam.locked | 6 +----- goblint.opam.template | 3 ++- gobview | 2 +- 5 files changed, 8 insertions(+), 10 deletions(-) diff --git a/dune-project b/dune-project index 05c7d9418c..81c8d2f091 100644 --- a/dune-project +++ b/dune-project @@ -24,7 +24,7 @@ (synopsis "Static analysis framework for C") (depends (ocaml (>= 4.10)) - (goblint-cil (>= 2.0.2)) ; TODO no way to define as pin-depends? Used goblint.opam.template to add it for now. https://github.com/ocaml/dune/issues/3231. Alternatively, removing this line and adding cil as a git submodule and `(vendored_dirs cil)` as ./dune also works. This way, no more need to reinstall the pinned cil opam package on changes. However, then cil is cleaned and has to be rebuild together with goblint. + (goblint-cil (>= 2.0.3)) ; TODO no way to define as pin-depends? Used goblint.opam.template to add it for now. https://github.com/ocaml/dune/issues/3231. Alternatively, removing this line and adding cil as a git submodule and `(vendored_dirs cil)` as ./dune also works. This way, no more need to reinstall the pinned cil opam package on changes. However, then cil is cleaned and has to be rebuild together with goblint. (batteries (>= 3.5.0)) (zarith (>= 1.8)) (yojson (>= 2.0.0)) diff --git a/goblint.opam b/goblint.opam index 34912fde26..669b2d9c40 100644 --- a/goblint.opam +++ b/goblint.opam @@ -21,7 +21,7 @@ bug-reports: "https://github.com/goblint/analyzer/issues" depends: [ "dune" {>= "3.7"} "ocaml" {>= "4.10"} - "goblint-cil" {>= "2.0.2"} + "goblint-cil" {>= "2.0.3"} "batteries" {>= "3.5.0"} "zarith" {>= "1.8"} "yojson" {>= "2.0.0"} @@ -75,7 +75,8 @@ dev-repo: "git+https://github.com/goblint/analyzer.git" # also remember to generate/adjust goblint.opam.locked! available: os-distribution != "alpine" & arch != "arm64" pin-depends: [ - [ "goblint-cil.2.0.2" "git+https://github.com/goblint/cil.git#c7ffc37ad83216a84d90fdbf427cc02a68ea5331" ] + # published goblint-cil 2.0.3 is currently up-to-date, so no pin needed + # [ "goblint-cil.2.0.3" "git+https://github.com/goblint/cil.git#d2760bacfbfdb25a374254de44f2ff1cb5f42abd" ] # TODO: add back after release, only pinned for optimization (https://github.com/ocaml-ppx/ppx_deriving/pull/252) [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] ] diff --git a/goblint.opam.locked b/goblint.opam.locked index 6e15ac8900..02eac0bb75 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -59,7 +59,7 @@ depends: [ "fileutils" {= "0.6.4"} "fmt" {= "0.9.0"} "fpath" {= "0.7.3"} - "goblint-cil" {= "2.0.2"} + "goblint-cil" {= "2.0.3"} "integers" {= "0.7.0"} "json-data-encoding" {= "0.12.1"} "jsonrpc" {= "1.15.0~5.0preview1"} @@ -130,10 +130,6 @@ post-messages: [ ] # TODO: manually reordered to avoid opam pin crash: https://github.com/ocaml/opam/issues/4936 pin-depends: [ - [ - "goblint-cil.2.0.2" - "git+https://github.com/goblint/cil.git#c7ffc37ad83216a84d90fdbf427cc02a68ea5331" - ] [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" diff --git a/goblint.opam.template b/goblint.opam.template index d8e25cde38..ca2796b3c7 100644 --- a/goblint.opam.template +++ b/goblint.opam.template @@ -2,7 +2,8 @@ # also remember to generate/adjust goblint.opam.locked! available: os-distribution != "alpine" & arch != "arm64" pin-depends: [ - [ "goblint-cil.2.0.2" "git+https://github.com/goblint/cil.git#c7ffc37ad83216a84d90fdbf427cc02a68ea5331" ] + # published goblint-cil 2.0.3 is currently up-to-date, so no pin needed + # [ "goblint-cil.2.0.3" "git+https://github.com/goblint/cil.git#d2760bacfbfdb25a374254de44f2ff1cb5f42abd" ] # TODO: add back after release, only pinned for optimization (https://github.com/ocaml-ppx/ppx_deriving/pull/252) [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] ] diff --git a/gobview b/gobview index b4467d820f..d4eb66b9eb 160000 --- a/gobview +++ b/gobview @@ -1 +1 @@ -Subproject commit b4467d820f28bac578fc0baf7f81393c67f6b82b +Subproject commit d4eb66b9eb277349a75141cb01899dbab9d3ef5d From dbd6479a53dbf76f351f853bbc9092d659a8a631 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 24 Nov 2023 16:40:27 +0200 Subject: [PATCH 1106/1312] Disable pins for v2.3.0 release --- goblint.opam | 6 +++--- goblint.opam.locked | 7 ------- goblint.opam.template | 6 +++--- 3 files changed, 6 insertions(+), 13 deletions(-) diff --git a/goblint.opam b/goblint.opam index 669b2d9c40..842c03933f 100644 --- a/goblint.opam +++ b/goblint.opam @@ -74,12 +74,12 @@ dev-repo: "git+https://github.com/goblint/analyzer.git" # on `dune build` goblint.opam will be generated from goblint.opam.template and dune-project # also remember to generate/adjust goblint.opam.locked! available: os-distribution != "alpine" & arch != "arm64" -pin-depends: [ +# pin-depends: [ # published goblint-cil 2.0.3 is currently up-to-date, so no pin needed # [ "goblint-cil.2.0.3" "git+https://github.com/goblint/cil.git#d2760bacfbfdb25a374254de44f2ff1cb5f42abd" ] # TODO: add back after release, only pinned for optimization (https://github.com/ocaml-ppx/ppx_deriving/pull/252) - [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] -] + # [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] +# ] post-messages: [ "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} ] diff --git a/goblint.opam.locked b/goblint.opam.locked index 02eac0bb75..aba9f38bda 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -128,10 +128,3 @@ conflicts: [ post-messages: [ "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} ] -# TODO: manually reordered to avoid opam pin crash: https://github.com/ocaml/opam/issues/4936 -pin-depends: [ - [ - "ppx_deriving.5.2.1" - "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" - ] -] diff --git a/goblint.opam.template b/goblint.opam.template index ca2796b3c7..95f90bcbd1 100644 --- a/goblint.opam.template +++ b/goblint.opam.template @@ -1,12 +1,12 @@ # on `dune build` goblint.opam will be generated from goblint.opam.template and dune-project # also remember to generate/adjust goblint.opam.locked! available: os-distribution != "alpine" & arch != "arm64" -pin-depends: [ +# pin-depends: [ # published goblint-cil 2.0.3 is currently up-to-date, so no pin needed # [ "goblint-cil.2.0.3" "git+https://github.com/goblint/cil.git#d2760bacfbfdb25a374254de44f2ff1cb5f42abd" ] # TODO: add back after release, only pinned for optimization (https://github.com/ocaml-ppx/ppx_deriving/pull/252) - [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] -] + # [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] +# ] post-messages: [ "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} ] From c1cced80063009ea5549da7927338f0c12216579 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 24 Nov 2023 20:56:40 +0100 Subject: [PATCH 1107/1312] Address requested changes to `invalidate_abstract_value` --- src/cdomains/valueDomain.ml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/cdomains/valueDomain.ml b/src/cdomains/valueDomain.ml index b6fbfaf7dc..985d7cca8b 100644 --- a/src/cdomains/valueDomain.ml +++ b/src/cdomains/valueDomain.ml @@ -58,6 +58,7 @@ sig type origin include Lattice.S with type t = value * size * origin + val map: (value -> value) -> t -> t val value: t -> value val invalidate_value: VDQ.t -> typ -> t -> t end @@ -77,6 +78,7 @@ struct type size = Size.t type origin = ZeroInit.t + let map f (v, s, o) = f v, s, o let value (a, b, c) = a let relift (a, b, c) = Value.relift a, b, c let invalidate_value ask t (v, s, o) = Value.invalidate_value ask t v, s, o @@ -745,9 +747,9 @@ struct | Float f -> Float (FD.top_of (FD.get_fkind f)) | Address _ -> Address (AD.top_ptr) | Struct s -> Struct (Structs.map invalidate_abstract_value s) - | Union u -> Union (Unions.top ()) + | Union u -> Union (Unions.top ()) (* More precise invalidate does not make sense, as it is not clear which component is accessed. *) | Array a -> Array (CArrays.map invalidate_abstract_value a) - | Blob _ -> Blob (Blobs.top ()) + | Blob b -> Blob (Blobs.map invalidate_abstract_value b) | Thread _ -> Thread (Threads.top ()) | JmpBuf _ -> JmpBuf (JmpBufs.top ()) | Mutex -> Mutex From e54510811fb2ca73837a5e4168adac5fdc30f1eb Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 24 Nov 2023 21:07:27 +0100 Subject: [PATCH 1108/1312] Simplify `substring_extraction` --- src/cdomains/arrayDomain.ml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 543ff2458a..d191562426 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1883,7 +1883,7 @@ struct type value = Val.t type ret = Null | NotNull | Top - type substr = IsNotSubstr | IsSubstrAtIndex0 | IsMaybeSubstr + type substr = N.substr = IsNotSubstr | IsSubstrAtIndex0 | IsMaybeSubstr let domain_of_t (t_f, _) = A.domain_of_t t_f @@ -1957,10 +1957,11 @@ struct (A.map Val.invalidate_abstract_value t_f1, N.string_concat t_n1 t_n2 n) else (A.map Val.invalidate_abstract_value t_f1, N.top ()) - let substring_extraction (_, t_n1) (_, t_n2) = match N.substring_extraction t_n1 t_n2 with - | IsNotSubstr when get_bool "ana.base.arrays.nullbytes" -> IsNotSubstr - | IsSubstrAtIndex0 when get_bool "ana.base.arrays.nullbytes" -> IsSubstrAtIndex0 - | _ -> IsMaybeSubstr + let substring_extraction (_, t_n1) (_, t_n2) = + if get_bool "ana.base.arrays.nullbytes" then + N.substring_extraction t_n1 t_n2 + else + IsMaybeSubstr let string_comparison (_, t_n1) (_, t_n2) n = if get_bool "ana.base.arrays.nullbytes" then N.string_comparison t_n1 t_n2 n From 1343915c17b8fcd15fd1c781eba53af45436d098 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 24 Nov 2023 21:17:03 +0100 Subject: [PATCH 1109/1312] Some simplifications --- src/cdomains/arrayDomain.ml | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index d191562426..c20c85967e 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1921,16 +1921,14 @@ struct (A.map f t_f, N.top ()) let fold_left f acc (t_f, _) = A.fold_left f acc t_f - let smart_join x y (t_f1, t_n1) (t_f2, t_n2) = + let smart_binop op_a op_n x y (t_f1, t_n1) (t_f2, t_n2) = if get_bool "ana.base.arrays.nullbytes" then - (A.smart_join x y t_f1 t_f2, N.smart_join x y t_n1 t_n2) + (op_a x y t_f1 t_f2, op_n x y t_n1 t_n2) else - (A.smart_join x y t_f1 t_f2, N.top ()) - let smart_widen x y (t_f1, t_n1) (t_f2, t_n2) = - if get_bool "ana.base.arrays.nullbytes" then - (A.smart_widen x y t_f1 t_f2, N.smart_widen x y t_n1 t_n2) - else - (A.smart_widen x y t_f1 t_f2, N.top ()) + (op_a x y t_f1 t_f2, N.top ()) + + let smart_join = smart_binop A.smart_join N.smart_join + let smart_widen = smart_binop A.smart_widen N.smart_widen let smart_leq x y (t_f1, t_n1) (t_f2, t_n2) = if get_bool "ana.base.arrays.nullbytes" then A.smart_leq x y t_f1 t_f2 && N.smart_leq x y t_n1 t_n2 @@ -1947,16 +1945,18 @@ struct N.to_string_length t_n else Idx.top_of !Cil.kindOfSizeOf - let string_copy (t_f1, t_n1) (_, t_n2) n = - if get_bool "ana.base.arrays.nullbytes" then - (A.map Val.invalidate_abstract_value t_f1, N.string_copy t_n1 t_n2 n) - else - (A.map Val.invalidate_abstract_value t_f1, N.top ()) - let string_concat (t_f1, t_n1) (_, t_n2) n = + + (* invalidates the information in A, and applies op t_n1 t_n2 n *) + (* when ana.base.arrays.nullbytes is set *) + let string_op op (t_f1, t_n1) (_, t_n2) n = if get_bool "ana.base.arrays.nullbytes" then - (A.map Val.invalidate_abstract_value t_f1, N.string_concat t_n1 t_n2 n) + (A.map Val.invalidate_abstract_value t_f1, op t_n1 t_n2 n) else (A.map Val.invalidate_abstract_value t_f1, N.top ()) + + let string_copy = string_op N.string_copy + let string_concat = string_op N.string_concat + let substring_extraction (_, t_n1) (_, t_n2) = if get_bool "ana.base.arrays.nullbytes" then N.substring_extraction t_n1 t_n2 From 5f622616ae767430516e6e5ac86ae45f6e7fb3e6 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 24 Nov 2023 22:14:10 +0100 Subject: [PATCH 1110/1312] Simplify `AttributeConfiguredAndNullByteArrayDomain` --- src/cdomains/arrayDomain.ml | 74 ++++++++++++++----------------------- 1 file changed, 28 insertions(+), 46 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index c20c85967e..166447ed1d 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1897,16 +1897,38 @@ struct | _ -> f_get else f_get - let set (ask:VDQ.t) (t_f, t_n) i v = + + let construct a n = if get_bool "ana.base.arrays.nullbytes" then - (A.set ask t_f i v, N.set ask t_n i v) + (a, n ()) else - (A.set ask t_f i v, N.top ()) - let make ?(varAttr=[]) ?(typAttr=[]) i v = + (a, N.top ()) + + let set (ask:VDQ.t) (t_f, t_n) i v = construct (A.set ask t_f i v) (fun () -> N.set ask t_n i v) + let make ?(varAttr=[]) ?(typAttr=[]) i v = construct (A.make ~varAttr ~typAttr i v) (fun () -> N.make ~varAttr ~typAttr i v) + let map f (t_f, t_n) = construct (A.map f t_f) (fun () -> N.map f t_n) + let update_length newl (t_f, t_n) = construct (A.update_length newl t_f) (fun () -> N.update_length newl t_n) + + let smart_binop op_a op_n x y (t_f1, t_n1) (t_f2, t_n2) = construct (op_a x y t_f1 t_f2) (fun () -> op_n x y t_n1 t_n2) + + let smart_join = smart_binop A.smart_join N.smart_join + let smart_widen = smart_binop A.smart_widen N.smart_widen + + let string_op op (t_f1, t_n1) (_, t_n2) n = construct (A.map Val.invalidate_abstract_value t_f1) (fun () -> op t_n1 t_n2 n) + let string_copy = string_op N.string_copy + let string_concat = string_op N.string_concat + + let extract op default (_, t_n1) (_, t_n2) n = if get_bool "ana.base.arrays.nullbytes" then - (A.make ~varAttr ~typAttr i v, N.make i v) + op t_n1 t_n2 n else - (A.make ~varAttr ~typAttr i v, N.top ()) + (* Hidden behind unit, as constructing defaults may happen to early otherwise *) + (* e.g. for Idx.top_of IInt *) + default () + + let substring_extraction x y = extract (fun x y _ -> N.substring_extraction x y) (fun () -> IsMaybeSubstr) x y None + let string_comparison = extract N.string_comparison (fun () -> Idx.top_of IInt) + let length (t_f, t_n) = if get_bool "ana.base.arrays.nullbytes" then N.length t_n @@ -1914,21 +1936,8 @@ struct A.length t_f let move_if_affected ?(replace_with_const=false) (ask:VDQ.t) (t_f, t_n) v f = (A.move_if_affected ~replace_with_const ask t_f v f, N.move_if_affected ~replace_with_const ask t_n v f) let get_vars_in_e (t_f, _) = A.get_vars_in_e t_f - let map f (t_f, t_n) = - if get_bool "ana.base.arrays.nullbytes" then - (A.map f t_f, N.map f t_n) - else - (A.map f t_f, N.top ()) let fold_left f acc (t_f, _) = A.fold_left f acc t_f - let smart_binop op_a op_n x y (t_f1, t_n1) (t_f2, t_n2) = - if get_bool "ana.base.arrays.nullbytes" then - (op_a x y t_f1 t_f2, op_n x y t_n1 t_n2) - else - (op_a x y t_f1 t_f2, N.top ()) - - let smart_join = smart_binop A.smart_join N.smart_join - let smart_widen = smart_binop A.smart_widen N.smart_widen let smart_leq x y (t_f1, t_n1) (t_f2, t_n2) = if get_bool "ana.base.arrays.nullbytes" then A.smart_leq x y t_f1 t_f2 && N.smart_leq x y t_n1 t_n2 @@ -1946,33 +1955,6 @@ struct else Idx.top_of !Cil.kindOfSizeOf - (* invalidates the information in A, and applies op t_n1 t_n2 n *) - (* when ana.base.arrays.nullbytes is set *) - let string_op op (t_f1, t_n1) (_, t_n2) n = - if get_bool "ana.base.arrays.nullbytes" then - (A.map Val.invalidate_abstract_value t_f1, op t_n1 t_n2 n) - else - (A.map Val.invalidate_abstract_value t_f1, N.top ()) - - let string_copy = string_op N.string_copy - let string_concat = string_op N.string_concat - - let substring_extraction (_, t_n1) (_, t_n2) = - if get_bool "ana.base.arrays.nullbytes" then - N.substring_extraction t_n1 t_n2 - else - IsMaybeSubstr - let string_comparison (_, t_n1) (_, t_n2) n = - if get_bool "ana.base.arrays.nullbytes" then - N.string_comparison t_n1 t_n2 n - else - Idx.top_of IInt - - let update_length newl (t_f, t_n) = - if get_bool "ana.base.arrays.nullbytes" then - (A.update_length newl t_f, N.update_length newl t_n) - else - (A.update_length newl t_f, N.top ()) let project ?(varAttr=[]) ?(typAttr=[]) ask (t_f, t_n) = (A.project ~varAttr ~typAttr ask t_f, N.project ~varAttr ~typAttr ask t_n) let invariant ~value_invariant ~offset ~lval (t_f, _) = A.invariant ~value_invariant ~offset ~lval t_f end From 8c08a785a02b04d0af4a57f0c5cdce74780efb91 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 24 Nov 2023 23:23:26 +0200 Subject: [PATCH 1111/1312] Use opam 2.1 in releasing guide opam 2.1 with built-in depext is required to avoid qcheck version conflict with batteries. --- docs/developer-guide/releasing.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/docs/developer-guide/releasing.md b/docs/developer-guide/releasing.md index fc5f5f68a1..4f49399f13 100644 --- a/docs/developer-guide/releasing.md +++ b/docs/developer-guide/releasing.md @@ -37,13 +37,11 @@ 2. Extract distribution archive. 3. Run Docker container in extracted directory: `docker run -it --rm -v $(pwd):/goblint ocaml/opam:ubuntu-22.04-ocaml-4.14` (or newer). 4. Navigate to distribution archive inside Docker container: `cd /goblint`. - 5. Pin package from distribution archive: `opam pin add --no-action .`. - 6. Install depexts: `opam depext --with-test goblint`. - 7. Install and test package: `opam install --with-test goblint`. - 8. Activate opam environment: `eval $(opam env)`. - 9. Check version: `goblint --version`. - 10. Check that analysis works: `goblint -v tests/regression/04-mutex/01-simple_rc.c`. - 11. Exit Docker container. + 5. Install and test package from distribution archive: `opam-2.1 install --with-test .`. + 6. Activate opam environment: `eval $(opam env)`. + 7. Check version: `goblint --version`. + 8. Check that analysis works: `goblint -v tests/regression/04-mutex/01-simple_rc.c`. + 9. Exit Docker container. 12. Temporarily enable Zenodo GitHub webhook. From a50b1b86ec1aed6a37b1e6093efb00f5d271e796 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 24 Nov 2023 22:47:47 +0100 Subject: [PATCH 1112/1312] Steps towards simplifications --- src/cdomains/arrayDomain.ml | 147 +++++++++++------------------------- src/cdomains/nullByteSet.ml | 65 ++++++++++++++++ 2 files changed, 109 insertions(+), 103 deletions(-) create mode 100644 src/cdomains/nullByteSet.ml diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 166447ed1d..bb304af85e 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -998,55 +998,8 @@ end module NullByte (Val: LatticeWithNull) (Idx: IntDomain.Z): Str with type value = Val.t and type idx = Idx.t = struct - module MustSet = struct - module M = SetDomain.Reverse (SetDomain.ToppedSet (IntDomain.BigInt) (struct let topname = "All Null" end)) - include M - - let compute_set len = - List.init (Z.to_int len) Z.of_int - |> of_list - - let remove i must_nulls_set min_size = - if M.is_bot must_nulls_set then - M.remove i (compute_set min_size) - else - M.remove i must_nulls_set - - let filter cond must_nulls_set min_size = - if M.is_bot must_nulls_set then - M.filter cond (compute_set min_size) - else - M.filter cond must_nulls_set - - let min_elt must_nulls_set = - if M.is_bot must_nulls_set then - Z.zero - else - M.min_elt must_nulls_set - end - - module MaySet = struct - module M = SetDomain.ToppedSet (IntDomain.BigInt) (struct let topname = "All Null" end) - include M - - let remove i may_nulls_set max_size = - if M.is_top may_nulls_set then - M.remove i (MustSet.compute_set max_size) - else - M.remove i may_nulls_set - - let filter cond may_nulls_set max_size = - if M.is_top may_nulls_set then - M.filter cond (MustSet.compute_set max_size) - else - M.filter cond may_nulls_set - - let min_elt may_nulls_set = - if M.is_top may_nulls_set then - Z.zero - else - M.min_elt may_nulls_set - end + module MustSet = NullByteSet.MustSet + module MaySet = NullByteSet.MaySet (* (Must Null Set, May Null Set, Array Size) *) include Lattice.Prod3 (MustSet) (MaySet) (Idx) @@ -1058,26 +1011,14 @@ struct type ret = Null | NotNull | Top type substr = IsNotSubstr | IsSubstrAtIndex0 | IsMaybeSubstr + module ArrayOobMessage = M.Category.Behavior.Undefined.ArrayOutOfBounds + (* helper: returns Idx.maximal except for Overflows that are mapped to None *) let idx_maximal i = match Idx.maximal i with | Some i when Z.fits_int i -> Some i | _ -> None let get (ask: VDQ.t) (must_nulls_set, may_nulls_set, size) (e, i) = - let all_indexes_must_null i max = - if MustSet.is_bot must_nulls_set then - true - else if Z.lt (Z.of_int (MustSet.cardinal must_nulls_set)) (Z.sub max i) then - false - else - let rec check_all_indexes i = - if Z.gt i max then - true - else if MustSet.mem i must_nulls_set then - check_all_indexes (Z.succ i) - else - false in - check_all_indexes i in let min interval = match Idx.minimal interval with | Some min_num when Z.geq min_num Z.zero -> min_num | _ -> Z.zero in (* assume worst case minimal natural number *) @@ -1098,7 +1039,7 @@ struct (* if there is no maximum size *) | Some max_i, None when Z.geq max_i Z.zero -> (* ... and maximum value in index interval < minimal size, return Null if all numbers in index interval are in must_nulls_set *) - if Z.lt max_i min_size && all_indexes_must_null min_i max_i then + if Z.lt max_i min_size && MustSet.interval_mem (min_i,max_i) must_nulls_set then Null (* ... return NotNull if no number in index interval is in may_nulls_set *) else if not (MaySet.exists (fun x -> Z.geq x min_i && Z.leq x max_i) may_nulls_set) then @@ -1107,7 +1048,7 @@ struct Top | Some max_i, Some max_size when Z.geq max_i Z.zero -> (* if maximum value in index interval < minimal size, return Null if all numbers in index interval are in must_nulls_set *) - if Z.lt max_i min_size && all_indexes_must_null min_i max_i then + if Z.lt max_i min_size && MustSet.interval_mem (min_i,max_i) must_nulls_set then Null (* if maximum value in index interval < maximal size, return NotNull if no number in index interval is in may_nulls_set *) else if Z.lt max_i max_size && not (MaySet.exists (fun x -> Z.geq x min_i && Z.leq x max_i) may_nulls_set) then @@ -1232,22 +1173,22 @@ struct let min_i, max_i = match Idx.minimal i, idx_maximal i with | Some min_i, Some max_i -> if Z.lt min_i Z.zero && Z.lt max_i Z.zero then - (M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.before_start "Tries to create an array of negative size"; + (M.error ~category:ArrayOobMessage.before_start "Tries to create an array of negative size"; Z.zero, Some Z.zero) else if Z.lt min_i Z.zero then - (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.before_start "May try to create an array of negative size"; + (M.warn ~category:ArrayOobMessage.before_start "May try to create an array of negative size"; Z.zero, Some max_i) else min_i, Some max_i | None, Some max_i -> if Z.lt max_i Z.zero then - (M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.before_start "Tries to create an array of negative size"; + (M.error ~category:ArrayOobMessage.before_start "Tries to create an array of negative size"; Z.zero, Some Z.zero) else Z.zero, Some max_i | Some min_i, None -> if Z.lt min_i Z.zero then - (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.before_start "May try to create an array of negative size"; + (M.warn ~category:ArrayOobMessage.before_start "May try to create an array of negative size"; Z.zero, None) else min_i, None @@ -1302,11 +1243,11 @@ struct let to_string (must_nulls_set, may_nulls_set, size) = (* if must_nulls_set and min_nulls_set empty, definitely no null byte in array => warn about certain buffer overflow and return tuple unchanged *) if MustSet.is_empty must_nulls_set && MaySet.is_empty may_nulls_set then - (M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array access past end: buffer overflow"; + (M.error ~category:ArrayOobMessage.past_end "Array access past end: buffer overflow"; (must_nulls_set, may_nulls_set, size)) (* if only must_nulls_set empty, no certainty about array containing null byte => warn about potential buffer overflow and return tuple unchanged *) else if MustSet.is_empty must_nulls_set then - (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "May access array past end: potential buffer overflow"; + (M.warn ~category:ArrayOobMessage.past_end "May access array past end: potential buffer overflow"; (must_nulls_set, may_nulls_set, size)) else let min_must_null = MustSet.min_elt must_nulls_set in @@ -1363,20 +1304,20 @@ struct ((match Idx.minimal size, idx_maximal size with | Some min_size, Some max_size -> if Z.gt (Z.of_int n) max_size then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array size is smaller than n bytes; can cause a buffer overflow" + M.warn ~category:ArrayOobMessage.past_end "Array size is smaller than n bytes; can cause a buffer overflow" else if Z.gt (Z.of_int n) min_size then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array size might be smaller than n bytes; can cause a buffer overflow" + M.warn ~category:ArrayOobMessage.past_end "Array size might be smaller than n bytes; can cause a buffer overflow" | Some min_size, None -> if Z.gt (Z.of_int n) min_size then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array size might be smaller than n bytes; can cause a buffer overflow" + M.warn ~category:ArrayOobMessage.past_end "Array size might be smaller than n bytes; can cause a buffer overflow" | None, Some max_size -> if Z.gt (Z.of_int n) max_size then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array size is smaller than n bytes; can cause a buffer overflow" + M.warn ~category:ArrayOobMessage.past_end "Array size is smaller than n bytes; can cause a buffer overflow" | None, None -> ()); (* if definitely no null byte in array, i.e. must_nulls_set = may_nulls_set = empty set *) if MustSet.is_empty must_nulls_set && MaySet.is_empty may_nulls_set then - (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end + (M.warn ~category:ArrayOobMessage.past_end "Resulting string might not be null-terminated because src doesn't contain a null byte"; match idx_maximal size with (* ... there *may* be null bytes from maximal size to n - 1 if maximal size < n (i.e. past end) *) @@ -1402,13 +1343,13 @@ struct let to_string_length (must_nulls_set, may_nulls_set, size) = (* if must_nulls_set and min_nulls_set empty, definitely no null byte in array => return interval [size, inf) and warn *) if MustSet.is_empty must_nulls_set && MaySet.is_empty may_nulls_set then - (M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array doesn't contain a null byte: buffer overflow"; + (M.error ~category:ArrayOobMessage.past_end "Array doesn't contain a null byte: buffer overflow"; match Idx.minimal size with | Some min_size -> Idx.starting !Cil.kindOfSizeOf min_size | None -> Idx.starting !Cil.kindOfSizeOf Z.zero) (* if only must_nulls_set empty, no guarantee that null ever encountered in array => return interval [minimal may null, inf) and *) else if MustSet.is_empty must_nulls_set then - (M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array might not contain a null byte: potential buffer overflow"; + (M.warn ~category:ArrayOobMessage.past_end "Array might not contain a null byte: potential buffer overflow"; Idx.starting !Cil.kindOfSizeOf (MaySet.min_elt may_nulls_set)) (* else return interval [minimal may null, minimal must null] *) else @@ -1420,9 +1361,9 @@ struct match Idx.minimal size1, idx_maximal size1, Idx.minimal len2, idx_maximal len2 with | Some min_size1, Some max_size1, Some min_len2, Some max_len2 -> (if Z.lt max_size1 min_len2 then - M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src is greater than the allocated size for dest" + M.error ~category:ArrayOobMessage.past_end "The length of string src is greater than the allocated size for dest" else if Z.lt min_size1 max_len2 then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src may be greater than the allocated size for dest"); + M.warn ~category:ArrayOobMessage.past_end "The length of string src may be greater than the allocated size for dest"); let must_nulls_set_result = let min_size2 = match Idx.minimal size2' with | Some min_size2 -> min_size2 @@ -1442,7 +1383,7 @@ struct (must_nulls_set_result, may_nulls_set_result, size1) | Some min_size1, None, Some min_len2, Some max_len2 -> (if Z.lt min_size1 max_len2 then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src may be greater than the allocated size for dest"); + M.warn ~category:ArrayOobMessage.past_end "The length of string src may be greater than the allocated size for dest"); let must_nulls_set_result = let min_size2 = match Idx.minimal size2' with | Some min_size2 -> min_size2 @@ -1456,9 +1397,9 @@ struct (must_nulls_set_result, may_nulls_set_result, size1) | Some min_size1, Some max_size1, Some min_len2, None -> (if Z.lt max_size1 min_len2 then - M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src is greater than the allocated size for dest" + M.error ~category:ArrayOobMessage.past_end "The length of string src is greater than the allocated size for dest" else if Z.lt min_size1 min_len2 then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src may be greater than the allocated size for dest"); + M.warn ~category:ArrayOobMessage.past_end "The length of string src may be greater than the allocated size for dest"); (* do not keep any index of dest as no maximal strlen of src *) let must_nulls_set_result = let min_size2 = match Idx.minimal size2' with @@ -1474,7 +1415,7 @@ struct (must_nulls_set_result, may_nulls_set_result, size1) | Some min_size1, None, Some min_len2, None -> (if Z.lt min_size1 min_len2 then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The length of string src may be greater than the allocated size for dest"); + M.warn ~category:ArrayOobMessage.past_end "The length of string src may be greater than the allocated size for dest"); (* do not keep any index of dest as no maximal strlen of src *) let must_nulls_set_result = let min_size2 = match Idx.minimal size2' with @@ -1494,23 +1435,23 @@ struct (match Idx.minimal size1, idx_maximal size1, Idx.minimal size2, idx_maximal size2 with | Some min_size1, _, Some min_size2, _ when Z.lt min_size1 min_size2 -> if not (MaySet.exists (Z.gt min_size1) may_nulls_set2) then - M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "src doesn't contain a null byte at an index smaller than the size of dest" + M.error ~category:ArrayOobMessage.past_end "src doesn't contain a null byte at an index smaller than the size of dest" else if not (MustSet.exists (Z.gt min_size1) must_nulls_set2) then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "src may not contain a null byte at an index smaller than the size of dest" + M.warn ~category:ArrayOobMessage.past_end "src may not contain a null byte at an index smaller than the size of dest" | Some min_size1, _, _, Some max_size2 when Z.lt min_size1 max_size2 -> if not (MaySet.exists (Z.gt min_size1) may_nulls_set2) then - M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "src doesn't contain a null byte at an index smaller than the size of dest" + M.error ~category:ArrayOobMessage.past_end "src doesn't contain a null byte at an index smaller than the size of dest" else if not (MustSet.exists (Z.gt min_size1) must_nulls_set2) then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "src may not contain a null byte at an index smaller than the size of dest" + M.warn ~category:ArrayOobMessage.past_end "src may not contain a null byte at an index smaller than the size of dest" | Some min_size1, _, _, None -> if not (MustSet.exists (Z.gt min_size1) must_nulls_set2) then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "src may not contain a null byte at an index smaller than the size of dest" + M.warn ~category:ArrayOobMessage.past_end "src may not contain a null byte at an index smaller than the size of dest" | _, Some max_size1, _, Some max_size2 when Z.lt max_size1 max_size2 -> if not (MustSet.exists (Z.gt max_size1) must_nulls_set2) then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "src may not contain a null byte at an index smaller than the size of dest" + M.warn ~category:ArrayOobMessage.past_end "src may not contain a null byte at an index smaller than the size of dest" |_, Some max_size1, _, None -> if not (MustSet.exists (Z.gt max_size1) must_nulls_set2) then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "src may not contain a null byte at an index smaller than the size of dest" + M.warn ~category:ArrayOobMessage.past_end "src may not contain a null byte at an index smaller than the size of dest" | _ -> ()) in match n with @@ -1531,10 +1472,10 @@ struct let update_sets min_size1 max_size1 max_size1_exists minlen1 maxlen1 maxlen1_exists minlen2 maxlen2 maxlen2_exists must_nulls_set2' may_nulls_set2' = (* track any potential buffer overflow and issue warning if needed *) (if max_size1_exists && Z.leq max_size1 (Z.add minlen1 minlen2) then - M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end + M.error ~category:ArrayOobMessage.past_end "The length of the concatenation of the strings in src and dest is greater than the allocated size for dest" else if (maxlen1_exists && maxlen2_exists && Z.leq min_size1 (Z.add maxlen1 maxlen2)) || not maxlen1_exists || not maxlen2_exists then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end + M.warn ~category:ArrayOobMessage.past_end "The length of the concatenation of the strings in src and dest may be greater than the allocated size for dest"); (* if any must_nulls_set empty, result must_nulls_set also empty; * for all i1, i2 in may_nulls_set1, may_nulls_set2: add i1 + i2 if it is <= strlen(dest) + strlen(src) to new may_nulls_set @@ -1702,13 +1643,13 @@ struct | None -> (* track any potential buffer overflow and issue warning if needed *) (if MustSet.is_empty must_nulls_set1 && MaySet.is_empty may_nulls_set1 then - M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array of string 1 doesn't contain a null byte: buffer overflow" + M.error ~category:ArrayOobMessage.past_end "Array of string 1 doesn't contain a null byte: buffer overflow" else if MustSet.is_empty must_nulls_set1 then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array of string 1 might not contain a null byte: potential buffer overflow"); + M.warn ~category:ArrayOobMessage.past_end "Array of string 1 might not contain a null byte: potential buffer overflow"); (if MustSet.is_empty must_nulls_set2 && MaySet.is_empty may_nulls_set2 then - M.error ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array of string 2 doesn't contain a null byte: buffer overflow" + M.error ~category:ArrayOobMessage.past_end "Array of string 2 doesn't contain a null byte: buffer overflow" else if MustSet.is_empty must_nulls_set2 then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "Array of string 2 might not contain a null byte: potential buffer overflow"); + M.warn ~category:ArrayOobMessage.past_end "Array of string 2 might not contain a null byte: potential buffer overflow"); (* compute abstract value for result of strcmp *) compare Z.zero false (* strncmp *) @@ -1723,21 +1664,21 @@ struct (match idx_maximal size1 with | Some max_size1 -> if Z.gt (Z.of_int n) max_size1 then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The size of the array of string 1 is smaller than n bytes" + M.warn ~category:ArrayOobMessage.past_end "The size of the array of string 1 is smaller than n bytes" else if Z.gt (Z.of_int n) min_size1 then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The size of the array of string 1 might be smaller than n bytes" + M.warn ~category:ArrayOobMessage.past_end "The size of the array of string 1 might be smaller than n bytes" | None -> if Z.gt (Z.of_int n) min_size1 then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The size of the array of string 1 might be smaller than n bytes"); + M.warn ~category:ArrayOobMessage.past_end "The size of the array of string 1 might be smaller than n bytes"); (match idx_maximal size2 with | Some max_size2 -> if Z.gt (Z.of_int n) max_size2 then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The size of the array of string 2 is smaller than n bytes" + M.warn ~category:ArrayOobMessage.past_end "The size of the array of string 2 is smaller than n bytes" else if Z.gt (Z.of_int n) min_size2 then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The size of the array of string 2 might be smaller than n bytes" + M.warn ~category:ArrayOobMessage.past_end "The size of the array of string 2 might be smaller than n bytes" | None -> if Z.gt (Z.of_int n) min_size2 then - M.warn ~category:M.Category.Behavior.Undefined.ArrayOutOfBounds.past_end "The size of the array of string 2 might be smaller than n bytes"); + M.warn ~category:ArrayOobMessage.past_end "The size of the array of string 2 might be smaller than n bytes"); (* compute abstract value for result of strncmp *) compare (Z.of_int n) true | _ -> Idx.top_of IInt diff --git a/src/cdomains/nullByteSet.ml b/src/cdomains/nullByteSet.ml new file mode 100644 index 0000000000..5977023b8e --- /dev/null +++ b/src/cdomains/nullByteSet.ml @@ -0,0 +1,65 @@ +module MustSet = struct + module M = SetDomain.Reverse (SetDomain.ToppedSet (IntDomain.BigInt) (struct let topname = "All Null" end)) + include M + + let compute_set len = + List.init (Z.to_int len) Z.of_int + |> of_list + + let remove i must_nulls_set min_size = + if M.is_bot must_nulls_set then + M.remove i (compute_set min_size) + else + M.remove i must_nulls_set + + let filter cond must_nulls_set min_size = + if M.is_bot must_nulls_set then + M.filter cond (compute_set min_size) + else + M.filter cond must_nulls_set + + let min_elt must_nulls_set = + if M.is_bot must_nulls_set then + Z.zero + else + M.min_elt must_nulls_set + + + let interval_mem (l,u) set = + if M.is_bot set then + true + else if Z.lt (Z.of_int (M.cardinal set)) (Z.sub u l) then + false + else + let rec check_all_indexes i = + if Z.gt i u then + true + else if M.mem i set then + check_all_indexes (Z.succ i) + else + false in + check_all_indexes l +end + +module MaySet = struct + module M = SetDomain.ToppedSet (IntDomain.BigInt) (struct let topname = "All Null" end) + include M + + let remove i may_nulls_set max_size = + if M.is_top may_nulls_set then + M.remove i (MustSet.compute_set max_size) + else + M.remove i may_nulls_set + + let filter cond may_nulls_set max_size = + if M.is_top may_nulls_set then + M.filter cond (MustSet.compute_set max_size) + else + M.filter cond may_nulls_set + + let min_elt may_nulls_set = + if M.is_top may_nulls_set then + Z.zero + else + M.min_elt may_nulls_set +end From 86b7c35bb981b5b7264ade4ef0073b226518b8fc Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 24 Nov 2023 23:54:16 +0100 Subject: [PATCH 1113/1312] Attempts towards simplification --- src/cdomains/arrayDomain.ml | 89 ++++++++++++++++++++++--------------- src/cdomains/nullByteSet.ml | 32 ++++++++++++- 2 files changed, 84 insertions(+), 37 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index bb304af85e..741207c9e4 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1000,6 +1000,7 @@ module NullByte (Val: LatticeWithNull) (Idx: IntDomain.Z): Str with type value = struct module MustSet = NullByteSet.MustSet module MaySet = NullByteSet.MaySet + module Nulls = NullByteSet.MustMaySet (* (Must Null Set, May Null Set, Array Size) *) include Lattice.Prod3 (MustSet) (MaySet) (Idx) @@ -1019,6 +1020,7 @@ struct | _ -> None let get (ask: VDQ.t) (must_nulls_set, may_nulls_set, size) (e, i) = + let nulls = (must_nulls_set, may_nulls_set) in let min interval = match Idx.minimal interval with | Some min_num when Z.geq min_num Z.zero -> min_num | _ -> Z.zero in (* assume worst case minimal natural number *) @@ -1031,7 +1033,7 @@ struct (* if there is no maximum value in index interval *) | None, _ -> (* ... return NotNull if no i >= min_i in may_nulls_set *) - if not (MaySet.exists (Z.leq min_i) may_nulls_set) then + if not (Nulls.may_exist (Z.leq min_i) nulls) then NotNull (* ... else return Top *) else @@ -1039,26 +1041,29 @@ struct (* if there is no maximum size *) | Some max_i, None when Z.geq max_i Z.zero -> (* ... and maximum value in index interval < minimal size, return Null if all numbers in index interval are in must_nulls_set *) - if Z.lt max_i min_size && MustSet.interval_mem (min_i,max_i) must_nulls_set then + if Z.lt max_i min_size && Nulls.must_mem_interval (min_i,max_i) nulls then Null (* ... return NotNull if no number in index interval is in may_nulls_set *) - else if not (MaySet.exists (fun x -> Z.geq x min_i && Z.leq x max_i) may_nulls_set) then + else if not (Nulls.may_exist (fun x -> Z.geq x min_i && Z.leq x max_i) nulls) then NotNull else Top | Some max_i, Some max_size when Z.geq max_i Z.zero -> (* if maximum value in index interval < minimal size, return Null if all numbers in index interval are in must_nulls_set *) - if Z.lt max_i min_size && MustSet.interval_mem (min_i,max_i) must_nulls_set then + if Z.lt max_i min_size && Nulls.must_mem_interval (min_i, max_i) nulls then Null (* if maximum value in index interval < maximal size, return NotNull if no number in index interval is in may_nulls_set *) - else if Z.lt max_i max_size && not (MaySet.exists (fun x -> Z.geq x min_i && Z.leq x max_i) may_nulls_set) then + else if Z.lt max_i max_size && not (Nulls.may_exist (fun x -> Z.geq x min_i && Z.leq x max_i) nulls) then NotNull else Top (* if maximum number in interval is invalid, i.e. negative, return Top of value *) | _ -> Top - let set (ask: VDQ.t) (must_nulls_set, may_nulls_set, size) (e, i) v = + let uf ((a,b),c) = (a,b,c) + + let set (ask: VDQ.t) ((must_nulls_set, may_nulls_set, size) as x) (e, i) v = + let nulls = (must_nulls_set, may_nulls_set) in let rec add_indexes i max may_nulls_set = if Z.gt i max then may_nulls_set @@ -1144,30 +1149,37 @@ struct (if Val.is_null v && idx_maximal size = None then match idx_maximal size with (* ... and there is no maximal size, modify may_nulls_set to top *) - | None -> (must_nulls_set, MaySet.top (), size) + | None -> uf @@ (Nulls.forget_may nulls, size) (* ... and there is a maximal size, add all i from minimal index to maximal size to may_nulls_set *) - | Some max_size -> (must_nulls_set, add_indexes min_i (Z.pred max_size) may_nulls_set, size) + | Some max_size -> uf @@ (Nulls.add_may_interval (min_i, Z.pred max_size) nulls, size) (* ... and value <> null, only keep indexes < minimal index in must_nulls_set *) else if Val.is_not_null v then - (MustSet.filter (Z.gt min_i) must_nulls_set min_size, may_nulls_set, size) + uf @@ (Nulls.filter_musts (Z.gt min_i) min_size nulls, size) (*..., value unknown *) else match Idx.minimal size, idx_maximal size with (* ... and size unknown, modify both sets to top *) - | None, None -> (MustSet.top (), MaySet.top (), size) + | None, None -> uf @@ (Nulls.top (), size) (* ... and only minimal size known, remove all indexes < minimal size from must_nulls_set and modify may_nulls_set to top *) - | Some min_size, None -> (MustSet.filter (Z.gt min_size) must_nulls_set min_size, MaySet.top (), size) + | Some min_size, None -> + let nulls = Nulls.forget_may nulls in + uf @@ (Nulls.filter_musts (Z.gt min_size) min_size nulls, size) (* ... and only maximal size known, modify must_nulls_set to top and add all i from minimal index to maximal size to may_nulls_set *) - | None, Some max_size -> (MustSet.top (), add_indexes min_i (Z.pred max_size) may_nulls_set, size) + | None, Some max_size -> + let nulls = Nulls.forget_must nulls in + uf @@ (Nulls.add_may_interval (min_i, Z.pred max_size) nulls, size) (* ... and size is known, remove all indexes < minimal size from must_nulls_set and add all i from minimal index to maximal size to may_nulls_set *) - | Some min_size, Some max_size -> (MustSet.filter (Z.gt min_size) must_nulls_set min_size, add_indexes min_i (Z.pred max_size) may_nulls_set, size)) + | Some min_size, Some max_size -> + let nulls = Nulls.filter_musts (Z.gt min_size) min_size nulls in + uf @@ (Nulls.add_may_interval (min_i, Z.pred max_size) nulls, size) + ) | Some max_i when Z.geq max_i Z.zero -> if Z.equal min_i max_i then set_exact min_i else (set_interval_must min_i max_i, set_interval_may min_i max_i, size) (* if maximum number in interval is invalid, i.e. negative, return tuple unmodified *) - | _ -> (must_nulls_set, may_nulls_set, size) + | _ -> x let make ?(varAttr=[]) ?(typAttr=[]) i v = let min_i, max_i = match Idx.minimal i, idx_maximal i with @@ -1240,20 +1252,21 @@ struct (set, set, Idx.of_int ILong (Z.succ last_null)) (** Returns an abstract value with at most one null byte marking the end of the string *) - let to_string (must_nulls_set, may_nulls_set, size) = + let to_string ((must_nulls_set, may_nulls_set, size) as x) = + let nulls = (must_nulls_set, may_nulls_set) in (* if must_nulls_set and min_nulls_set empty, definitely no null byte in array => warn about certain buffer overflow and return tuple unchanged *) - if MustSet.is_empty must_nulls_set && MaySet.is_empty may_nulls_set then - (M.error ~category:ArrayOobMessage.past_end "Array access past end: buffer overflow"; - (must_nulls_set, may_nulls_set, size)) + if Nulls.must_be_empty nulls then + (M.error ~category:ArrayOobMessage.past_end "Array access past end: buffer overflow"; x) (* if only must_nulls_set empty, no certainty about array containing null byte => warn about potential buffer overflow and return tuple unchanged *) - else if MustSet.is_empty must_nulls_set then - (M.warn ~category:ArrayOobMessage.past_end "May access array past end: potential buffer overflow"; - (must_nulls_set, may_nulls_set, size)) + else if Nulls.may_be_empty nulls then + (M.warn ~category:ArrayOobMessage.past_end "May access array past end: potential buffer overflow"; x) else - let min_must_null = MustSet.min_elt must_nulls_set in + let min_must_null = Nulls.min_must_elem nulls in + let min_may_null = Nulls.min_may_elem nulls in (* if smallest index in sets coincides, only this null byte is kept in both sets *) - if Z.equal min_must_null (MaySet.min_elt may_nulls_set) then - (MustSet.singleton min_must_null, MaySet.singleton min_must_null, Idx.of_int ILong (Z.succ min_must_null)) + if Z.equal min_must_null min_may_null then + let (must,may) = Nulls.precise_singleton min_must_null in + (must, may, Idx.of_int ILong (Z.succ min_must_null)) (* else return empty must_nulls_set and keep every index up to smallest index of must_nulls_set included in may_nulls_set *) else match idx_maximal size with @@ -1273,6 +1286,7 @@ struct * marking the end of the string and if needed followed by further null bytes to obtain * an n bytes string. *) let to_n_string (must_nulls_set, may_nulls_set, size) n = + let nulls = (must_nulls_set, may_nulls_set) in let rec add_indexes i max set = if Z.geq i max then set @@ -1316,7 +1330,7 @@ struct | None, None -> ()); (* if definitely no null byte in array, i.e. must_nulls_set = may_nulls_set = empty set *) - if MustSet.is_empty must_nulls_set && MaySet.is_empty may_nulls_set then + if Nulls.must_be_empty nulls then (M.warn ~category:ArrayOobMessage.past_end "Resulting string might not be null-terminated because src doesn't contain a null byte"; match idx_maximal size with @@ -1325,13 +1339,13 @@ struct | _ -> (must_nulls_set, may_nulls_set, Idx.of_int ILong (Z.of_int n))) (* if only must_nulls_set empty, remove indexes >= n from may_nulls_set and add all indexes from minimal may null index to n - 1; * warn as in any case, resulting array not guaranteed to contain null byte *) - else if MustSet.is_empty must_nulls_set then - let min_may_null = MaySet.min_elt may_nulls_set in + else if Nulls.may_be_empty nulls then + let min_may_null = Nulls.min_may_elem nulls in warn_no_null Z.zero false min_may_null; (must_nulls_set, update_may_indexes min_may_null may_nulls_set, Idx.of_int ILong (Z.of_int n)) else - let min_must_null = MustSet.min_elt must_nulls_set in - let min_may_null = MaySet.min_elt may_nulls_set in + let min_must_null = Nulls.min_must_elem nulls in + let min_may_null = Nulls.min_may_elem nulls in (* warn if resulting array may not contain null byte *) warn_no_null min_must_null true min_may_null; (* if min_must_null = min_may_null, remove indexes >= n and add all indexes from minimal must/may null to n - 1 in the sets *) @@ -1341,19 +1355,21 @@ struct (MustSet.top (), update_may_indexes min_may_null may_nulls_set, Idx.of_int ILong (Z.of_int n))) let to_string_length (must_nulls_set, may_nulls_set, size) = + let nulls = (must_nulls_set, may_nulls_set) in (* if must_nulls_set and min_nulls_set empty, definitely no null byte in array => return interval [size, inf) and warn *) - if MustSet.is_empty must_nulls_set && MaySet.is_empty may_nulls_set then + (* TODO: check of must set really needed? *) + if Nulls.must_be_empty nulls then (M.error ~category:ArrayOobMessage.past_end "Array doesn't contain a null byte: buffer overflow"; match Idx.minimal size with | Some min_size -> Idx.starting !Cil.kindOfSizeOf min_size | None -> Idx.starting !Cil.kindOfSizeOf Z.zero) (* if only must_nulls_set empty, no guarantee that null ever encountered in array => return interval [minimal may null, inf) and *) - else if MustSet.is_empty must_nulls_set then + else if Nulls.may_be_empty nulls then (M.warn ~category:ArrayOobMessage.past_end "Array might not contain a null byte: potential buffer overflow"; - Idx.starting !Cil.kindOfSizeOf (MaySet.min_elt may_nulls_set)) + Idx.starting !Cil.kindOfSizeOf (Nulls.min_may_elem nulls)) (* else return interval [minimal may null, minimal must null] *) else - Idx.of_interval !Cil.kindOfSizeOf (MaySet.min_elt may_nulls_set, MustSet.min_elt must_nulls_set) + Idx.of_interval !Cil.kindOfSizeOf (Nulls.min_may_elem nulls, Nulls.min_must_elem nulls) let string_copy (must_nulls_set1, may_nulls_set1, size1) (must_nulls_set2, may_nulls_set2, size2) n = (* filter out indexes before strlen(src) from dest sets and after strlen(src) from src sets and build union, keep size of dest *) @@ -1599,13 +1615,14 @@ struct compute_concat must_nulls_set2' may_nulls_set2' | _ -> (MustSet.top (), MaySet.top (), size1) - let substring_extraction haystack (must_nulls_set_needle, may_nulls_set_needle, size_needle) = + let substring_extraction haystack ((must_needle, may_needle, size_needle) as needle) = + let nulls_needle = (must_needle, may_needle) in (* if needle is empty string, i.e. certain null byte at index 0, return value of strstr is pointer to haystack *) - if MustSet.mem Z.zero must_nulls_set_needle then + if Nulls.must_mem Z.zero nulls_needle then IsSubstrAtIndex0 else let haystack_len = to_string_length haystack in - let needle_len = to_string_length (must_nulls_set_needle, may_nulls_set_needle, size_needle) in + let needle_len = to_string_length needle in match idx_maximal haystack_len, Idx.minimal needle_len with | Some haystack_max, Some needle_min -> (* if strlen(haystack) < strlen(needle), needle can never be substring of haystack => return None *) diff --git a/src/cdomains/nullByteSet.ml b/src/cdomains/nullByteSet.ml index 5977023b8e..3fc3889ffc 100644 --- a/src/cdomains/nullByteSet.ml +++ b/src/cdomains/nullByteSet.ml @@ -24,7 +24,6 @@ module MustSet = struct else M.min_elt must_nulls_set - let interval_mem (l,u) set = if M.is_bot set then true @@ -63,3 +62,34 @@ module MaySet = struct else M.min_elt may_nulls_set end + +module MustMaySet = struct + include Lattice.Prod (MustSet) (MaySet) + + let must_mem i (musts, mays) = MustSet.mem i musts + let must_mem_interval (l,u) (musts, mays) = MustSet.interval_mem (l,u) musts + + let may_be_empty (musts, mays) = MustSet.is_empty musts + let must_be_empty (musts, mays) = MaySet.is_empty mays + + let min_may_elem (musts, mays) = MaySet.min_elt mays + let min_must_elem (musts, mays) = MustSet.min_elt musts + + let add_may_interval (l,u) (musts, mays) = + let rec add_indexes i max set = + if Z.gt i max then + set + else + add_indexes (Z.succ i) max (MaySet.add i set) + in + (musts, add_indexes l u mays) + + let precise_singleton i = + (MustSet.singleton i, MaySet.singleton i) + + let may_exist f (musts, mays) = MaySet.exists f mays + + let forget_may (musts, mays) = (musts, MaySet.top ()) + let forget_must (musts, mays) = (MustSet.top (), mays) + let filter_musts f min_size (musts, mays) = (MustSet.filter f musts min_size, mays) +end \ No newline at end of file From a354e63052d0b80d37ff5cb29b953348411e5097 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 24 Nov 2023 23:57:19 +0100 Subject: [PATCH 1114/1312] Simplify --- src/cdomains/arrayDomain.ml | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 741207c9e4..9b890980bf 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1600,17 +1600,11 @@ struct if not (MaySet.exists (Z.gt (Z.of_int n)) may_nulls_set2) then (MustSet.singleton (Z.of_int n), MaySet.singleton (Z.of_int n)) else if not (MustSet.exists (Z.gt (Z.of_int n)) must_nulls_set2) then - let max_size2 = match idx_maximal size2 with - | Some max_size2 -> max_size2 - | None -> Z.succ (Z.of_int n) in + let max_size2 = BatOption.default (Z.succ (Z.of_int n)) (idx_maximal size2) in (MustSet.empty (), MaySet.add (Z.of_int n) (MaySet.filter (Z.geq (Z.of_int n)) may_nulls_set2 max_size2)) else - let min_size2 = match Idx.minimal size2 with - | Some min_size2 -> min_size2 - | None -> Z.zero in - let max_size2 = match idx_maximal size2 with - | Some max_size2 -> max_size2 - | None -> Z.of_int n in + let min_size2 = BatOption.default Z.zero (Idx.minimal size2) in + let max_size2 = BatOption.default (Z.of_int n) (idx_maximal size2) in (MustSet.filter (Z.gt (Z.of_int n)) must_nulls_set2 min_size2, MaySet.filter (Z.gt (Z.of_int n)) may_nulls_set2 max_size2) in compute_concat must_nulls_set2' may_nulls_set2' | _ -> (MustSet.top (), MaySet.top (), size1) From 8933c0a0a31616232934dcd289889a6f2f46cd06 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sat, 25 Nov 2023 12:58:59 +0100 Subject: [PATCH 1115/1312] Simplify --- src/cdomains/arrayDomain.ml | 32 ++++++++++++++++---------------- src/cdomains/nullByteSet.ml | 29 +++++++++++++++++++++-------- 2 files changed, 37 insertions(+), 24 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 9b890980bf..02f9fe8d31 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1041,7 +1041,7 @@ struct (* if there is no maximum size *) | Some max_i, None when Z.geq max_i Z.zero -> (* ... and maximum value in index interval < minimal size, return Null if all numbers in index interval are in must_nulls_set *) - if Z.lt max_i min_size && Nulls.must_mem_interval (min_i,max_i) nulls then + if Z.lt max_i min_size && Nulls.interval_mem Definitely (min_i,max_i) nulls then Null (* ... return NotNull if no number in index interval is in may_nulls_set *) else if not (Nulls.may_exist (fun x -> Z.geq x min_i && Z.leq x max_i) nulls) then @@ -1050,7 +1050,7 @@ struct Top | Some max_i, Some max_size when Z.geq max_i Z.zero -> (* if maximum value in index interval < minimal size, return Null if all numbers in index interval are in must_nulls_set *) - if Z.lt max_i min_size && Nulls.must_mem_interval (min_i, max_i) nulls then + if Z.lt max_i min_size && Nulls.interval_mem Definitely (min_i, max_i) nulls then Null (* if maximum value in index interval < maximal size, return NotNull if no number in index interval is in may_nulls_set *) else if Z.lt max_i max_size && not (Nulls.may_exist (fun x -> Z.geq x min_i && Z.leq x max_i) nulls) then @@ -1255,14 +1255,14 @@ struct let to_string ((must_nulls_set, may_nulls_set, size) as x) = let nulls = (must_nulls_set, may_nulls_set) in (* if must_nulls_set and min_nulls_set empty, definitely no null byte in array => warn about certain buffer overflow and return tuple unchanged *) - if Nulls.must_be_empty nulls then + if Nulls.is_empty Definitely nulls then (M.error ~category:ArrayOobMessage.past_end "Array access past end: buffer overflow"; x) (* if only must_nulls_set empty, no certainty about array containing null byte => warn about potential buffer overflow and return tuple unchanged *) - else if Nulls.may_be_empty nulls then + else if Nulls.is_empty Possibly nulls then (M.warn ~category:ArrayOobMessage.past_end "May access array past end: potential buffer overflow"; x) else - let min_must_null = Nulls.min_must_elem nulls in - let min_may_null = Nulls.min_may_elem nulls in + let min_must_null = Nulls.min_elem Definitely nulls in + let min_may_null = Nulls.min_elem Possibly nulls in (* if smallest index in sets coincides, only this null byte is kept in both sets *) if Z.equal min_must_null min_may_null then let (must,may) = Nulls.precise_singleton min_must_null in @@ -1330,7 +1330,7 @@ struct | None, None -> ()); (* if definitely no null byte in array, i.e. must_nulls_set = may_nulls_set = empty set *) - if Nulls.must_be_empty nulls then + if Nulls.is_empty Definitely nulls then (M.warn ~category:ArrayOobMessage.past_end "Resulting string might not be null-terminated because src doesn't contain a null byte"; match idx_maximal size with @@ -1339,13 +1339,13 @@ struct | _ -> (must_nulls_set, may_nulls_set, Idx.of_int ILong (Z.of_int n))) (* if only must_nulls_set empty, remove indexes >= n from may_nulls_set and add all indexes from minimal may null index to n - 1; * warn as in any case, resulting array not guaranteed to contain null byte *) - else if Nulls.may_be_empty nulls then - let min_may_null = Nulls.min_may_elem nulls in + else if Nulls.is_empty Possibly nulls then + let min_may_null = Nulls.min_elem Possibly nulls in warn_no_null Z.zero false min_may_null; (must_nulls_set, update_may_indexes min_may_null may_nulls_set, Idx.of_int ILong (Z.of_int n)) else - let min_must_null = Nulls.min_must_elem nulls in - let min_may_null = Nulls.min_may_elem nulls in + let min_must_null = Nulls.min_elem Definitely nulls in + let min_may_null = Nulls.min_elem Possibly nulls in (* warn if resulting array may not contain null byte *) warn_no_null min_must_null true min_may_null; (* if min_must_null = min_may_null, remove indexes >= n and add all indexes from minimal must/may null to n - 1 in the sets *) @@ -1358,18 +1358,18 @@ struct let nulls = (must_nulls_set, may_nulls_set) in (* if must_nulls_set and min_nulls_set empty, definitely no null byte in array => return interval [size, inf) and warn *) (* TODO: check of must set really needed? *) - if Nulls.must_be_empty nulls then + if Nulls.is_empty Definitely nulls then (M.error ~category:ArrayOobMessage.past_end "Array doesn't contain a null byte: buffer overflow"; match Idx.minimal size with | Some min_size -> Idx.starting !Cil.kindOfSizeOf min_size | None -> Idx.starting !Cil.kindOfSizeOf Z.zero) (* if only must_nulls_set empty, no guarantee that null ever encountered in array => return interval [minimal may null, inf) and *) - else if Nulls.may_be_empty nulls then + else if Nulls.is_empty Possibly nulls then (M.warn ~category:ArrayOobMessage.past_end "Array might not contain a null byte: potential buffer overflow"; - Idx.starting !Cil.kindOfSizeOf (Nulls.min_may_elem nulls)) + Idx.starting !Cil.kindOfSizeOf (Nulls.min_elem Possibly nulls)) (* else return interval [minimal may null, minimal must null] *) else - Idx.of_interval !Cil.kindOfSizeOf (Nulls.min_may_elem nulls, Nulls.min_must_elem nulls) + Idx.of_interval !Cil.kindOfSizeOf (Nulls.min_elem Possibly nulls, Nulls.min_elem Definitely nulls) let string_copy (must_nulls_set1, may_nulls_set1, size1) (must_nulls_set2, may_nulls_set2, size2) n = (* filter out indexes before strlen(src) from dest sets and after strlen(src) from src sets and build union, keep size of dest *) @@ -1612,7 +1612,7 @@ struct let substring_extraction haystack ((must_needle, may_needle, size_needle) as needle) = let nulls_needle = (must_needle, may_needle) in (* if needle is empty string, i.e. certain null byte at index 0, return value of strstr is pointer to haystack *) - if Nulls.must_mem Z.zero nulls_needle then + if Nulls.mem Definitely Z.zero nulls_needle then IsSubstrAtIndex0 else let haystack_len = to_string_length haystack in diff --git a/src/cdomains/nullByteSet.ml b/src/cdomains/nullByteSet.ml index 3fc3889ffc..ea8f963ab0 100644 --- a/src/cdomains/nullByteSet.ml +++ b/src/cdomains/nullByteSet.ml @@ -66,14 +66,27 @@ end module MustMaySet = struct include Lattice.Prod (MustSet) (MaySet) - let must_mem i (musts, mays) = MustSet.mem i musts - let must_mem_interval (l,u) (musts, mays) = MustSet.interval_mem (l,u) musts - - let may_be_empty (musts, mays) = MustSet.is_empty musts - let must_be_empty (musts, mays) = MaySet.is_empty mays - - let min_may_elem (musts, mays) = MaySet.min_elt mays - let min_must_elem (musts, mays) = MustSet.min_elt musts + type mode = Definitely | Possibly + + let is_empty mode (musts, mays) = + match mode with + | Definitely -> MaySet.is_empty mays + | Possibly -> MustSet.is_empty musts + + let min_elem mode (musts, mays) = + match mode with + | Definitely -> MustSet.min_elt musts + | Possibly -> MaySet.min_elt mays + + let mem mode i (musts, mays) = + match mode with + | Definitely -> MustSet.mem i musts + | Possibly -> MaySet.mem i mays + + let interval_mem mode (l,u) (musts, mays) = + match mode with + | Definitely -> MustSet.interval_mem (l,u) musts + | Possibly -> failwith "not implemented" let add_may_interval (l,u) (musts, mays) = let rec add_indexes i max set = From 09c069d7168968b412bd1cbc3ac80643b67b52e8 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sat, 25 Nov 2023 13:17:57 +0100 Subject: [PATCH 1116/1312] Simplify --- src/cdomains/arrayDomain.ml | 51 ++++++++++++++++++++----------------- src/cdomains/nullByteSet.ml | 10 ++++++++ 2 files changed, 38 insertions(+), 23 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 02f9fe8d31..cfcc702bb4 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1077,40 +1077,42 @@ struct let min_i = min i in let max_i = idx_maximal i in - let set_exact i = + let set_exact_nulls i = match idx_maximal size with (* if size has no upper limit *) | None -> (* ... and value <> null, remove i from must_nulls_set and also from may_nulls_set if not top *) if Val.is_not_null v && not (MaySet.is_top may_nulls_set) then - (MustSet.remove i must_nulls_set min_size, MaySet.M.remove i may_nulls_set, size) + Nulls.remove Definitely i nulls min_size else if Val.is_not_null v then - (MustSet.remove i must_nulls_set min_size, may_nulls_set, size) + Nulls.remove Possibly i nulls min_size (* ..., i < minimal size and value = null, add i to must_nulls_set and may_nulls_set *) else if Z.lt i min_size && Val.is_null v then - (MustSet.add i must_nulls_set, MaySet.add i may_nulls_set, size) + Nulls.add Definitely i nulls (* ..., i >= minimal size and value = null, add i only to may_nulls_set *) else if Val.is_null v then - (must_nulls_set, MaySet.add i may_nulls_set, size) + Nulls.add Possibly i nulls (* ... and value unknown, remove i from must_nulls_set and add it to may_nulls_set *) else - (MustSet.remove i must_nulls_set min_size, MaySet.add i may_nulls_set, size) + let removed = Nulls.remove Possibly i nulls min_size in + Nulls.add Possibly i removed | Some max_size -> (* if value <> null, remove i from must_nulls_set and may_nulls_set *) if Val.is_not_null v then - (MustSet.remove i must_nulls_set min_size, MaySet.remove i may_nulls_set max_size, size) + Nulls.remove Definitely i nulls min_size (* if i < minimal size and value = null, add i to must_nulls_set and may_nulls_set *) else if Z.lt i min_size && Val.is_null v then - (MustSet.add i must_nulls_set, MaySet.add i may_nulls_set, size) + Nulls.add Definitely i nulls (* if minimal size <= i < maximal size and value = null, add i only to may_nulls_set *) else if Z.lt i max_size && Val.is_null v then - (must_nulls_set, MaySet.add i may_nulls_set, size) + Nulls.add Possibly i nulls (* if i < maximal size and value unknown, remove i from must_nulls_set and add it to may_nulls_set *) else if Z.lt i max_size then - (MustSet.remove i must_nulls_set min_size, MaySet.add i may_nulls_set, size) - (* if i >= maximal size, return tuple unmodified *) + let removed = Nulls.remove Possibly i nulls min_size in + Nulls.add Possibly i removed else - (must_nulls_set, may_nulls_set, size) in + nulls + in let set_interval_must min_i max_i = (* if value = null, return must_nulls_set unmodified as not clear which index is set to null *) @@ -1142,44 +1144,47 @@ struct (* warn if index is (potentially) out of bounds *) array_oob_check (module Idx) (must_nulls_set, size) (e, i); - match max_i with + let nulls = match max_i with (* if no maximum number in index interval *) | None -> (* ..., value = null *) (if Val.is_null v && idx_maximal size = None then match idx_maximal size with (* ... and there is no maximal size, modify may_nulls_set to top *) - | None -> uf @@ (Nulls.forget_may nulls, size) + | None -> Nulls.forget_may nulls (* ... and there is a maximal size, add all i from minimal index to maximal size to may_nulls_set *) - | Some max_size -> uf @@ (Nulls.add_may_interval (min_i, Z.pred max_size) nulls, size) + | Some max_size -> Nulls.add_may_interval (min_i, Z.pred max_size) nulls (* ... and value <> null, only keep indexes < minimal index in must_nulls_set *) else if Val.is_not_null v then - uf @@ (Nulls.filter_musts (Z.gt min_i) min_size nulls, size) + Nulls.filter_musts (Z.gt min_i) min_size nulls (*..., value unknown *) else match Idx.minimal size, idx_maximal size with (* ... and size unknown, modify both sets to top *) - | None, None -> uf @@ (Nulls.top (), size) + | None, None -> Nulls.top () (* ... and only minimal size known, remove all indexes < minimal size from must_nulls_set and modify may_nulls_set to top *) | Some min_size, None -> let nulls = Nulls.forget_may nulls in - uf @@ (Nulls.filter_musts (Z.gt min_size) min_size nulls, size) + Nulls.filter_musts (Z.gt min_size) min_size nulls (* ... and only maximal size known, modify must_nulls_set to top and add all i from minimal index to maximal size to may_nulls_set *) | None, Some max_size -> let nulls = Nulls.forget_must nulls in - uf @@ (Nulls.add_may_interval (min_i, Z.pred max_size) nulls, size) + Nulls.add_may_interval (min_i, Z.pred max_size) nulls (* ... and size is known, remove all indexes < minimal size from must_nulls_set and add all i from minimal index to maximal size to may_nulls_set *) | Some min_size, Some max_size -> let nulls = Nulls.filter_musts (Z.gt min_size) min_size nulls in - uf @@ (Nulls.add_may_interval (min_i, Z.pred max_size) nulls, size) + Nulls.add_may_interval (min_i, Z.pred max_size) nulls ) | Some max_i when Z.geq max_i Z.zero -> if Z.equal min_i max_i then - set_exact min_i + set_exact_nulls min_i else - (set_interval_must min_i max_i, set_interval_may min_i max_i, size) + (set_interval_must min_i max_i, set_interval_may min_i max_i) (* if maximum number in interval is invalid, i.e. negative, return tuple unmodified *) - | _ -> x + | _ -> nulls + in + uf @@ (nulls, size) + let make ?(varAttr=[]) ?(typAttr=[]) i v = let min_i, max_i = match Idx.minimal i, idx_maximal i with diff --git a/src/cdomains/nullByteSet.ml b/src/cdomains/nullByteSet.ml index ea8f963ab0..a21a4cb066 100644 --- a/src/cdomains/nullByteSet.ml +++ b/src/cdomains/nullByteSet.ml @@ -88,6 +88,16 @@ module MustMaySet = struct | Definitely -> MustSet.interval_mem (l,u) musts | Possibly -> failwith "not implemented" + let remove mode i (musts, mays) min_size = + match mode with + | Definitely -> (MustSet.remove i musts min_size, MaySet.remove i mays min_size) + | Possibly -> (MustSet.remove i musts min_size, mays) + + let add mode i (musts, mays) = + match mode with + | Definitely -> (MustSet.add i musts, MaySet.add i mays) + | Possibly -> (musts, MaySet.add i mays) + let add_may_interval (l,u) (musts, mays) = let rec add_indexes i max set = if Z.gt i max then From f8ee3d2738c2c0d4f407e832f14d2e2d6b12f81f Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sat, 25 Nov 2023 13:31:03 +0100 Subject: [PATCH 1117/1312] simplify --- src/cdomains/arrayDomain.ml | 19 ++++++++++++++++++- src/cdomains/nullByteSet.ml | 12 ++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index cfcc702bb4..d462aca666 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1142,6 +1142,23 @@ struct else add_indexes min_i max_i may_nulls_set in + let set_interval min_i max_i = + if Val.is_null v then + match idx_maximal size with + (* ... and size has no upper limit, add all indexes of interval to may_nulls_set *) + | None -> Nulls.add_interval Possibly (min_i, max_i) nulls + | Some max_size -> + (* ... add all indexes < maximal size to may_nulls_set *) + if Z.equal min_i Z.zero && Z.geq max_i max_size then + (must_nulls_set, MaySet.top ()) + else if Z.geq max_i max_size then + (must_nulls_set, add_indexes min_i (Z.pred max_size) may_nulls_set) + else + Nulls.add_interval Possibly (min_i, max_i) nulls + else + (set_interval_must min_i max_i, set_interval_may min_i max_i) + in + (* warn if index is (potentially) out of bounds *) array_oob_check (module Idx) (must_nulls_set, size) (e, i); let nulls = match max_i with @@ -1179,7 +1196,7 @@ struct if Z.equal min_i max_i then set_exact_nulls min_i else - (set_interval_must min_i max_i, set_interval_may min_i max_i) + set_interval min_i max_i (* if maximum number in interval is invalid, i.e. negative, return tuple unmodified *) | _ -> nulls in diff --git a/src/cdomains/nullByteSet.ml b/src/cdomains/nullByteSet.ml index a21a4cb066..cdeb481b07 100644 --- a/src/cdomains/nullByteSet.ml +++ b/src/cdomains/nullByteSet.ml @@ -98,6 +98,18 @@ module MustMaySet = struct | Definitely -> (MustSet.add i musts, MaySet.add i mays) | Possibly -> (musts, MaySet.add i mays) + let add_interval mode (l,u) (musts, mays) = + match mode with + | Definitely -> failwith "todo" + | Possibly -> + let rec add_indexes i max set = + if Z.gt i max then + set + else + add_indexes (Z.succ i) max (MaySet.add i set) + in + (musts, add_indexes l u mays) + let add_may_interval (l,u) (musts, mays) = let rec add_indexes i max set = if Z.gt i max then From 81c8b63d5698f9270db0b778831a4347be78a864 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sat, 25 Nov 2023 18:02:45 +0100 Subject: [PATCH 1118/1312] Cleanup --- src/cdomains/arrayDomain.ml | 10 +++++----- src/cdomains/nullByteSet.ml | 12 ++++-------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index d462aca666..4a0a9acb8d 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1150,9 +1150,9 @@ struct | Some max_size -> (* ... add all indexes < maximal size to may_nulls_set *) if Z.equal min_i Z.zero && Z.geq max_i max_size then - (must_nulls_set, MaySet.top ()) + Nulls.add_all Possibly nulls else if Z.geq max_i max_size then - (must_nulls_set, add_indexes min_i (Z.pred max_size) may_nulls_set) + Nulls.add_interval Possibly (min_i, Z.pred max_size) nulls else Nulls.add_interval Possibly (min_i, max_i) nulls else @@ -1170,7 +1170,7 @@ struct (* ... and there is no maximal size, modify may_nulls_set to top *) | None -> Nulls.forget_may nulls (* ... and there is a maximal size, add all i from minimal index to maximal size to may_nulls_set *) - | Some max_size -> Nulls.add_may_interval (min_i, Z.pred max_size) nulls + | Some max_size -> Nulls.add_interval Possibly (min_i, Z.pred max_size) nulls (* ... and value <> null, only keep indexes < minimal index in must_nulls_set *) else if Val.is_not_null v then Nulls.filter_musts (Z.gt min_i) min_size nulls @@ -1186,11 +1186,11 @@ struct (* ... and only maximal size known, modify must_nulls_set to top and add all i from minimal index to maximal size to may_nulls_set *) | None, Some max_size -> let nulls = Nulls.forget_must nulls in - Nulls.add_may_interval (min_i, Z.pred max_size) nulls + Nulls.add_interval Possibly (min_i, Z.pred max_size) nulls (* ... and size is known, remove all indexes < minimal size from must_nulls_set and add all i from minimal index to maximal size to may_nulls_set *) | Some min_size, Some max_size -> let nulls = Nulls.filter_musts (Z.gt min_size) min_size nulls in - Nulls.add_may_interval (min_i, Z.pred max_size) nulls + Nulls.add_interval Possibly (min_i, Z.pred max_size) nulls ) | Some max_i when Z.geq max_i Z.zero -> if Z.equal min_i max_i then diff --git a/src/cdomains/nullByteSet.ml b/src/cdomains/nullByteSet.ml index cdeb481b07..5cf6445ac6 100644 --- a/src/cdomains/nullByteSet.ml +++ b/src/cdomains/nullByteSet.ml @@ -110,14 +110,10 @@ module MustMaySet = struct in (musts, add_indexes l u mays) - let add_may_interval (l,u) (musts, mays) = - let rec add_indexes i max set = - if Z.gt i max then - set - else - add_indexes (Z.succ i) max (MaySet.add i set) - in - (musts, add_indexes l u mays) + let add_all mode (musts, mays) = + match mode with + | Definitely -> failwith "todo" + | Possibly -> (musts, MaySet.top ()) let precise_singleton i = (MustSet.singleton i, MaySet.singleton i) From 8abc9c950013da5dd2d9ab5b78732a6e40ee5786 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sat, 25 Nov 2023 18:11:49 +0100 Subject: [PATCH 1119/1312] Progress --- src/cdomains/arrayDomain.ml | 5 +++++ src/cdomains/nullByteSet.ml | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 4a0a9acb8d..fbc859f282 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1155,6 +1155,11 @@ struct Nulls.add_interval Possibly (min_i, Z.pred max_size) nulls else Nulls.add_interval Possibly (min_i, max_i) nulls + else if Val.is_not_null v then + if Z.equal min_i Z.zero && Z.geq max_i min_size then + Nulls.remove_all Possibly nulls + else + Nulls.filter_musts (fun x -> (Z.lt x min_i || Z.gt x max_i) && Z.lt x min_size) min_size nulls else (set_interval_must min_i max_i, set_interval_may min_i max_i) in diff --git a/src/cdomains/nullByteSet.ml b/src/cdomains/nullByteSet.ml index 5cf6445ac6..7a4bf7c1d7 100644 --- a/src/cdomains/nullByteSet.ml +++ b/src/cdomains/nullByteSet.ml @@ -115,6 +115,11 @@ module MustMaySet = struct | Definitely -> failwith "todo" | Possibly -> (musts, MaySet.top ()) + let remove_all mode (musts, mays) = + match mode with + | Definitely -> (MustSet.top (), mays) + | Possibly -> failwith "todo" + let precise_singleton i = (MustSet.singleton i, MaySet.singleton i) From f166671f9ab4bfd8e54d77206668db552e7c93b9 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sat, 25 Nov 2023 18:33:36 +0100 Subject: [PATCH 1120/1312] Simplify --- src/cdomains/arrayDomain.ml | 52 ++++++++++--------------------------- 1 file changed, 14 insertions(+), 38 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index fbc859f282..33817698e4 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1064,11 +1064,6 @@ struct let set (ask: VDQ.t) ((must_nulls_set, may_nulls_set, size) as x) (e, i) v = let nulls = (must_nulls_set, may_nulls_set) in - let rec add_indexes i max may_nulls_set = - if Z.gt i max then - may_nulls_set - else - add_indexes (Z.succ i) max (MaySet.add i may_nulls_set) in let min interval = match Idx.minimal interval with | Some min_num when Z.geq min_num Z.zero -> min_num | _ -> Z.zero in (* assume worst case minimal natural number *) @@ -1114,34 +1109,6 @@ struct nulls in - let set_interval_must min_i max_i = - (* if value = null, return must_nulls_set unmodified as not clear which index is set to null *) - if Val.is_null v then - must_nulls_set - (* if value <> null or unknown, only keep indexes must_i < minimal index and must_i > maximal index *) - else if Z.equal min_i Z.zero && Z.geq max_i min_size then - MustSet.top () - else - MustSet.filter (fun x -> (Z.lt x min_i || Z.gt x max_i) && Z.lt x min_size) must_nulls_set min_size in - - let set_interval_may min_i max_i = - (* if value <> null, return may_nulls_set unmodified as not clear which index is set to value *) - if Val.is_not_null v then - may_nulls_set - (* if value = null or unknown *) - else - match idx_maximal size with - (* ... and size has no upper limit, add all indexes of interval to may_nulls_set *) - | None -> add_indexes min_i max_i may_nulls_set - | Some max_size -> - (* ... add all indexes < maximal size to may_nulls_set *) - if Z.equal min_i Z.zero && Z.geq max_i max_size then - MaySet.top () - else if Z.geq max_i max_size then - add_indexes min_i (Z.pred max_size) may_nulls_set - else - add_indexes min_i max_i may_nulls_set in - let set_interval min_i max_i = if Val.is_null v then match idx_maximal size with @@ -1151,17 +1118,26 @@ struct (* ... add all indexes < maximal size to may_nulls_set *) if Z.equal min_i Z.zero && Z.geq max_i max_size then Nulls.add_all Possibly nulls - else if Z.geq max_i max_size then - Nulls.add_interval Possibly (min_i, Z.pred max_size) nulls - else - Nulls.add_interval Possibly (min_i, max_i) nulls + else + Nulls.add_interval Possibly (min_i, Z.min (Z.pred max_size) max_i) nulls else if Val.is_not_null v then if Z.equal min_i Z.zero && Z.geq max_i min_size then Nulls.remove_all Possibly nulls else Nulls.filter_musts (fun x -> (Z.lt x min_i || Z.gt x max_i) && Z.lt x min_size) min_size nulls else - (set_interval_must min_i max_i, set_interval_may min_i max_i) + let nulls = match idx_maximal size with + (* ... and size has no upper limit, add all indexes of interval to may_nulls_set *) + | None -> Nulls.add_interval Possibly (min_i,max_i) nulls + | Some max_size when Z.equal min_i Z.zero && Z.geq max_i max_size -> + (* ... add all indexes < maximal size to may_nulls_set *) + Nulls.add_all Possibly nulls + | Some max_size -> Nulls.add_interval Possibly (min_i, Z.min (Z.pred max_size) max_i) nulls + in + if Z.equal min_i Z.zero && Z.geq max_i min_size then + Nulls.remove_all Possibly nulls + else + Nulls.filter_musts (fun x -> (Z.lt x min_i || Z.gt x max_i) && Z.lt x min_size) min_size nulls in (* warn if index is (potentially) out of bounds *) From 404e505cb28237f4d6701fcfb28a4128740cd486 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sat, 25 Nov 2023 18:50:22 +0100 Subject: [PATCH 1121/1312] Simplify --- src/cdomains/arrayDomain.ml | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 33817698e4..52e3c8eb49 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1064,9 +1064,7 @@ struct let set (ask: VDQ.t) ((must_nulls_set, may_nulls_set, size) as x) (e, i) v = let nulls = (must_nulls_set, may_nulls_set) in - let min interval = match Idx.minimal interval with - | Some min_num when Z.geq min_num Z.zero -> min_num - | _ -> Z.zero in (* assume worst case minimal natural number *) + let min interval = Z.max Z.zero (BatOption.default Z.zero (Idx.minimal interval)) in let min_size = min size in let min_i = min i in @@ -1207,17 +1205,21 @@ struct Z.zero, None) else min_i, None - | None, None -> Z.zero, None in - match max_i, Val.is_null v, Val.is_not_null v with - (* if value = null, return (bot = all indexes up to minimal size - 1, top = all indexes up to maximal size - 1, size) *) - | Some max_i, true, _ -> (MustSet.bot (), MaySet.top (), Idx.of_interval ILong (min_i, max_i)) - | None, true, _ -> (MustSet.bot (), MaySet.top (), Idx.starting ILong min_i) - (* if value <> null, return (top = no indexes, bot = no indexes, size) *) - | Some max_i, false, true -> (MustSet.top (), MaySet.bot (), Idx.of_interval ILong (min_i, max_i)) - | None, false, true -> (MustSet.top (), MaySet.bot (), Idx.starting ILong min_i) - (* if value unknown, return (top = no indexes, top = all indexes up to maximal size - 1, size) *) - | Some max_i, false, false -> (MustSet.top (), MaySet.top (), Idx.of_interval ILong (min_i, max_i)) - | None, false, false -> (MustSet.top (), MaySet.top (), Idx.starting ILong min_i) + | None, None -> Z.zero, None + in + let size = match max_i with + | Some max_i -> Idx.of_interval ILong (min_i, max_i) + | None -> Idx.starting ILong min_i + in + let nulls = + if Val.is_null v then + Nulls.make_all_must () + else if Val.is_not_null v then + Nulls.make_none_may () + else + Nulls.top () + in + uf @@ (nulls, size) let length (_, _, size) = Some size From 97c6c08fb8827a46e72a12ea3fcbe70cdf98d91b Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sat, 25 Nov 2023 18:57:48 +0100 Subject: [PATCH 1122/1312] Simplify --- src/cdomains/nullByteSet.ml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/cdomains/nullByteSet.ml b/src/cdomains/nullByteSet.ml index 7a4bf7c1d7..93e542c01f 100644 --- a/src/cdomains/nullByteSet.ml +++ b/src/cdomains/nullByteSet.ml @@ -123,6 +123,9 @@ module MustMaySet = struct let precise_singleton i = (MustSet.singleton i, MaySet.singleton i) + let make_all_must () = (MustSet.bot (), MaySet.top ()) + let make_none_may () = (MustSet.top (), MaySet.bot ()) + let may_exist f (musts, mays) = MaySet.exists f mays let forget_may (musts, mays) = (musts, MaySet.top ()) From 92d25b0b48a2653a1499d0756ee822407e26b752 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sat, 25 Nov 2023 18:58:07 +0100 Subject: [PATCH 1123/1312] Simplify --- src/cdomains/arrayDomain.ml | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 52e3c8eb49..a40cc79a20 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1223,18 +1223,19 @@ struct let length (_, _, size) = Some size - let move_if_affected ?(replace_with_const=false) _ sets_and_size _ _ = sets_and_size + let move_if_affected ?(replace_with_const=false) _ x _ _ = x let get_vars_in_e _ = [] let map f (must_nulls_set, may_nulls_set, size) = + let nulls = (must_nulls_set, may_nulls_set) in (* if f(null) = null, all values in must_nulls_set still are surely null; * assume top for may_nulls_set as checking effect of f for every possible value is unfeasbile *) if Val.is_null (f (Val.null ())) then - (must_nulls_set, MaySet.top (), size) + uf @@ (Nulls.forget_may nulls, size) (* else also return top for must_nulls_set *) else - (MustSet.top (), MaySet.top (), size) + uf @@ (Nulls.top (), size) let fold_left f acc _ = f acc (Val.top ()) @@ -1386,17 +1387,13 @@ struct else if Z.lt min_size1 max_len2 then M.warn ~category:ArrayOobMessage.past_end "The length of string src may be greater than the allocated size for dest"); let must_nulls_set_result = - let min_size2 = match Idx.minimal size2' with - | Some min_size2 -> min_size2 - | None -> Z.zero in + let min_size2 = BatOption.default Z.zero (Idx.minimal size2') in (* get must nulls from src string < minimal size of dest *) MustSet.filter (Z.gt min_size1) must_nulls_set2' min_size2 (* and keep indexes of dest >= maximal strlen of src *) |> MustSet.union (MustSet.filter (Z.leq max_len2) must_nulls_set1 min_size1) in let may_nulls_set_result = - let max_size2 = match idx_maximal size2' with - | Some max_size2 -> max_size2 - | None -> max_size1 in + let max_size2 = BatOption.default max_size1 (idx_maximal size2') in (* get may nulls from src string < maximal size of dest *) MaySet.filter (Z.gt max_size1) may_nulls_set2' max_size2 (* and keep indexes of dest >= minimal strlen of src *) @@ -1406,9 +1403,7 @@ struct (if Z.lt min_size1 max_len2 then M.warn ~category:ArrayOobMessage.past_end "The length of string src may be greater than the allocated size for dest"); let must_nulls_set_result = - let min_size2 = match Idx.minimal size2' with - | Some min_size2 -> min_size2 - | None -> Z.zero in + let min_size2 = BatOption.default Z.zero (Idx.minimal size2') in MustSet.filter (Z.gt min_size1) must_nulls_set2' min_size2 |> MustSet.union (MustSet.filter (Z.leq max_len2) must_nulls_set1 min_size1) in let may_nulls_set_result = @@ -1423,14 +1418,10 @@ struct M.warn ~category:ArrayOobMessage.past_end "The length of string src may be greater than the allocated size for dest"); (* do not keep any index of dest as no maximal strlen of src *) let must_nulls_set_result = - let min_size2 = match Idx.minimal size2' with - | Some min_size2 -> min_size2 - | None -> Z.zero in + let min_size2 = BatOption.default Z.zero (Idx.minimal size2') in MustSet.filter (Z.gt min_size1) must_nulls_set2' min_size2 in let may_nulls_set_result = - let max_size2 = match idx_maximal size2' with - | Some max_size2 -> max_size2 - | None -> max_size1 in + let max_size2 = BatOption.default max_size1 (idx_maximal size2') in MaySet.filter (Z.gt max_size1) may_nulls_set2' max_size2 |> MaySet.union (MaySet.filter (Z.leq min_len2) may_nulls_set1 max_size1) in (must_nulls_set_result, may_nulls_set_result, size1) @@ -1439,9 +1430,7 @@ struct M.warn ~category:ArrayOobMessage.past_end "The length of string src may be greater than the allocated size for dest"); (* do not keep any index of dest as no maximal strlen of src *) let must_nulls_set_result = - let min_size2 = match Idx.minimal size2' with - | Some min_size2 -> min_size2 - | None -> Z.zero in + let min_size2 = BatOption.default Z.zero (Idx.minimal size2') in MustSet.filter (Z.gt min_size1) must_nulls_set2' min_size2 in let may_nulls_set_result = (* get all may nulls from src string as no maximal size of dest *) From 8db296664ae475e726e87a7b9edfc4be638d2b94 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sat, 25 Nov 2023 19:24:32 +0100 Subject: [PATCH 1124/1312] Simplify --- src/cdomains/arrayDomain.ml | 81 ++++++++++++++++++++----------------- 1 file changed, 44 insertions(+), 37 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index a40cc79a20..82f616e3d7 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1292,63 +1292,68 @@ struct * marking the end of the string and if needed followed by further null bytes to obtain * an n bytes string. *) let to_n_string (must_nulls_set, may_nulls_set, size) n = - let nulls = (must_nulls_set, may_nulls_set) in - let rec add_indexes i max set = - if Z.geq i max then - set - else - add_indexes (Z.succ i) max (MaySet.add i set) in - let update_must_indexes min_must_null must_nulls_set = - if Z.equal min_must_null Z.zero then - MustSet.bot () - else - (* if strlen < n, every byte starting from min_must_null is surely also transformed to null *) - add_indexes min_must_null (Z.of_int n) must_nulls_set - |> MustSet.M.filter (Z.gt (Z.of_int n)) in - let update_may_indexes min_may_null may_nulls_set = - if Z.equal min_may_null Z.zero then - MaySet.top () - else - (* if minimal strlen < n, every byte starting from minimal may null index may be transformed to null *) - add_indexes min_may_null (Z.of_int n) may_nulls_set - |> MaySet.M.filter (Z.gt (Z.of_int n)) in - let warn_no_null min_must_null exists_min_must_null min_may_null = - if Z.geq min_may_null (Z.of_int n) then - M.warn "Resulting string might not be null-terminated because src doesn't contain a null byte in the first n bytes" - else if (exists_min_must_null && (Z.geq min_must_null (Z.of_int n)) || (Z.gt min_must_null min_may_null)) || not exists_min_must_null then - M.warn "Resulting string might not be null-terminated because src might not contain a null byte in the first n bytes" in - if n < 0 then - (MustSet.top (), MaySet.top (), Idx.top_of ILong) + uf @@ (Nulls.top (), Idx.top_of ILong) else + let n = Z.of_int n in + let nulls = (must_nulls_set, may_nulls_set) in + let rec add_indexes i max set = + if Z.geq i max then + set + else + add_indexes (Z.succ i) max (MaySet.add i set) in + let update_must_indexes min_must_null must_nulls_set = + if Z.equal min_must_null Z.zero then + MustSet.bot () + else + (* if strlen < n, every byte starting from min_must_null is surely also transformed to null *) + add_indexes min_must_null n must_nulls_set + |> MustSet.M.filter (Z.gt n) in + let update_may_indexes min_may_null may_nulls_set = + if Z.equal min_may_null Z.zero then + MaySet.top () + else + (* if minimal strlen < n, every byte starting from minimal may null index may be transformed to null *) + add_indexes min_may_null n may_nulls_set + |> MaySet.M.filter (Z.gt n) in + let warn_no_null min_must_null exists_min_must_null min_may_null = + if Z.geq min_may_null n then + M.warn "Resulting string might not be null-terminated because src doesn't contain a null byte in the first n bytes" + else if (exists_min_must_null && (Z.geq min_must_null n) || (Z.gt min_must_null min_may_null)) || not exists_min_must_null then + M.warn "Resulting string might not be null-terminated because src might not contain a null byte in the first n bytes" + in ((match Idx.minimal size, idx_maximal size with | Some min_size, Some max_size -> - if Z.gt (Z.of_int n) max_size then + if Z.gt n max_size then M.warn ~category:ArrayOobMessage.past_end "Array size is smaller than n bytes; can cause a buffer overflow" - else if Z.gt (Z.of_int n) min_size then + else if Z.gt n min_size then M.warn ~category:ArrayOobMessage.past_end "Array size might be smaller than n bytes; can cause a buffer overflow" | Some min_size, None -> - if Z.gt (Z.of_int n) min_size then + if Z.gt n min_size then M.warn ~category:ArrayOobMessage.past_end "Array size might be smaller than n bytes; can cause a buffer overflow" | None, Some max_size -> - if Z.gt (Z.of_int n) max_size then + if Z.gt n max_size then M.warn ~category:ArrayOobMessage.past_end "Array size is smaller than n bytes; can cause a buffer overflow" | None, None -> ()); - + let nulls = (* if definitely no null byte in array, i.e. must_nulls_set = may_nulls_set = empty set *) if Nulls.is_empty Definitely nulls then (M.warn ~category:ArrayOobMessage.past_end "Resulting string might not be null-terminated because src doesn't contain a null byte"; match idx_maximal size with (* ... there *may* be null bytes from maximal size to n - 1 if maximal size < n (i.e. past end) *) - | Some max_size when Z.geq max_size Z.zero -> (must_nulls_set, add_indexes max_size (Z.of_int n) may_nulls_set, Idx.of_int ILong (Z.of_int n)) - | _ -> (must_nulls_set, may_nulls_set, Idx.of_int ILong (Z.of_int n))) + | Some max_size when Z.geq max_size Z.zero -> Nulls.add_interval Possibly (max_size, Z.pred n) nulls + | _ -> nulls) (* if only must_nulls_set empty, remove indexes >= n from may_nulls_set and add all indexes from minimal may null index to n - 1; * warn as in any case, resulting array not guaranteed to contain null byte *) else if Nulls.is_empty Possibly nulls then let min_may_null = Nulls.min_elem Possibly nulls in warn_no_null Z.zero false min_may_null; - (must_nulls_set, update_may_indexes min_may_null may_nulls_set, Idx.of_int ILong (Z.of_int n)) + if Z.equal min_may_null Z.zero then + Nulls.forget_may nulls + else + let (must, mays) = Nulls.add_interval Possibly (min_may_null, Z.pred n) nulls in + (must, mays |> MaySet.M.filter (Z.gt n)) (* TODO: this makes little sense *) else let min_must_null = Nulls.min_elem Definitely nulls in let min_may_null = Nulls.min_elem Possibly nulls in @@ -1356,9 +1361,11 @@ struct warn_no_null min_must_null true min_may_null; (* if min_must_null = min_may_null, remove indexes >= n and add all indexes from minimal must/may null to n - 1 in the sets *) if Z.equal min_must_null min_may_null then - (update_must_indexes min_must_null must_nulls_set, update_may_indexes min_may_null may_nulls_set, Idx.of_int ILong (Z.of_int n)) + (update_must_indexes min_must_null must_nulls_set, update_may_indexes min_may_null may_nulls_set) else - (MustSet.top (), update_may_indexes min_may_null may_nulls_set, Idx.of_int ILong (Z.of_int n))) + (MustSet.top (), update_may_indexes min_may_null may_nulls_set) + in + uf @@ (nulls, Idx.of_int ILong n)) let to_string_length (must_nulls_set, may_nulls_set, size) = let nulls = (must_nulls_set, may_nulls_set) in From 8318ad8e1ff2d613a6aa259bacc2894743314d32 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sat, 25 Nov 2023 20:21:37 +0100 Subject: [PATCH 1125/1312] Simplify --- src/cdomains/arrayDomain.ml | 34 ++++++++-------------------------- src/cdomains/nullByteSet.ml | 29 +++++++++++++++++++++-------- 2 files changed, 29 insertions(+), 34 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 82f616e3d7..fed8be60b6 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1108,34 +1108,16 @@ struct in let set_interval min_i max_i = - if Val.is_null v then - match idx_maximal size with - (* ... and size has no upper limit, add all indexes of interval to may_nulls_set *) - | None -> Nulls.add_interval Possibly (min_i, max_i) nulls - | Some max_size -> - (* ... add all indexes < maximal size to may_nulls_set *) - if Z.equal min_i Z.zero && Z.geq max_i max_size then - Nulls.add_all Possibly nulls - else - Nulls.add_interval Possibly (min_i, Z.min (Z.pred max_size) max_i) nulls - else if Val.is_not_null v then - if Z.equal min_i Z.zero && Z.geq max_i min_size then - Nulls.remove_all Possibly nulls - else - Nulls.filter_musts (fun x -> (Z.lt x min_i || Z.gt x max_i) && Z.lt x min_size) min_size nulls + (* Update max_i so it is capped at the maximum size *) + let max_i = BatOption.map_default (fun x -> Z.min max_i @@ Z.pred x) max_i (idx_maximal size) in + if Val.is_not_null v then + Nulls.remove_interval Possibly (min_i, max_i) min_size nulls else - let nulls = match idx_maximal size with - (* ... and size has no upper limit, add all indexes of interval to may_nulls_set *) - | None -> Nulls.add_interval Possibly (min_i,max_i) nulls - | Some max_size when Z.equal min_i Z.zero && Z.geq max_i max_size -> - (* ... add all indexes < maximal size to may_nulls_set *) - Nulls.add_all Possibly nulls - | Some max_size -> Nulls.add_interval Possibly (min_i, Z.min (Z.pred max_size) max_i) nulls - in - if Z.equal min_i Z.zero && Z.geq max_i min_size then - Nulls.remove_all Possibly nulls + let nulls = Nulls.add_interval ~maxfull:(idx_maximal size) Possibly (min_i, max_i) nulls in + if Val.is_null v then + nulls else - Nulls.filter_musts (fun x -> (Z.lt x min_i || Z.gt x max_i) && Z.lt x min_size) min_size nulls + Nulls.remove_interval Possibly (min_i, max_i) min_size nulls in (* warn if index is (potentially) out of bounds *) diff --git a/src/cdomains/nullByteSet.ml b/src/cdomains/nullByteSet.ml index 93e542c01f..349526d092 100644 --- a/src/cdomains/nullByteSet.ml +++ b/src/cdomains/nullByteSet.ml @@ -98,17 +98,30 @@ module MustMaySet = struct | Definitely -> (MustSet.add i musts, MaySet.add i mays) | Possibly -> (musts, MaySet.add i mays) - let add_interval mode (l,u) (musts, mays) = + let add_interval ?maxfull mode (l,u) (musts, mays) = match mode with | Definitely -> failwith "todo" | Possibly -> - let rec add_indexes i max set = - if Z.gt i max then - set + match maxfull with + | Some Some maxfull when Z.equal l Z.zero && Z.geq u maxfull -> + (musts, MaySet.top ()) + | _ -> + let rec add_indexes i max set = + if Z.gt i max then + set + else + add_indexes (Z.succ i) max (MaySet.add i set) + in + (musts, add_indexes l u mays) + + let remove_interval mode (l,u) min_size (musts, mays) = + match mode with + | Definitely -> failwith "todo" + | Possibly -> + if Z.equal l Z.zero && Z.geq u min_size then + (MustSet.top (), mays) else - add_indexes (Z.succ i) max (MaySet.add i set) - in - (musts, add_indexes l u mays) + (MustSet.filter (fun x -> (Z.lt x l || Z.gt x u) && Z.lt x min_size) musts min_size, mays) let add_all mode (musts, mays) = match mode with @@ -131,4 +144,4 @@ module MustMaySet = struct let forget_may (musts, mays) = (musts, MaySet.top ()) let forget_must (musts, mays) = (MustSet.top (), mays) let filter_musts f min_size (musts, mays) = (MustSet.filter f musts min_size, mays) -end \ No newline at end of file +end From 86872a18b3faa890e06da45900dc165679dd266d Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 26 Nov 2023 11:49:11 +0100 Subject: [PATCH 1126/1312] Simplify --- src/cdomains/arrayDomain.ml | 65 +++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index fed8be60b6..d14d4ec5c8 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1010,6 +1010,18 @@ struct type value = Val.t type ret = Null | NotNull | Top + module Val = struct + include Val + + let is_null v = + if is_not_null v then + NotNull + else if is_null v then + Null + else + Top + end + type substr = IsNotSubstr | IsSubstrAtIndex0 | IsMaybeSubstr module ArrayOobMessage = M.Category.Behavior.Undefined.ArrayOutOfBounds @@ -1060,7 +1072,7 @@ struct (* if maximum number in interval is invalid, i.e. negative, return Top of value *) | _ -> Top - let uf ((a,b),c) = (a,b,c) + let uf ((a,b),c) = (a,b,c) let set (ask: VDQ.t) ((must_nulls_set, may_nulls_set, size) as x) (e, i) v = let nulls = (must_nulls_set, may_nulls_set) in @@ -1074,30 +1086,26 @@ struct match idx_maximal size with (* if size has no upper limit *) | None -> - (* ... and value <> null, remove i from must_nulls_set and also from may_nulls_set if not top *) - if Val.is_not_null v && not (MaySet.is_top may_nulls_set) then - Nulls.remove Definitely i nulls min_size - else if Val.is_not_null v then - Nulls.remove Possibly i nulls min_size - (* ..., i < minimal size and value = null, add i to must_nulls_set and may_nulls_set *) - else if Z.lt i min_size && Val.is_null v then - Nulls.add Definitely i nulls - (* ..., i >= minimal size and value = null, add i only to may_nulls_set *) - else if Val.is_null v then - Nulls.add Possibly i nulls - (* ... and value unknown, remove i from must_nulls_set and add it to may_nulls_set *) - else + (match Val.is_null v with + | NotNull -> + Nulls.remove (if MaySet.is_top may_nulls_set then Possibly else Definitely) i nulls min_size + (* ... and value <> null, remove i from must_nulls_set and also from may_nulls_set if not top *) + | Null -> + Nulls.add (if Z.lt i min_size then Definitely else Possibly) i nulls + (* i < minimal size and value = null, add i to must_nulls_set and may_nulls_set *) + (* i >= minimal size and value = null, add i only to may_nulls_set *) + | Top -> let removed = Nulls.remove Possibly i nulls min_size in - Nulls.add Possibly i removed + Nulls.add Possibly i removed) | Some max_size -> (* if value <> null, remove i from must_nulls_set and may_nulls_set *) if Val.is_not_null v then Nulls.remove Definitely i nulls min_size (* if i < minimal size and value = null, add i to must_nulls_set and may_nulls_set *) - else if Z.lt i min_size && Val.is_null v then + else if Z.lt i min_size && Val.is_null v = Null then Nulls.add Definitely i nulls (* if minimal size <= i < maximal size and value = null, add i only to may_nulls_set *) - else if Z.lt i max_size && Val.is_null v then + else if Z.lt i max_size && Val.is_null v = Null then Nulls.add Possibly i nulls (* if i < maximal size and value unknown, remove i from must_nulls_set and add it to may_nulls_set *) else if Z.lt i max_size then @@ -1114,7 +1122,7 @@ struct Nulls.remove_interval Possibly (min_i, max_i) min_size nulls else let nulls = Nulls.add_interval ~maxfull:(idx_maximal size) Possibly (min_i, max_i) nulls in - if Val.is_null v then + if Val.is_null v = Null then nulls else Nulls.remove_interval Possibly (min_i, max_i) min_size nulls @@ -1126,7 +1134,7 @@ struct (* if no maximum number in index interval *) | None -> (* ..., value = null *) - (if Val.is_null v && idx_maximal size = None then + (if Val.is_null v = Null && idx_maximal size = None then match idx_maximal size with (* ... and there is no maximal size, modify may_nulls_set to top *) | None -> Nulls.forget_may nulls @@ -1193,13 +1201,10 @@ struct | Some max_i -> Idx.of_interval ILong (min_i, max_i) | None -> Idx.starting ILong min_i in - let nulls = - if Val.is_null v then - Nulls.make_all_must () - else if Val.is_not_null v then - Nulls.make_none_may () - else - Nulls.top () + let nulls = match Val.is_null v with + | Null -> Nulls.make_all_must () + | NotNull -> Nulls.make_none_may () + | Top -> Nulls.top () in uf @@ (nulls, size) @@ -1213,11 +1218,9 @@ struct let nulls = (must_nulls_set, may_nulls_set) in (* if f(null) = null, all values in must_nulls_set still are surely null; * assume top for may_nulls_set as checking effect of f for every possible value is unfeasbile *) - if Val.is_null (f (Val.null ())) then - uf @@ (Nulls.forget_may nulls, size) - (* else also return top for must_nulls_set *) - else - uf @@ (Nulls.top (), size) + match Val.is_null (f (Val.null ())) with + | Null -> uf @@ (Nulls.forget_may nulls, size) + | _ -> uf @@ (Nulls.top (), size) (* else also return top for must_nulls_set *) let fold_left f acc _ = f acc (Val.top ()) From 55d9a531a6a6a1566fa82b98b209a34f929647bf Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 26 Nov 2023 12:02:13 +0100 Subject: [PATCH 1127/1312] Simplify --- src/cdomains/arrayDomain.ml | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index d14d4ec5c8..bde4934994 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1098,21 +1098,19 @@ struct let removed = Nulls.remove Possibly i nulls min_size in Nulls.add Possibly i removed) | Some max_size -> - (* if value <> null, remove i from must_nulls_set and may_nulls_set *) - if Val.is_not_null v then - Nulls.remove Definitely i nulls min_size - (* if i < minimal size and value = null, add i to must_nulls_set and may_nulls_set *) - else if Z.lt i min_size && Val.is_null v = Null then - Nulls.add Definitely i nulls - (* if minimal size <= i < maximal size and value = null, add i only to may_nulls_set *) - else if Z.lt i max_size && Val.is_null v = Null then - Nulls.add Possibly i nulls - (* if i < maximal size and value unknown, remove i from must_nulls_set and add it to may_nulls_set *) - else if Z.lt i max_size then - let removed = Nulls.remove Possibly i nulls min_size in - Nulls.add Possibly i removed - else - nulls + (match Val.is_null v with + | NotNull -> + Nulls.remove Definitely i nulls min_size + (* if value <> null, remove i from must_nulls_set and may_nulls_set *) + | Null when Z.lt i min_size -> + Nulls.add Definitely i nulls + | Null when Z.lt i max_size -> + Nulls.add Possibly i nulls + | NotNull when Z.lt i max_size -> + let removed = Nulls.remove Possibly i nulls min_size in + Nulls.add Possibly i removed + | _ -> nulls + ) in let set_interval min_i max_i = From b4d8bdb9c0204583b18d83ba57a1c32c28d0184d Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 26 Nov 2023 12:04:14 +0100 Subject: [PATCH 1128/1312] simplify --- src/cdomains/arrayDomain.ml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index bde4934994..a3823dfcbb 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1116,14 +1116,12 @@ struct let set_interval min_i max_i = (* Update max_i so it is capped at the maximum size *) let max_i = BatOption.map_default (fun x -> Z.min max_i @@ Z.pred x) max_i (idx_maximal size) in - if Val.is_not_null v then - Nulls.remove_interval Possibly (min_i, max_i) min_size nulls - else + match Val.is_null v with + | NotNull -> Nulls.remove_interval Possibly (min_i, max_i) min_size nulls + | Null -> Nulls.add_interval ~maxfull:(idx_maximal size) Possibly (min_i, max_i) nulls + | Top -> let nulls = Nulls.add_interval ~maxfull:(idx_maximal size) Possibly (min_i, max_i) nulls in - if Val.is_null v = Null then - nulls - else - Nulls.remove_interval Possibly (min_i, max_i) min_size nulls + Nulls.remove_interval Possibly (min_i, max_i) min_size nulls in (* warn if index is (potentially) out of bounds *) From 998feb8c04faf2e667c8b7bb42a2488bfe97cd49 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 26 Nov 2023 12:32:19 +0100 Subject: [PATCH 1129/1312] Simplify --- src/cdomains/arrayDomain.ml | 97 +++++++++++++++++++------------------ src/cdomains/nullByteSet.ml | 10 ++++ 2 files changed, 59 insertions(+), 48 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index a3823dfcbb..e42b062818 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1003,7 +1003,7 @@ struct module Nulls = NullByteSet.MustMaySet (* (Must Null Set, May Null Set, Array Size) *) - include Lattice.Prod3 (MustSet) (MaySet) (Idx) + include Lattice.Prod (Nulls) (Idx) let name () = "arrays containing null bytes" type idx = Idx.t @@ -1031,8 +1031,7 @@ struct | Some i when Z.fits_int i -> Some i | _ -> None - let get (ask: VDQ.t) (must_nulls_set, may_nulls_set, size) (e, i) = - let nulls = (must_nulls_set, may_nulls_set) in + let get (ask: VDQ.t) (nulls, size) (e, i) = let min interval = match Idx.minimal interval with | Some min_num when Z.geq min_num Z.zero -> min_num | _ -> Z.zero in (* assume worst case minimal natural number *) @@ -1072,10 +1071,9 @@ struct (* if maximum number in interval is invalid, i.e. negative, return Top of value *) | _ -> Top - let uf ((a,b),c) = (a,b,c) + let uf (a,c) = (a,c) - let set (ask: VDQ.t) ((must_nulls_set, may_nulls_set, size) as x) (e, i) v = - let nulls = (must_nulls_set, may_nulls_set) in + let set (ask: VDQ.t) ((nulls, size) as x) (e, i) v = let min interval = Z.max Z.zero (BatOption.default Z.zero (Idx.minimal interval)) in let min_size = min size in @@ -1088,7 +1086,7 @@ struct | None -> (match Val.is_null v with | NotNull -> - Nulls.remove (if MaySet.is_top may_nulls_set then Possibly else Definitely) i nulls min_size + Nulls.remove (if Nulls.is_full_set Possibly nulls then Possibly else Definitely) i nulls min_size (* ... and value <> null, remove i from must_nulls_set and also from may_nulls_set if not top *) | Null -> Nulls.add (if Z.lt i min_size then Definitely else Possibly) i nulls @@ -1106,7 +1104,7 @@ struct Nulls.add Definitely i nulls | Null when Z.lt i max_size -> Nulls.add Possibly i nulls - | NotNull when Z.lt i max_size -> + | Top when Z.lt i max_size -> let removed = Nulls.remove Possibly i nulls min_size in Nulls.add Possibly i removed | _ -> nulls @@ -1125,7 +1123,7 @@ struct in (* warn if index is (potentially) out of bounds *) - array_oob_check (module Idx) (must_nulls_set, size) (e, i); + array_oob_check (module Idx) (Nulls.get_set Possibly, size) (e, i); let nulls = match max_i with (* if no maximum number in index interval *) | None -> @@ -1204,14 +1202,13 @@ struct in uf @@ (nulls, size) - let length (_, _, size) = Some size + let length (_, size) = Some size let move_if_affected ?(replace_with_const=false) _ x _ _ = x let get_vars_in_e _ = [] - let map f (must_nulls_set, may_nulls_set, size) = - let nulls = (must_nulls_set, may_nulls_set) in + let map f (nulls, size) = (* if f(null) = null, all values in must_nulls_set still are surely null; * assume top for may_nulls_set as checking effect of f for every possible value is unfeasbile *) match Val.is_null (f (Val.null ())) with @@ -1236,11 +1233,10 @@ struct | Some i -> build_set (i + 1) (MaySet.add (Z.of_int i) set) | None -> MaySet.add last_null set in let set = build_set 0 (MaySet.empty ()) in - (set, set, Idx.of_int ILong (Z.succ last_null)) + ((set, set), Idx.of_int ILong (Z.succ last_null)) (** Returns an abstract value with at most one null byte marking the end of the string *) - let to_string ((must_nulls_set, may_nulls_set, size) as x) = - let nulls = (must_nulls_set, may_nulls_set) in + let to_string ((nulls, size) as x:t):t = (* if must_nulls_set and min_nulls_set empty, definitely no null byte in array => warn about certain buffer overflow and return tuple unchanged *) if Nulls.is_empty Definitely nulls then (M.error ~category:ArrayOobMessage.past_end "Array access past end: buffer overflow"; x) @@ -1252,27 +1248,28 @@ struct let min_may_null = Nulls.min_elem Possibly nulls in (* if smallest index in sets coincides, only this null byte is kept in both sets *) if Z.equal min_must_null min_may_null then - let (must,may) = Nulls.precise_singleton min_must_null in - (must, may, Idx.of_int ILong (Z.succ min_must_null)) + let nulls = Nulls.precise_singleton min_must_null in + (nulls, Idx.of_int ILong (Z.succ min_must_null)) (* else return empty must_nulls_set and keep every index up to smallest index of must_nulls_set included in may_nulls_set *) else match idx_maximal size with - | Some max_size -> (MustSet.empty (), MaySet.filter (Z.geq min_must_null) may_nulls_set max_size, Idx.of_int ILong (Z.succ min_must_null)) + | Some max_size -> ((MustSet.empty (), MaySet.filter (Z.geq min_must_null) (Nulls.get_set Possibly nulls) max_size), Idx.of_int ILong (Z.succ min_must_null)) | None -> - if MaySet.is_top may_nulls_set then + if MaySet.is_top (Nulls.get_set Possibly nulls) then let rec add_indexes acc i = if Z.gt i min_must_null then acc else add_indexes (MaySet.add i acc) (Z.succ i) in - (MustSet.empty (), add_indexes (MaySet.empty ()) Z.zero, Idx.of_int ILong (Z.succ min_must_null)) + ((MustSet.empty (), add_indexes (MaySet.empty ()) Z.zero), Idx.of_int ILong (Z.succ min_must_null)) else - (MustSet.empty (), MaySet.M.filter (Z.geq min_must_null) may_nulls_set, Idx.of_int ILong (Z.succ min_must_null)) + ((MustSet.empty (), MaySet.M.filter (Z.geq min_must_null) (Nulls.get_set Possibly nulls)), Idx.of_int ILong (Z.succ min_must_null)) (** [to_n_string index_set n] returns an abstract value with a potential null byte * marking the end of the string and if needed followed by further null bytes to obtain * an n bytes string. *) - let to_n_string (must_nulls_set, may_nulls_set, size) n = + let to_n_string (nulls, size) n:t = + let must_nulls_set, may_nulls_set = nulls in if n < 0 then uf @@ (Nulls.top (), Idx.top_of ILong) else @@ -1348,8 +1345,7 @@ struct in uf @@ (nulls, Idx.of_int ILong n)) - let to_string_length (must_nulls_set, may_nulls_set, size) = - let nulls = (must_nulls_set, may_nulls_set) in + let to_string_length (nulls, size) = (* if must_nulls_set and min_nulls_set empty, definitely no null byte in array => return interval [size, inf) and warn *) (* TODO: check of must set really needed? *) if Nulls.is_empty Definitely nulls then @@ -1365,7 +1361,9 @@ struct else Idx.of_interval !Cil.kindOfSizeOf (Nulls.min_elem Possibly nulls, Nulls.min_elem Definitely nulls) - let string_copy (must_nulls_set1, may_nulls_set1, size1) (must_nulls_set2, may_nulls_set2, size2) n = + let string_copy (nulls1, size1) (nulls2, size2) n = + let must_nulls_set1, may_nulls_set1 = nulls1 in + let must_nulls_set2, may_nulls_set2 = nulls2 in (* filter out indexes before strlen(src) from dest sets and after strlen(src) from src sets and build union, keep size of dest *) let update_sets must_nulls_set2' may_nulls_set2' size2' len2 = match Idx.minimal size1, idx_maximal size1, Idx.minimal len2, idx_maximal len2 with @@ -1386,7 +1384,7 @@ struct MaySet.filter (Z.gt max_size1) may_nulls_set2' max_size2 (* and keep indexes of dest >= minimal strlen of src *) |> MaySet.union (MaySet.filter (Z.leq min_len2) may_nulls_set1 max_size1) in - (must_nulls_set_result, may_nulls_set_result, size1) + ((must_nulls_set_result, may_nulls_set_result), size1) | Some min_size1, None, Some min_len2, Some max_len2 -> (if Z.lt min_size1 max_len2 then M.warn ~category:ArrayOobMessage.past_end "The length of string src may be greater than the allocated size for dest"); @@ -1398,7 +1396,7 @@ struct (* get all may nulls from src string as no maximal size of dest *) may_nulls_set2' |> MaySet.union (MaySet.filter (Z.leq min_len2) may_nulls_set1 (Z.succ min_len2)) in - (must_nulls_set_result, may_nulls_set_result, size1) + ((must_nulls_set_result, may_nulls_set_result), size1) | Some min_size1, Some max_size1, Some min_len2, None -> (if Z.lt max_size1 min_len2 then M.error ~category:ArrayOobMessage.past_end "The length of string src is greater than the allocated size for dest" @@ -1412,7 +1410,7 @@ struct let max_size2 = BatOption.default max_size1 (idx_maximal size2') in MaySet.filter (Z.gt max_size1) may_nulls_set2' max_size2 |> MaySet.union (MaySet.filter (Z.leq min_len2) may_nulls_set1 max_size1) in - (must_nulls_set_result, may_nulls_set_result, size1) + ((must_nulls_set_result, may_nulls_set_result), size1) | Some min_size1, None, Some min_len2, None -> (if Z.lt min_size1 min_len2 then M.warn ~category:ArrayOobMessage.past_end "The length of string src may be greater than the allocated size for dest"); @@ -1424,9 +1422,9 @@ struct (* get all may nulls from src string as no maximal size of dest *) may_nulls_set2' |> MaySet.union (MaySet.filter (Z.leq min_len2) may_nulls_set1 (Z.succ min_len2)) in - (must_nulls_set_result, may_nulls_set_result, size1) + ((must_nulls_set_result, may_nulls_set_result), size1) (* any other case shouldn't happen as minimal index is always >= 0 *) - | _ -> (MustSet.top (), MaySet.top (), size1) in + | _ -> ((MustSet.top (), MaySet.top ()), size1) in (* warn if size of dest is (potentially) smaller than size of src and the latter (potentially) has no null byte at index < size of dest *) let sizes_warning size2 = @@ -1456,17 +1454,19 @@ struct (* strcpy *) | None -> sizes_warning size2; - let must_nulls_set2', may_nulls_set2', size2' = to_string (must_nulls_set2, may_nulls_set2, size2) in - let strlen2 = to_string_length (must_nulls_set2, may_nulls_set2, size2) in + let (must_nulls_set2', may_nulls_set2'), size2' = to_string (nulls2, size2) in + let strlen2 = to_string_length (nulls2, size2) in update_sets must_nulls_set2' may_nulls_set2' size2' strlen2 (* strncpy = exactly n bytes from src are copied to dest *) | Some n when n >= 0 -> sizes_warning (Idx.of_int ILong (Z.of_int n)); - let must_nulls_set2', may_nulls_set2', size2' = to_n_string (must_nulls_set2, may_nulls_set2, size2) n in + let (must_nulls_set2', may_nulls_set2'), size2' = to_n_string (nulls2, size2) n in update_sets must_nulls_set2' may_nulls_set2' size2' (Idx.of_int !Cil.kindOfSizeOf (Z.of_int n)) - | _ -> (MustSet.top (), MaySet.top (), size1) + | _ -> (Nulls.top (), size1) - let string_concat (must_nulls_set1, may_nulls_set1, size1) (must_nulls_set2, may_nulls_set2, size2) n = + let string_concat (nulls1, size1) (nulls2, size2) n = + let (must_nulls_set1, may_nulls_set1) = nulls1 in + let (must_nulls_set2, may_nulls_set2) = nulls2 in let update_sets min_size1 max_size1 max_size1_exists minlen1 maxlen1 maxlen1_exists minlen2 maxlen2 maxlen2_exists must_nulls_set2' may_nulls_set2' = (* track any potential buffer overflow and issue warning if needed *) (if max_size1_exists && Z.leq max_size1 (Z.add minlen1 minlen2) then @@ -1498,7 +1498,7 @@ struct |> MaySet.union (MaySet.M.filter (Z.lt (Z.add minlen1 minlen2)) may_nulls_set1) else MaySet.top () in - (MustSet.top (), may_nulls_set_result, size1) + ((MustSet.top (), may_nulls_set_result), size1) (* if minimal must null = minimal may null in ar1 and ar2, add them together and keep indexes > strlen(dest) + strlen(src) of ar1 *) else if Z.equal (MustSet.min_elt must_nulls_set1) (MaySet.min_elt may_nulls_set1) && Z.equal (MustSet.min_elt must_nulls_set2') (MaySet.min_elt may_nulls_set2') then let min_i1 = MustSet.min_elt must_nulls_set1 in @@ -1515,7 +1515,7 @@ struct |> MaySet.M.filter (fun x -> if max_size1_exists then Z.gt max_size1 x else true) else MaySet.top () in - (must_nulls_set_result, may_nulls_set_result, size1) + ((must_nulls_set_result, may_nulls_set_result), size1) (* else only add all may nulls together <= strlen(dest) + strlen(src) *) else let min_i2 = MustSet.min_elt must_nulls_set2' in @@ -1542,11 +1542,11 @@ struct |> MaySet.union (MaySet.M.filter (Z.lt (Z.add minlen1 minlen2)) may_nulls_set1) else MaySet.top () in - (must_nulls_set_result, may_nulls_set_result, size1) in + ((must_nulls_set_result, may_nulls_set_result), size1) in let compute_concat must_nulls_set2' may_nulls_set2' = - let strlen1 = to_string_length (must_nulls_set1, may_nulls_set1, size1) in - let strlen2 = to_string_length (must_nulls_set2', may_nulls_set2', size2) in + let strlen1 = to_string_length ((must_nulls_set1, may_nulls_set1), size1) in + let strlen2 = to_string_length ((must_nulls_set2', may_nulls_set2'), size2) in match Idx.minimal size1, Idx.minimal strlen1, Idx.minimal strlen2 with | Some min_size1, Some minlen1, Some minlen2 -> begin match idx_maximal size1, idx_maximal strlen1, idx_maximal strlen2 with @@ -1567,18 +1567,18 @@ struct update_sets min_size1 Z.zero false minlen1 Z.zero false minlen2 Z.zero false must_nulls_set2' may_nulls_set2' end (* any other case shouldn't happen as minimal index is always >= 0 *) - | _ -> (MustSet.top (), MaySet.top (), size1) in + | _ -> (Nulls.top (), size1) in match n with (* strcat *) | None -> - let must_nulls_set2', may_nulls_set2', _ = to_string (must_nulls_set2, may_nulls_set2, size2) in + let (must_nulls_set2', may_nulls_set2'), _ = to_string ((must_nulls_set2, may_nulls_set2), size2) in compute_concat must_nulls_set2' may_nulls_set2' (* strncat *) | Some n when n >= 0 -> (* take at most n bytes from src; if no null byte among them, add null byte at index n *) let must_nulls_set2', may_nulls_set2' = - let must_nulls_set2, may_nulls_set2, size2 = to_string (must_nulls_set2, may_nulls_set2, size2) in + let (must_nulls_set2, may_nulls_set2), size2 = to_string ((must_nulls_set2, may_nulls_set2), size2) in if not (MaySet.exists (Z.gt (Z.of_int n)) may_nulls_set2) then (MustSet.singleton (Z.of_int n), MaySet.singleton (Z.of_int n)) else if not (MustSet.exists (Z.gt (Z.of_int n)) must_nulls_set2) then @@ -1589,10 +1589,9 @@ struct let max_size2 = BatOption.default (Z.of_int n) (idx_maximal size2) in (MustSet.filter (Z.gt (Z.of_int n)) must_nulls_set2 min_size2, MaySet.filter (Z.gt (Z.of_int n)) may_nulls_set2 max_size2) in compute_concat must_nulls_set2' may_nulls_set2' - | _ -> (MustSet.top (), MaySet.top (), size1) + | _ -> (Nulls.top (), size1) - let substring_extraction haystack ((must_needle, may_needle, size_needle) as needle) = - let nulls_needle = (must_needle, may_needle) in + let substring_extraction haystack ((nulls_needle, size_needle) as needle) = (* if needle is empty string, i.e. certain null byte at index 0, return value of strstr is pointer to haystack *) if Nulls.mem Definitely Z.zero nulls_needle then IsSubstrAtIndex0 @@ -1608,7 +1607,9 @@ struct IsMaybeSubstr | _ -> IsMaybeSubstr - let string_comparison (must_nulls_set1, may_nulls_set1, size1) (must_nulls_set2, may_nulls_set2, size2) n = + let string_comparison (nulls1, size1) (nulls2, size2) n = + let (must_nulls_set1, may_nulls_set1) = nulls1 in + let (must_nulls_set2, may_nulls_set2) = nulls2 in let compare n n_exists = (* if s1 = s2 = empty string, i.e. certain null byte at index 0, or n = 0, return 0 *) if (MustSet.mem Z.zero must_nulls_set1 && (MustSet.mem Z.zero must_nulls_set2)) @@ -1676,7 +1677,7 @@ struct compare (Z.of_int n) true | _ -> Idx.top_of IInt - let update_length new_size (must_nulls_set, may_nulls_set, size) = (must_nulls_set, may_nulls_set, new_size) + let update_length new_size (nulls, size) = (nulls, new_size) let project ?(varAttr=[]) ?(typAttr=[]) _ t = t diff --git a/src/cdomains/nullByteSet.ml b/src/cdomains/nullByteSet.ml index 349526d092..769b9cc485 100644 --- a/src/cdomains/nullByteSet.ml +++ b/src/cdomains/nullByteSet.ml @@ -133,6 +133,16 @@ module MustMaySet = struct | Definitely -> (MustSet.top (), mays) | Possibly -> failwith "todo" + let is_full_set mode (musts, mays) = + match mode with + | Definitely -> MustSet.is_bot musts + | Possibly -> MaySet.is_top mays + + let get_set mode (musts, mays) = + match mode with + | Definitely -> musts + | Possibly -> mays + let precise_singleton i = (MustSet.singleton i, MaySet.singleton i) From 54682753e1e8353d8c559ed64a68fb1d478ae016 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 26 Nov 2023 12:39:17 +0100 Subject: [PATCH 1130/1312] Simplify --- src/cdomains/arrayDomain.ml | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index e42b062818..6fceba963b 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1544,7 +1544,7 @@ struct MaySet.top () in ((must_nulls_set_result, may_nulls_set_result), size1) in - let compute_concat must_nulls_set2' may_nulls_set2' = + let compute_concat (must_nulls_set2',may_nulls_set2') = let strlen1 = to_string_length ((must_nulls_set1, may_nulls_set1), size1) in let strlen2 = to_string_length ((must_nulls_set2', may_nulls_set2'), size2) in match Idx.minimal size1, Idx.minimal strlen1, Idx.minimal strlen2 with @@ -1572,12 +1572,12 @@ struct match n with (* strcat *) | None -> - let (must_nulls_set2', may_nulls_set2'), _ = to_string ((must_nulls_set2, may_nulls_set2), size2) in - compute_concat must_nulls_set2' may_nulls_set2' + let nulls2', _ = to_string ((must_nulls_set2, may_nulls_set2), size2) in + compute_concat nulls2' (* strncat *) | Some n when n >= 0 -> (* take at most n bytes from src; if no null byte among them, add null byte at index n *) - let must_nulls_set2', may_nulls_set2' = + let nulls2' = let (must_nulls_set2, may_nulls_set2), size2 = to_string ((must_nulls_set2, may_nulls_set2), size2) in if not (MaySet.exists (Z.gt (Z.of_int n)) may_nulls_set2) then (MustSet.singleton (Z.of_int n), MaySet.singleton (Z.of_int n)) @@ -1587,8 +1587,9 @@ struct else let min_size2 = BatOption.default Z.zero (Idx.minimal size2) in let max_size2 = BatOption.default (Z.of_int n) (idx_maximal size2) in - (MustSet.filter (Z.gt (Z.of_int n)) must_nulls_set2 min_size2, MaySet.filter (Z.gt (Z.of_int n)) may_nulls_set2 max_size2) in - compute_concat must_nulls_set2' may_nulls_set2' + (MustSet.filter (Z.gt (Z.of_int n)) must_nulls_set2 min_size2, MaySet.filter (Z.gt (Z.of_int n)) may_nulls_set2 max_size2) + in + compute_concat nulls2' | _ -> (Nulls.top (), size1) let substring_extraction haystack ((nulls_needle, size_needle) as needle) = @@ -1648,12 +1649,8 @@ struct compare Z.zero false (* strncmp *) | Some n when n >= 0 -> - let min_size1 = match Idx.minimal size1 with - | Some min_size1 -> min_size1 - | None -> Z.zero in - let min_size2 = match Idx.minimal size2 with - | Some min_size2 -> min_size2 - | None -> Z.zero in + let min_size1 = BatOption.default Z.zero (Idx.minimal size1) in + let min_size2 = BatOption.default Z.zero (Idx.minimal size2) in (* issue a warning if n is (potentially) smaller than array sizes *) (match idx_maximal size1 with | Some max_size1 -> From 23b6f7401e16ed4bb07194fd46221ac66278f62e Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 26 Nov 2023 12:47:55 +0100 Subject: [PATCH 1131/1312] SImplify --- src/cdomains/arrayDomain.ml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 6fceba963b..48105bd2cc 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1613,14 +1613,13 @@ struct let (must_nulls_set2, may_nulls_set2) = nulls2 in let compare n n_exists = (* if s1 = s2 = empty string, i.e. certain null byte at index 0, or n = 0, return 0 *) - if (MustSet.mem Z.zero must_nulls_set1 && (MustSet.mem Z.zero must_nulls_set2)) - || (n_exists && Z.equal Z.zero n) then + if (Nulls.mem Definitely Z.zero nulls1 && Nulls.mem Definitely Z.zero nulls2) || (n_exists && Z.equal Z.zero n) then Idx.of_int IInt Z.zero (* if only s1 = empty string, return negative integer *) - else if MustSet.mem Z.zero must_nulls_set1 && not (MaySet.mem Z.zero may_nulls_set2) then + else if Nulls.mem Definitely Z.zero nulls1 && not (Nulls.mem Possibly Z.zero nulls2) then Idx.ending IInt Z.minus_one (* if only s2 = empty string, return positive integer *) - else if MustSet.mem Z.zero must_nulls_set2 then + else if Nulls.mem Definitely Z.zero nulls2 then Idx.starting IInt Z.one else (* if first null bytes are certain, have different indexes and are before index n if n present, return integer <> 0 *) @@ -1637,13 +1636,13 @@ struct (* strcmp *) | None -> (* track any potential buffer overflow and issue warning if needed *) - (if MustSet.is_empty must_nulls_set1 && MaySet.is_empty may_nulls_set1 then + (if Nulls.is_empty Definitely nulls1 && Nulls.is_empty Possibly nulls1 then M.error ~category:ArrayOobMessage.past_end "Array of string 1 doesn't contain a null byte: buffer overflow" - else if MustSet.is_empty must_nulls_set1 then + else if Nulls.is_empty Possibly nulls1 then M.warn ~category:ArrayOobMessage.past_end "Array of string 1 might not contain a null byte: potential buffer overflow"); - (if MustSet.is_empty must_nulls_set2 && MaySet.is_empty may_nulls_set2 then + (if Nulls.is_empty Definitely nulls2 && Nulls.is_empty Possibly nulls2 then M.error ~category:ArrayOobMessage.past_end "Array of string 2 doesn't contain a null byte: buffer overflow" - else if MustSet.is_empty must_nulls_set2 then + else if Nulls.is_empty Possibly nulls2 then M.warn ~category:ArrayOobMessage.past_end "Array of string 2 might not contain a null byte: potential buffer overflow"); (* compute abstract value for result of strcmp *) compare Z.zero false @@ -1660,7 +1659,8 @@ struct M.warn ~category:ArrayOobMessage.past_end "The size of the array of string 1 might be smaller than n bytes" | None -> if Z.gt (Z.of_int n) min_size1 then - M.warn ~category:ArrayOobMessage.past_end "The size of the array of string 1 might be smaller than n bytes"); + M.warn ~category:ArrayOobMessage.past_end "The size of the array of string 1 might be smaller than n bytes" + ); (match idx_maximal size2 with | Some max_size2 -> if Z.gt (Z.of_int n) max_size2 then From 0858696c4d03294074bfdc523ce3ce557d6639f2 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 26 Nov 2023 13:05:57 +0100 Subject: [PATCH 1132/1312] Progress --- src/cdomains/arrayDomain.ml | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 48105bd2cc..835d0d31ea 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1609,8 +1609,6 @@ struct | _ -> IsMaybeSubstr let string_comparison (nulls1, size1) (nulls2, size2) n = - let (must_nulls_set1, may_nulls_set1) = nulls1 in - let (must_nulls_set2, may_nulls_set2) = nulls2 in let compare n n_exists = (* if s1 = s2 = empty string, i.e. certain null byte at index 0, or n = 0, return 0 *) if (Nulls.mem Definitely Z.zero nulls1 && Nulls.mem Definitely Z.zero nulls2) || (n_exists && Z.equal Z.zero n) then @@ -1621,16 +1619,21 @@ struct (* if only s2 = empty string, return positive integer *) else if Nulls.mem Definitely Z.zero nulls2 then Idx.starting IInt Z.one - else - (* if first null bytes are certain, have different indexes and are before index n if n present, return integer <> 0 *) - (try if Z.equal (MustSet.min_elt must_nulls_set1) (MaySet.min_elt may_nulls_set1) - && Z.equal (MustSet.min_elt must_nulls_set2) (MaySet.min_elt may_nulls_set2) - && (not n_exists || Z.lt (MustSet.min_elt must_nulls_set1) n || Z.lt (MustSet.min_elt must_nulls_set2) n ) - && not (Z.equal (MustSet.min_elt must_nulls_set1) (MustSet.min_elt must_nulls_set2)) then - Idx.of_excl_list IInt [Z.zero] - else + else + try + let min_must1 = Nulls.min_elem Definitely nulls1 in + let min_must2 = Nulls.min_elem Definitely nulls2 in + if not (Z.equal min_must1 min_must2) + && Z.equal min_must1 (Nulls.min_elem Possibly nulls1) + && Z.equal min_must2 (Nulls.min_elem Possibly nulls2) + && (not n_exists || Z.lt min_must1 n || Z.lt min_must2 n) + then + (* if first null bytes are certain, have different indexes and are before index n if n present, return integer <> 0 *) + Idx.of_excl_list IInt [Z.zero] + else Idx.top_of IInt - with Not_found -> Idx.top_of IInt) in + with Not_found -> Idx.top_of IInt + in match n with (* strcmp *) From 74c7693715fb7b80fc12e30654d66486409a86a8 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 26 Nov 2023 13:06:51 +0100 Subject: [PATCH 1133/1312] Simplify --- src/cdomains/arrayDomain.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 835d0d31ea..1312a3eeaa 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1073,7 +1073,7 @@ struct let uf (a,c) = (a,c) - let set (ask: VDQ.t) ((nulls, size) as x) (e, i) v = + let set (ask: VDQ.t) (nulls, size) (e, i) v = let min interval = Z.max Z.zero (BatOption.default Z.zero (Idx.minimal interval)) in let min_size = min size in From cc9043194b8003ccd25891bf4f76d6f24b3a798f Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 26 Nov 2023 13:13:00 +0100 Subject: [PATCH 1134/1312] Simplify --- src/cdomains/arrayDomain.ml | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 1312a3eeaa..30771d6c23 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1071,8 +1071,6 @@ struct (* if maximum number in interval is invalid, i.e. negative, return Top of value *) | _ -> Top - let uf (a,c) = (a,c) - let set (ask: VDQ.t) (nulls, size) (e, i) v = let min interval = Z.max Z.zero (BatOption.default Z.zero (Idx.minimal interval)) in @@ -1163,7 +1161,7 @@ struct (* if maximum number in interval is invalid, i.e. negative, return tuple unmodified *) | _ -> nulls in - uf @@ (nulls, size) + (nulls, size) let make ?(varAttr=[]) ?(typAttr=[]) i v = @@ -1191,16 +1189,13 @@ struct min_i, None | None, None -> Z.zero, None in - let size = match max_i with - | Some max_i -> Idx.of_interval ILong (min_i, max_i) - | None -> Idx.starting ILong min_i - in + let size = BatOption.map_default (fun x -> Idx.of_interval ILong (min_i, x)) (Idx.starting ILong min_i) max_i in let nulls = match Val.is_null v with | Null -> Nulls.make_all_must () | NotNull -> Nulls.make_none_may () | Top -> Nulls.top () in - uf @@ (nulls, size) + (nulls, size) let length (_, size) = Some size @@ -1212,8 +1207,8 @@ struct (* if f(null) = null, all values in must_nulls_set still are surely null; * assume top for may_nulls_set as checking effect of f for every possible value is unfeasbile *) match Val.is_null (f (Val.null ())) with - | Null -> uf @@ (Nulls.forget_may nulls, size) - | _ -> uf @@ (Nulls.top (), size) (* else also return top for must_nulls_set *) + | Null -> (Nulls.forget_may nulls, size) + | _ -> (Nulls.top (), size) (* else also return top for must_nulls_set *) let fold_left f acc _ = f acc (Val.top ()) @@ -1271,10 +1266,9 @@ struct let to_n_string (nulls, size) n:t = let must_nulls_set, may_nulls_set = nulls in if n < 0 then - uf @@ (Nulls.top (), Idx.top_of ILong) + (Nulls.top (), Idx.top_of ILong) else let n = Z.of_int n in - let nulls = (must_nulls_set, may_nulls_set) in let rec add_indexes i max set = if Z.geq i max then set @@ -1300,7 +1294,7 @@ struct else if (exists_min_must_null && (Z.geq min_must_null n) || (Z.gt min_must_null min_may_null)) || not exists_min_must_null then M.warn "Resulting string might not be null-terminated because src might not contain a null byte in the first n bytes" in - ((match Idx.minimal size, idx_maximal size with + (match Idx.minimal size, idx_maximal size with | Some min_size, Some max_size -> if Z.gt n max_size then M.warn ~category:ArrayOobMessage.past_end "Array size is smaller than n bytes; can cause a buffer overflow" @@ -1343,7 +1337,7 @@ struct else (MustSet.top (), update_may_indexes min_may_null may_nulls_set) in - uf @@ (nulls, Idx.of_int ILong n)) + (nulls, Idx.of_int ILong n) let to_string_length (nulls, size) = (* if must_nulls_set and min_nulls_set empty, definitely no null byte in array => return interval [size, inf) and warn *) From cee44cd3936c673ed584a9c9cd03ad104702c363 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 26 Nov 2023 15:40:17 +0100 Subject: [PATCH 1135/1312] Simplify --- src/cdomains/arrayDomain.ml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 30771d6c23..14d077e707 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1460,7 +1460,6 @@ struct let string_concat (nulls1, size1) (nulls2, size2) n = let (must_nulls_set1, may_nulls_set1) = nulls1 in - let (must_nulls_set2, may_nulls_set2) = nulls2 in let update_sets min_size1 max_size1 max_size1_exists minlen1 maxlen1 maxlen1_exists minlen2 maxlen2 maxlen2_exists must_nulls_set2' may_nulls_set2' = (* track any potential buffer overflow and issue warning if needed *) (if max_size1_exists && Z.leq max_size1 (Z.add minlen1 minlen2) then @@ -1566,22 +1565,23 @@ struct match n with (* strcat *) | None -> - let nulls2', _ = to_string ((must_nulls_set2, may_nulls_set2), size2) in + let nulls2', _ = to_string (nulls2, size2) in compute_concat nulls2' (* strncat *) | Some n when n >= 0 -> + let n = Z.of_int n in (* take at most n bytes from src; if no null byte among them, add null byte at index n *) let nulls2' = - let (must_nulls_set2, may_nulls_set2), size2 = to_string ((must_nulls_set2, may_nulls_set2), size2) in - if not (MaySet.exists (Z.gt (Z.of_int n)) may_nulls_set2) then - (MustSet.singleton (Z.of_int n), MaySet.singleton (Z.of_int n)) - else if not (MustSet.exists (Z.gt (Z.of_int n)) must_nulls_set2) then - let max_size2 = BatOption.default (Z.succ (Z.of_int n)) (idx_maximal size2) in - (MustSet.empty (), MaySet.add (Z.of_int n) (MaySet.filter (Z.geq (Z.of_int n)) may_nulls_set2 max_size2)) + let (must_nulls_set2, may_nulls_set2), size2 = to_string (nulls2, size2) in + if not (MaySet.exists (Z.gt n) may_nulls_set2) then + (Nulls.precise_singleton n) + else if not (MustSet.exists (Z.gt n) must_nulls_set2) then + let max_size2 = BatOption.default (Z.succ n) (idx_maximal size2) in + (MustSet.empty (), MaySet.add n (MaySet.filter (Z.geq n) may_nulls_set2 max_size2)) else let min_size2 = BatOption.default Z.zero (Idx.minimal size2) in - let max_size2 = BatOption.default (Z.of_int n) (idx_maximal size2) in - (MustSet.filter (Z.gt (Z.of_int n)) must_nulls_set2 min_size2, MaySet.filter (Z.gt (Z.of_int n)) may_nulls_set2 max_size2) + let max_size2 = BatOption.default n (idx_maximal size2) in + (MustSet.filter (Z.gt n) must_nulls_set2 min_size2, MaySet.filter (Z.gt n) may_nulls_set2 max_size2) in compute_concat nulls2' | _ -> (Nulls.top (), size1) From 5951b2af2ce500ea9f575ff9f0e1c7605ce3d7f9 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 26 Nov 2023 16:18:37 +0100 Subject: [PATCH 1136/1312] Introduce alias for Z, pull up warning function --- src/cdomains/arrayDomain.ml | 169 +++++++++++++++++++----------------- src/cdomains/nullByteSet.ml | 5 +- 2 files changed, 92 insertions(+), 82 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 14d077e707..920e97982a 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1002,6 +1002,11 @@ struct module MaySet = NullByteSet.MaySet module Nulls = NullByteSet.MustMaySet + let (<.) = Z.lt + let (<=.) = Z.leq + let (>.) = Z.gt + let (>=.) = Z.geq + (* (Must Null Set, May Null Set, Array Size) *) include Lattice.Prod (Nulls) (Idx) @@ -1025,6 +1030,7 @@ struct type substr = IsNotSubstr | IsSubstrAtIndex0 | IsMaybeSubstr module ArrayOobMessage = M.Category.Behavior.Undefined.ArrayOutOfBounds + let warn_past_end = M.error ~category:ArrayOobMessage.past_end (* helper: returns Idx.maximal except for Overflows that are mapped to None *) let idx_maximal i = match Idx.maximal i with @@ -1033,7 +1039,7 @@ struct let get (ask: VDQ.t) (nulls, size) (e, i) = let min interval = match Idx.minimal interval with - | Some min_num when Z.geq min_num Z.zero -> min_num + | Some min_num when min_num >=. Z.zero -> min_num | _ -> Z.zero in (* assume worst case minimal natural number *) let min_i = min i in @@ -1044,27 +1050,27 @@ struct (* if there is no maximum value in index interval *) | None, _ -> (* ... return NotNull if no i >= min_i in may_nulls_set *) - if not (Nulls.may_exist (Z.leq min_i) nulls) then + if not (Nulls.exists Possibly ((<=.) min_i) nulls) then NotNull (* ... else return Top *) else Top (* if there is no maximum size *) - | Some max_i, None when Z.geq max_i Z.zero -> + | Some max_i, None when max_i >=. Z.zero -> (* ... and maximum value in index interval < minimal size, return Null if all numbers in index interval are in must_nulls_set *) - if Z.lt max_i min_size && Nulls.interval_mem Definitely (min_i,max_i) nulls then + if max_i <. min_size && Nulls.interval_mem Definitely (min_i,max_i) nulls then Null (* ... return NotNull if no number in index interval is in may_nulls_set *) - else if not (Nulls.may_exist (fun x -> Z.geq x min_i && Z.leq x max_i) nulls) then + else if not (Nulls.exists Possibly (fun x -> x >=. min_i && x <=. max_i) nulls) then NotNull else Top - | Some max_i, Some max_size when Z.geq max_i Z.zero -> + | Some max_i, Some max_size when max_i >=. Z.zero -> (* if maximum value in index interval < minimal size, return Null if all numbers in index interval are in must_nulls_set *) - if Z.lt max_i min_size && Nulls.interval_mem Definitely (min_i, max_i) nulls then + if max_i <. min_size && Nulls.interval_mem Definitely (min_i, max_i) nulls then Null (* if maximum value in index interval < maximal size, return NotNull if no number in index interval is in may_nulls_set *) - else if Z.lt max_i max_size && not (Nulls.may_exist (fun x -> Z.geq x min_i && Z.leq x max_i) nulls) then + else if max_i <. max_size && not (Nulls.exists Possibly (fun x -> x >=. min_i && x <=. max_i) nulls) then NotNull else Top @@ -1087,7 +1093,7 @@ struct Nulls.remove (if Nulls.is_full_set Possibly nulls then Possibly else Definitely) i nulls min_size (* ... and value <> null, remove i from must_nulls_set and also from may_nulls_set if not top *) | Null -> - Nulls.add (if Z.lt i min_size then Definitely else Possibly) i nulls + Nulls.add (if i <. min_size then Definitely else Possibly) i nulls (* i < minimal size and value = null, add i to must_nulls_set and may_nulls_set *) (* i >= minimal size and value = null, add i only to may_nulls_set *) | Top -> @@ -1098,11 +1104,11 @@ struct | NotNull -> Nulls.remove Definitely i nulls min_size (* if value <> null, remove i from must_nulls_set and may_nulls_set *) - | Null when Z.lt i min_size -> + | Null when i <. min_size -> Nulls.add Definitely i nulls - | Null when Z.lt i max_size -> + | Null when i <. max_size -> Nulls.add Possibly i nulls - | Top when Z.lt i max_size -> + | Top when i <. max_size -> let removed = Nulls.remove Possibly i nulls min_size in Nulls.add Possibly i removed | _ -> nulls @@ -1153,7 +1159,7 @@ struct let nulls = Nulls.filter_musts (Z.gt min_size) min_size nulls in Nulls.add_interval Possibly (min_i, Z.pred max_size) nulls ) - | Some max_i when Z.geq max_i Z.zero -> + | Some max_i when max_i >=. Z.zero -> if Z.equal min_i max_i then set_exact_nulls min_i else @@ -1167,22 +1173,22 @@ struct let make ?(varAttr=[]) ?(typAttr=[]) i v = let min_i, max_i = match Idx.minimal i, idx_maximal i with | Some min_i, Some max_i -> - if Z.lt min_i Z.zero && Z.lt max_i Z.zero then + if min_i <. Z.zero && max_i <. Z.zero then (M.error ~category:ArrayOobMessage.before_start "Tries to create an array of negative size"; Z.zero, Some Z.zero) - else if Z.lt min_i Z.zero then + else if min_i <. Z.zero then (M.warn ~category:ArrayOobMessage.before_start "May try to create an array of negative size"; Z.zero, Some max_i) else min_i, Some max_i | None, Some max_i -> - if Z.lt max_i Z.zero then + if max_i <. Z.zero then (M.error ~category:ArrayOobMessage.before_start "Tries to create an array of negative size"; Z.zero, Some Z.zero) else Z.zero, Some max_i | Some min_i, None -> - if Z.lt min_i Z.zero then + if min_i <. Z.zero then (M.warn ~category:ArrayOobMessage.before_start "May try to create an array of negative size"; Z.zero, None) else @@ -1221,7 +1227,7 @@ struct let to_null_byte_domain s = let last_null = Z.of_int (String.length s) in let rec build_set i set = - if Z.geq (Z.of_int i) last_null then + if (Z.of_int i) >=. last_null then MaySet.add last_null set else match String.index_from_opt s i '\x00' with @@ -1234,10 +1240,10 @@ struct let to_string ((nulls, size) as x:t):t = (* if must_nulls_set and min_nulls_set empty, definitely no null byte in array => warn about certain buffer overflow and return tuple unchanged *) if Nulls.is_empty Definitely nulls then - (M.error ~category:ArrayOobMessage.past_end "Array access past end: buffer overflow"; x) + (warn_past_end "Array access past end: buffer overflow"; x) (* if only must_nulls_set empty, no certainty about array containing null byte => warn about potential buffer overflow and return tuple unchanged *) else if Nulls.is_empty Possibly nulls then - (M.warn ~category:ArrayOobMessage.past_end "May access array past end: potential buffer overflow"; x) + (warn_past_end "May access array past end: potential buffer overflow"; x) else let min_must_null = Nulls.min_elem Definitely nulls in let min_may_null = Nulls.min_elem Possibly nulls in @@ -1252,7 +1258,7 @@ struct | None -> if MaySet.is_top (Nulls.get_set Possibly nulls) then let rec add_indexes acc i = - if Z.gt i min_must_null then + if i >. min_must_null then acc else add_indexes (MaySet.add i acc) (Z.succ i) in @@ -1291,26 +1297,26 @@ struct let warn_no_null min_must_null exists_min_must_null min_may_null = if Z.geq min_may_null n then M.warn "Resulting string might not be null-terminated because src doesn't contain a null byte in the first n bytes" - else if (exists_min_must_null && (Z.geq min_must_null n) || (Z.gt min_must_null min_may_null)) || not exists_min_must_null then + else if (exists_min_must_null && (min_must_null >=. n) || min_must_null >. min_may_null) || not exists_min_must_null then M.warn "Resulting string might not be null-terminated because src might not contain a null byte in the first n bytes" in (match Idx.minimal size, idx_maximal size with | Some min_size, Some max_size -> - if Z.gt n max_size then - M.warn ~category:ArrayOobMessage.past_end "Array size is smaller than n bytes; can cause a buffer overflow" - else if Z.gt n min_size then - M.warn ~category:ArrayOobMessage.past_end "Array size might be smaller than n bytes; can cause a buffer overflow" + if n >. max_size then + warn_past_end "Array size is smaller than n bytes; can cause a buffer overflow" + else if n >. min_size then + warn_past_end "Array size might be smaller than n bytes; can cause a buffer overflow" | Some min_size, None -> - if Z.gt n min_size then - M.warn ~category:ArrayOobMessage.past_end "Array size might be smaller than n bytes; can cause a buffer overflow" + if n >. min_size then + warn_past_end "Array size might be smaller than n bytes; can cause a buffer overflow" | None, Some max_size -> - if Z.gt n max_size then - M.warn ~category:ArrayOobMessage.past_end "Array size is smaller than n bytes; can cause a buffer overflow" + if n >. max_size then + warn_past_end "Array size is smaller than n bytes; can cause a buffer overflow" | None, None -> ()); let nulls = (* if definitely no null byte in array, i.e. must_nulls_set = may_nulls_set = empty set *) if Nulls.is_empty Definitely nulls then - (M.warn ~category:ArrayOobMessage.past_end + (warn_past_end "Resulting string might not be null-terminated because src doesn't contain a null byte"; match idx_maximal size with (* ... there *may* be null bytes from maximal size to n - 1 if maximal size < n (i.e. past end) *) @@ -1343,13 +1349,13 @@ struct (* if must_nulls_set and min_nulls_set empty, definitely no null byte in array => return interval [size, inf) and warn *) (* TODO: check of must set really needed? *) if Nulls.is_empty Definitely nulls then - (M.error ~category:ArrayOobMessage.past_end "Array doesn't contain a null byte: buffer overflow"; + (warn_past_end "Array doesn't contain a null byte: buffer overflow"; match Idx.minimal size with | Some min_size -> Idx.starting !Cil.kindOfSizeOf min_size | None -> Idx.starting !Cil.kindOfSizeOf Z.zero) (* if only must_nulls_set empty, no guarantee that null ever encountered in array => return interval [minimal may null, inf) and *) else if Nulls.is_empty Possibly nulls then - (M.warn ~category:ArrayOobMessage.past_end "Array might not contain a null byte: potential buffer overflow"; + (warn_past_end "Array might not contain a null byte: potential buffer overflow"; Idx.starting !Cil.kindOfSizeOf (Nulls.min_elem Possibly nulls)) (* else return interval [minimal may null, minimal must null] *) else @@ -1362,10 +1368,10 @@ struct let update_sets must_nulls_set2' may_nulls_set2' size2' len2 = match Idx.minimal size1, idx_maximal size1, Idx.minimal len2, idx_maximal len2 with | Some min_size1, Some max_size1, Some min_len2, Some max_len2 -> - (if Z.lt max_size1 min_len2 then - M.error ~category:ArrayOobMessage.past_end "The length of string src is greater than the allocated size for dest" - else if Z.lt min_size1 max_len2 then - M.warn ~category:ArrayOobMessage.past_end "The length of string src may be greater than the allocated size for dest"); + (if max_size1 <. min_len2 then + warn_past_end "The length of string src is greater than the allocated size for dest" + else if min_size1 <. max_len2 then + warn_past_end "The length of string src may be greater than the allocated size for dest"); let must_nulls_set_result = let min_size2 = BatOption.default Z.zero (Idx.minimal size2') in (* get must nulls from src string < minimal size of dest *) @@ -1380,8 +1386,8 @@ struct |> MaySet.union (MaySet.filter (Z.leq min_len2) may_nulls_set1 max_size1) in ((must_nulls_set_result, may_nulls_set_result), size1) | Some min_size1, None, Some min_len2, Some max_len2 -> - (if Z.lt min_size1 max_len2 then - M.warn ~category:ArrayOobMessage.past_end "The length of string src may be greater than the allocated size for dest"); + (if min_size1 <. max_len2 then + warn_past_end "The length of string src may be greater than the allocated size for dest"); let must_nulls_set_result = let min_size2 = BatOption.default Z.zero (Idx.minimal size2') in MustSet.filter (Z.gt min_size1) must_nulls_set2' min_size2 @@ -1392,10 +1398,10 @@ struct |> MaySet.union (MaySet.filter (Z.leq min_len2) may_nulls_set1 (Z.succ min_len2)) in ((must_nulls_set_result, may_nulls_set_result), size1) | Some min_size1, Some max_size1, Some min_len2, None -> - (if Z.lt max_size1 min_len2 then - M.error ~category:ArrayOobMessage.past_end "The length of string src is greater than the allocated size for dest" - else if Z.lt min_size1 min_len2 then - M.warn ~category:ArrayOobMessage.past_end "The length of string src may be greater than the allocated size for dest"); + (if max_size1 <. min_len2 then + warn_past_end "The length of string src is greater than the allocated size for dest" + else if min_size1 <. min_len2 then + warn_past_end"The length of string src may be greater than the allocated size for dest"); (* do not keep any index of dest as no maximal strlen of src *) let must_nulls_set_result = let min_size2 = BatOption.default Z.zero (Idx.minimal size2') in @@ -1406,8 +1412,8 @@ struct |> MaySet.union (MaySet.filter (Z.leq min_len2) may_nulls_set1 max_size1) in ((must_nulls_set_result, may_nulls_set_result), size1) | Some min_size1, None, Some min_len2, None -> - (if Z.lt min_size1 min_len2 then - M.warn ~category:ArrayOobMessage.past_end "The length of string src may be greater than the allocated size for dest"); + (if min_size1 <. min_len2 then + warn_past_end "The length of string src may be greater than the allocated size for dest"); (* do not keep any index of dest as no maximal strlen of src *) let must_nulls_set_result = let min_size2 = BatOption.default Z.zero (Idx.minimal size2') in @@ -1418,30 +1424,30 @@ struct |> MaySet.union (MaySet.filter (Z.leq min_len2) may_nulls_set1 (Z.succ min_len2)) in ((must_nulls_set_result, may_nulls_set_result), size1) (* any other case shouldn't happen as minimal index is always >= 0 *) - | _ -> ((MustSet.top (), MaySet.top ()), size1) in + | _ -> (Nulls.top (), size1) in (* warn if size of dest is (potentially) smaller than size of src and the latter (potentially) has no null byte at index < size of dest *) let sizes_warning size2 = (match Idx.minimal size1, idx_maximal size1, Idx.minimal size2, idx_maximal size2 with - | Some min_size1, _, Some min_size2, _ when Z.lt min_size1 min_size2 -> + | Some min_size1, _, Some min_size2, _ when min_size1 <. min_size2 -> if not (MaySet.exists (Z.gt min_size1) may_nulls_set2) then - M.error ~category:ArrayOobMessage.past_end "src doesn't contain a null byte at an index smaller than the size of dest" + warn_past_end "src doesn't contain a null byte at an index smaller than the size of dest" else if not (MustSet.exists (Z.gt min_size1) must_nulls_set2) then - M.warn ~category:ArrayOobMessage.past_end "src may not contain a null byte at an index smaller than the size of dest" - | Some min_size1, _, _, Some max_size2 when Z.lt min_size1 max_size2 -> + warn_past_end "src may not contain a null byte at an index smaller than the size of dest" + | Some min_size1, _, _, Some max_size2 when min_size1 <. max_size2 -> if not (MaySet.exists (Z.gt min_size1) may_nulls_set2) then - M.error ~category:ArrayOobMessage.past_end "src doesn't contain a null byte at an index smaller than the size of dest" + warn_past_end "src doesn't contain a null byte at an index smaller than the size of dest" else if not (MustSet.exists (Z.gt min_size1) must_nulls_set2) then - M.warn ~category:ArrayOobMessage.past_end "src may not contain a null byte at an index smaller than the size of dest" + warn_past_end "src may not contain a null byte at an index smaller than the size of dest" | Some min_size1, _, _, None -> if not (MustSet.exists (Z.gt min_size1) must_nulls_set2) then - M.warn ~category:ArrayOobMessage.past_end "src may not contain a null byte at an index smaller than the size of dest" - | _, Some max_size1, _, Some max_size2 when Z.lt max_size1 max_size2 -> + warn_past_end "src may not contain a null byte at an index smaller than the size of dest" + | _, Some max_size1, _, Some max_size2 when max_size1 <. max_size2 -> if not (MustSet.exists (Z.gt max_size1) must_nulls_set2) then - M.warn ~category:ArrayOobMessage.past_end "src may not contain a null byte at an index smaller than the size of dest" + warn_past_end "src may not contain a null byte at an index smaller than the size of dest" |_, Some max_size1, _, None -> if not (MustSet.exists (Z.gt max_size1) must_nulls_set2) then - M.warn ~category:ArrayOobMessage.past_end "src may not contain a null byte at an index smaller than the size of dest" + warn_past_end "src may not contain a null byte at an index smaller than the size of dest" | _ -> ()) in match n with @@ -1463,10 +1469,10 @@ struct let update_sets min_size1 max_size1 max_size1_exists minlen1 maxlen1 maxlen1_exists minlen2 maxlen2 maxlen2_exists must_nulls_set2' may_nulls_set2' = (* track any potential buffer overflow and issue warning if needed *) (if max_size1_exists && Z.leq max_size1 (Z.add minlen1 minlen2) then - M.error ~category:ArrayOobMessage.past_end + warn_past_end "The length of the concatenation of the strings in src and dest is greater than the allocated size for dest" else if (maxlen1_exists && maxlen2_exists && Z.leq min_size1 (Z.add maxlen1 maxlen2)) || not maxlen1_exists || not maxlen2_exists then - M.warn ~category:ArrayOobMessage.past_end + warn_past_end "The length of the concatenation of the strings in src and dest may be greater than the allocated size for dest"); (* if any must_nulls_set empty, result must_nulls_set also empty; * for all i1, i2 in may_nulls_set1, may_nulls_set2: add i1 + i2 if it is <= strlen(dest) + strlen(src) to new may_nulls_set @@ -1505,7 +1511,7 @@ struct if max_size1_exists then MaySet.filter (Z.lt min_i) may_nulls_set1 max_size1 |> MaySet.add min_i - |> MaySet.M.filter (fun x -> if max_size1_exists then Z.gt max_size1 x else true) + |> MaySet.M.filter (fun x -> if max_size1_exists then max_size1 >. x else true) else MaySet.top () in ((must_nulls_set_result, may_nulls_set_result), size1) @@ -1516,7 +1522,7 @@ struct match idx_maximal size2 with | Some max_size2 -> MaySet.filter (Z.geq min_i2) may_nulls_set2' max_size2 | None -> MaySet.filter (Z.geq min_i2) may_nulls_set2' (Z.succ min_i2) in - let must_nulls_set_result = MustSet.filter (fun x -> if maxlen1_exists && maxlen2_exists then Z.lt (Z.add maxlen1 maxlen2) x else false) must_nulls_set1 min_size1 in + let must_nulls_set_result = MustSet.filter (fun x -> if maxlen1_exists && maxlen2_exists then (Z.add maxlen1 maxlen2) <. x else false) must_nulls_set1 min_size1 in let may_nulls_set_result = if max_size1_exists then MaySet.filter (fun x -> if maxlen1_exists && maxlen2_exists then Z.leq x (Z.add maxlen1 maxlen2) else true) may_nulls_set1 max_size1 @@ -1525,7 +1531,7 @@ struct |> List.map (fun (i1, i2) -> Z.add i1 i2) |> MaySet.of_list |> MaySet.union (MaySet.filter (Z.lt (Z.add minlen1 minlen2)) may_nulls_set1 max_size1) - |> MaySet.M.filter (fun x -> if max_size1_exists then Z.gt max_size1 x else true) + |> MaySet.M.filter (fun x -> if max_size1_exists then max_size1 >. x else true) else if not (MaySet.is_top may_nulls_set1) then MaySet.M.filter (fun x -> if maxlen1_exists && maxlen2_exists then Z.leq x (Z.add maxlen1 maxlen2) else true) may_nulls_set1 |> MaySet.elements @@ -1538,7 +1544,7 @@ struct ((must_nulls_set_result, may_nulls_set_result), size1) in let compute_concat (must_nulls_set2',may_nulls_set2') = - let strlen1 = to_string_length ((must_nulls_set1, may_nulls_set1), size1) in + let strlen1 = to_string_length (nulls1, size1) in let strlen2 = to_string_length ((must_nulls_set2', may_nulls_set2'), size2) in match Idx.minimal size1, Idx.minimal strlen1, Idx.minimal strlen2 with | Some min_size1, Some minlen1, Some minlen2 -> @@ -1596,7 +1602,7 @@ struct match idx_maximal haystack_len, Idx.minimal needle_len with | Some haystack_max, Some needle_min -> (* if strlen(haystack) < strlen(needle), needle can never be substring of haystack => return None *) - if Z.lt haystack_max needle_min then + if haystack_max <. needle_min then IsNotSubstr else IsMaybeSubstr @@ -1620,7 +1626,7 @@ struct if not (Z.equal min_must1 min_must2) && Z.equal min_must1 (Nulls.min_elem Possibly nulls1) && Z.equal min_must2 (Nulls.min_elem Possibly nulls2) - && (not n_exists || Z.lt min_must1 n || Z.lt min_must2 n) + && (not n_exists || min_must1 <. n || min_must2 <. n) then (* if first null bytes are certain, have different indexes and are before index n if n present, return integer <> 0 *) Idx.of_excl_list IInt [Z.zero] @@ -1634,41 +1640,42 @@ struct | None -> (* track any potential buffer overflow and issue warning if needed *) (if Nulls.is_empty Definitely nulls1 && Nulls.is_empty Possibly nulls1 then - M.error ~category:ArrayOobMessage.past_end "Array of string 1 doesn't contain a null byte: buffer overflow" + warn_past_end "Array of string 1 doesn't contain a null byte: buffer overflow" else if Nulls.is_empty Possibly nulls1 then - M.warn ~category:ArrayOobMessage.past_end "Array of string 1 might not contain a null byte: potential buffer overflow"); + warn_past_end "Array of string 1 might not contain a null byte: potential buffer overflow"); (if Nulls.is_empty Definitely nulls2 && Nulls.is_empty Possibly nulls2 then - M.error ~category:ArrayOobMessage.past_end "Array of string 2 doesn't contain a null byte: buffer overflow" + warn_past_end "Array of string 2 doesn't contain a null byte: buffer overflow" else if Nulls.is_empty Possibly nulls2 then - M.warn ~category:ArrayOobMessage.past_end "Array of string 2 might not contain a null byte: potential buffer overflow"); + warn_past_end "Array of string 2 might not contain a null byte: potential buffer overflow"); (* compute abstract value for result of strcmp *) compare Z.zero false (* strncmp *) | Some n when n >= 0 -> + let n = Z.of_int n in let min_size1 = BatOption.default Z.zero (Idx.minimal size1) in let min_size2 = BatOption.default Z.zero (Idx.minimal size2) in (* issue a warning if n is (potentially) smaller than array sizes *) (match idx_maximal size1 with | Some max_size1 -> - if Z.gt (Z.of_int n) max_size1 then - M.warn ~category:ArrayOobMessage.past_end "The size of the array of string 1 is smaller than n bytes" - else if Z.gt (Z.of_int n) min_size1 then - M.warn ~category:ArrayOobMessage.past_end "The size of the array of string 1 might be smaller than n bytes" + if n >. max_size1 then + warn_past_end"The size of the array of string 1 is smaller than n bytes" + else if n >. min_size1 then + warn_past_end "The size of the array of string 1 might be smaller than n bytes" | None -> - if Z.gt (Z.of_int n) min_size1 then - M.warn ~category:ArrayOobMessage.past_end "The size of the array of string 1 might be smaller than n bytes" + if n >. min_size1 then + warn_past_end "The size of the array of string 1 might be smaller than n bytes" ); (match idx_maximal size2 with | Some max_size2 -> - if Z.gt (Z.of_int n) max_size2 then - M.warn ~category:ArrayOobMessage.past_end "The size of the array of string 2 is smaller than n bytes" - else if Z.gt (Z.of_int n) min_size2 then - M.warn ~category:ArrayOobMessage.past_end "The size of the array of string 2 might be smaller than n bytes" + if n >. max_size2 then + warn_past_end "The size of the array of string 2 is smaller than n bytes" + else if n >. min_size2 then + warn_past_end "The size of the array of string 2 might be smaller than n bytes" | None -> - if Z.gt (Z.of_int n) min_size2 then - M.warn ~category:ArrayOobMessage.past_end "The size of the array of string 2 might be smaller than n bytes"); + if n >. min_size2 then + warn_past_end "The size of the array of string 2 might be smaller than n bytes"); (* compute abstract value for result of strncmp *) - compare (Z.of_int n) true + compare n true | _ -> Idx.top_of IInt let update_length new_size (nulls, size) = (nulls, new_size) diff --git a/src/cdomains/nullByteSet.ml b/src/cdomains/nullByteSet.ml index 769b9cc485..283b15306c 100644 --- a/src/cdomains/nullByteSet.ml +++ b/src/cdomains/nullByteSet.ml @@ -149,7 +149,10 @@ module MustMaySet = struct let make_all_must () = (MustSet.bot (), MaySet.top ()) let make_none_may () = (MustSet.top (), MaySet.bot ()) - let may_exist f (musts, mays) = MaySet.exists f mays + let exists mode f (musts, mays) = + match mode with + | Definitely -> MustSet.exists f musts + | Possibly -> MaySet.exists f mays let forget_may (musts, mays) = (musts, MaySet.top ()) let forget_must (musts, mays) = (MustSet.top (), mays) From cd57e1faa5a70c46e249c783edcdd58d0173ca82 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 26 Nov 2023 17:38:15 +0100 Subject: [PATCH 1137/1312] Progress --- src/cdomains/arrayDomain.ml | 89 +++++++++++++++++++------------------ 1 file changed, 45 insertions(+), 44 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 920e97982a..ae6c35a6e0 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1361,42 +1361,44 @@ struct else Idx.of_interval !Cil.kindOfSizeOf (Nulls.min_elem Possibly nulls, Nulls.min_elem Definitely nulls) - let string_copy (nulls1, size1) (nulls2, size2) n = - let must_nulls_set1, may_nulls_set1 = nulls1 in - let must_nulls_set2, may_nulls_set2 = nulls2 in + let string_copy (dstnulls, dstsize) ((srcnulls, srcsize) as src) n = + let must_nulls_set1, may_nulls_set1 = dstnulls in (* filter out indexes before strlen(src) from dest sets and after strlen(src) from src sets and build union, keep size of dest *) - let update_sets must_nulls_set2' may_nulls_set2' size2' len2 = - match Idx.minimal size1, idx_maximal size1, Idx.minimal len2, idx_maximal len2 with - | Some min_size1, Some max_size1, Some min_len2, Some max_len2 -> - (if max_size1 <. min_len2 then + let update_sets (truncatednulls, truncatedsize) len2 = + let must_nulls_set2',may_nulls_set2' = truncatednulls in + match Idx.minimal dstsize, idx_maximal dstsize, Idx.minimal len2, idx_maximal len2 with + | Some min_dstsize, Some max_dstsize, Some min_srclen, Some max_srclen -> + (if max_dstsize <. min_srclen then warn_past_end "The length of string src is greater than the allocated size for dest" - else if min_size1 <. max_len2 then + else if min_dstsize <. max_srclen then warn_past_end "The length of string src may be greater than the allocated size for dest"); let must_nulls_set_result = - let min_size2 = BatOption.default Z.zero (Idx.minimal size2') in + let min_size2 = BatOption.default Z.zero (Idx.minimal truncatedsize) in (* get must nulls from src string < minimal size of dest *) - MustSet.filter (Z.gt min_size1) must_nulls_set2' min_size2 + MustSet.filter (Z.gt min_dstsize) must_nulls_set2' min_size2 (* and keep indexes of dest >= maximal strlen of src *) - |> MustSet.union (MustSet.filter (Z.leq max_len2) must_nulls_set1 min_size1) in + |> MustSet.union (MustSet.filter (Z.leq max_srclen) must_nulls_set1 min_dstsize) in let may_nulls_set_result = - let max_size2 = BatOption.default max_size1 (idx_maximal size2') in + let max_size2 = BatOption.default max_dstsize (idx_maximal truncatedsize) in (* get may nulls from src string < maximal size of dest *) - MaySet.filter (Z.gt max_size1) may_nulls_set2' max_size2 + MaySet.filter (Z.gt max_dstsize) may_nulls_set2' max_size2 (* and keep indexes of dest >= minimal strlen of src *) - |> MaySet.union (MaySet.filter (Z.leq min_len2) may_nulls_set1 max_size1) in - ((must_nulls_set_result, may_nulls_set_result), size1) + |> MaySet.union (MaySet.filter (Z.leq min_srclen) may_nulls_set1 max_dstsize) in + ((must_nulls_set_result, may_nulls_set_result), dstsize) + + | Some min_size1, None, Some min_len2, Some max_len2 -> (if min_size1 <. max_len2 then warn_past_end "The length of string src may be greater than the allocated size for dest"); let must_nulls_set_result = - let min_size2 = BatOption.default Z.zero (Idx.minimal size2') in + let min_size2 = BatOption.default Z.zero (Idx.minimal truncatedsize) in MustSet.filter (Z.gt min_size1) must_nulls_set2' min_size2 |> MustSet.union (MustSet.filter (Z.leq max_len2) must_nulls_set1 min_size1) in let may_nulls_set_result = (* get all may nulls from src string as no maximal size of dest *) may_nulls_set2' |> MaySet.union (MaySet.filter (Z.leq min_len2) may_nulls_set1 (Z.succ min_len2)) in - ((must_nulls_set_result, may_nulls_set_result), size1) + ((must_nulls_set_result, may_nulls_set_result), dstsize) | Some min_size1, Some max_size1, Some min_len2, None -> (if max_size1 <. min_len2 then warn_past_end "The length of string src is greater than the allocated size for dest" @@ -1404,65 +1406,64 @@ struct warn_past_end"The length of string src may be greater than the allocated size for dest"); (* do not keep any index of dest as no maximal strlen of src *) let must_nulls_set_result = - let min_size2 = BatOption.default Z.zero (Idx.minimal size2') in + let min_size2 = BatOption.default Z.zero (Idx.minimal truncatedsize) in MustSet.filter (Z.gt min_size1) must_nulls_set2' min_size2 in let may_nulls_set_result = - let max_size2 = BatOption.default max_size1 (idx_maximal size2') in + let max_size2 = BatOption.default max_size1 (idx_maximal truncatedsize) in MaySet.filter (Z.gt max_size1) may_nulls_set2' max_size2 |> MaySet.union (MaySet.filter (Z.leq min_len2) may_nulls_set1 max_size1) in - ((must_nulls_set_result, may_nulls_set_result), size1) + ((must_nulls_set_result, may_nulls_set_result), dstsize) | Some min_size1, None, Some min_len2, None -> (if min_size1 <. min_len2 then warn_past_end "The length of string src may be greater than the allocated size for dest"); (* do not keep any index of dest as no maximal strlen of src *) let must_nulls_set_result = - let min_size2 = BatOption.default Z.zero (Idx.minimal size2') in + let min_size2 = BatOption.default Z.zero (Idx.minimal truncatedsize) in MustSet.filter (Z.gt min_size1) must_nulls_set2' min_size2 in let may_nulls_set_result = (* get all may nulls from src string as no maximal size of dest *) may_nulls_set2' |> MaySet.union (MaySet.filter (Z.leq min_len2) may_nulls_set1 (Z.succ min_len2)) in - ((must_nulls_set_result, may_nulls_set_result), size1) + ((must_nulls_set_result, may_nulls_set_result), dstsize) (* any other case shouldn't happen as minimal index is always >= 0 *) - | _ -> (Nulls.top (), size1) in + | _ -> (Nulls.top (), dstsize) in (* warn if size of dest is (potentially) smaller than size of src and the latter (potentially) has no null byte at index < size of dest *) - let sizes_warning size2 = - (match Idx.minimal size1, idx_maximal size1, Idx.minimal size2, idx_maximal size2 with - | Some min_size1, _, Some min_size2, _ when min_size1 <. min_size2 -> - if not (MaySet.exists (Z.gt min_size1) may_nulls_set2) then + let sizes_warning srcsize = + (match Idx.minimal dstsize, idx_maximal dstsize, Idx.minimal srcsize, idx_maximal srcsize with + | Some min_dstsize, _, Some min_srcsize, _ when min_dstsize <. min_srcsize -> + if not (Nulls.exists Possibly (Z.gt min_dstsize) srcnulls) then warn_past_end "src doesn't contain a null byte at an index smaller than the size of dest" - else if not (MustSet.exists (Z.gt min_size1) must_nulls_set2) then + else if not (Nulls.exists Definitely (Z.gt min_dstsize) srcnulls) then warn_past_end "src may not contain a null byte at an index smaller than the size of dest" - | Some min_size1, _, _, Some max_size2 when min_size1 <. max_size2 -> - if not (MaySet.exists (Z.gt min_size1) may_nulls_set2) then + | Some min_dstsize, _, _, Some max_srcsize when min_dstsize <. max_srcsize -> + if not (Nulls.exists Possibly (Z.gt min_dstsize) srcnulls) then warn_past_end "src doesn't contain a null byte at an index smaller than the size of dest" - else if not (MustSet.exists (Z.gt min_size1) must_nulls_set2) then + else if not (Nulls.exists Definitely (Z.gt min_dstsize) srcnulls) then warn_past_end "src may not contain a null byte at an index smaller than the size of dest" - | Some min_size1, _, _, None -> - if not (MustSet.exists (Z.gt min_size1) must_nulls_set2) then + | Some min_dstsize, _, _, None -> + if not (Nulls.exists Definitely (Z.gt min_dstsize) srcnulls) then warn_past_end "src may not contain a null byte at an index smaller than the size of dest" - | _, Some max_size1, _, Some max_size2 when max_size1 <. max_size2 -> - if not (MustSet.exists (Z.gt max_size1) must_nulls_set2) then + | _, Some mac_dstsize, _, Some max_srcsize when mac_dstsize <. max_srcsize -> + if not (Nulls.exists Definitely (Z.gt mac_dstsize) srcnulls) then warn_past_end "src may not contain a null byte at an index smaller than the size of dest" - |_, Some max_size1, _, None -> - if not (MustSet.exists (Z.gt max_size1) must_nulls_set2) then + |_, Some max_dstsize, _, None -> + if not (Nulls.exists Definitely (Z.gt max_dstsize) srcnulls) then warn_past_end "src may not contain a null byte at an index smaller than the size of dest" | _ -> ()) in match n with (* strcpy *) | None -> - sizes_warning size2; - let (must_nulls_set2', may_nulls_set2'), size2' = to_string (nulls2, size2) in - let strlen2 = to_string_length (nulls2, size2) in - update_sets must_nulls_set2' may_nulls_set2' size2' strlen2 + sizes_warning srcsize; + let truncated = to_string src in + update_sets truncated (to_string_length src) (* strncpy = exactly n bytes from src are copied to dest *) | Some n when n >= 0 -> sizes_warning (Idx.of_int ILong (Z.of_int n)); - let (must_nulls_set2', may_nulls_set2'), size2' = to_n_string (nulls2, size2) n in - update_sets must_nulls_set2' may_nulls_set2' size2' (Idx.of_int !Cil.kindOfSizeOf (Z.of_int n)) - | _ -> (Nulls.top (), size1) + let truncated = to_n_string src n in + update_sets truncated (Idx.of_int !Cil.kindOfSizeOf (Z.of_int n)) + | _ -> (Nulls.top (), dstsize) let string_concat (nulls1, size1) (nulls2, size2) n = let (must_nulls_set1, may_nulls_set1) = nulls1 in From b85ed973887968ad5bacd2fab9f296c45e7205aa Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 26 Nov 2023 18:08:12 +0100 Subject: [PATCH 1138/1312] Progress --- src/cdomains/arrayDomain.ml | 10 +++++----- src/cdomains/nullByteSet.ml | 2 ++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index ae6c35a6e0..3edfb4d207 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1234,7 +1234,7 @@ struct | Some i -> build_set (i + 1) (MaySet.add (Z.of_int i) set) | None -> MaySet.add last_null set in let set = build_set 0 (MaySet.empty ()) in - ((set, set), Idx.of_int ILong (Z.succ last_null)) + (Nulls.precise_set set, Idx.of_int ILong (Z.succ last_null)) (** Returns an abstract value with at most one null byte marking the end of the string *) let to_string ((nulls, size) as x:t):t = @@ -1579,10 +1579,10 @@ struct let n = Z.of_int n in (* take at most n bytes from src; if no null byte among them, add null byte at index n *) let nulls2' = - let (must_nulls_set2, may_nulls_set2), size2 = to_string (nulls2, size2) in - if not (MaySet.exists (Z.gt n) may_nulls_set2) then - (Nulls.precise_singleton n) - else if not (MustSet.exists (Z.gt n) must_nulls_set2) then + let ((must_nulls_set2, may_nulls_set2) as nulls2), size2 = to_string (nulls2, size2) in + if not (Nulls.exists Possibly (Z.gt n) nulls2) then + Nulls.precise_singleton n + else if not (Nulls.exists Definitely (Z.gt n) nulls2) then let max_size2 = BatOption.default (Z.succ n) (idx_maximal size2) in (MustSet.empty (), MaySet.add n (MaySet.filter (Z.geq n) may_nulls_set2 max_size2)) else diff --git a/src/cdomains/nullByteSet.ml b/src/cdomains/nullByteSet.ml index 283b15306c..320126b517 100644 --- a/src/cdomains/nullByteSet.ml +++ b/src/cdomains/nullByteSet.ml @@ -146,6 +146,8 @@ module MustMaySet = struct let precise_singleton i = (MustSet.singleton i, MaySet.singleton i) + let precise_set s = (s,s) + let make_all_must () = (MustSet.bot (), MaySet.top ()) let make_none_may () = (MustSet.top (), MaySet.bot ()) From ef3f6872fe53ba04cad1f0aa19c776621bbb9fe0 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 26 Nov 2023 18:38:17 +0100 Subject: [PATCH 1139/1312] Pull things together --- src/cdomains/arrayDomain.ml | 64 ++++++++++++++++++++----------------- src/cdomains/nullByteSet.ml | 5 ++- 2 files changed, 39 insertions(+), 30 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 3edfb4d207..f720e2cb9b 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1006,6 +1006,7 @@ struct let (<=.) = Z.leq let (>.) = Z.gt let (>=.) = Z.geq + let (=.) = Z.equal (* (Must Null Set, May Null Set, Array Size) *) include Lattice.Prod (Nulls) (Idx) @@ -1160,7 +1161,7 @@ struct Nulls.add_interval Possibly (min_i, Z.pred max_size) nulls ) | Some max_i when max_i >=. Z.zero -> - if Z.equal min_i max_i then + if min_i =. max_i then set_exact_nulls min_i else set_interval min_i max_i @@ -1195,13 +1196,12 @@ struct min_i, None | None, None -> Z.zero, None in - let size = BatOption.map_default (fun x -> Idx.of_interval ILong (min_i, x)) (Idx.starting ILong min_i) max_i in - let nulls = match Val.is_null v with - | Null -> Nulls.make_all_must () - | NotNull -> Nulls.make_none_may () - | Top -> Nulls.top () - in - (nulls, size) + let size = BatOption.map_default (fun max -> Idx.of_interval ILong (min_i, max)) (Idx.starting ILong min_i) max_i in + match Val.is_null v with + | Null -> (Nulls.make_all_must (), size) + | NotNull -> (Nulls.empty (), size) + | Top -> (Nulls.top (), size) + let length (_, size) = Some size @@ -1248,7 +1248,7 @@ struct let min_must_null = Nulls.min_elem Definitely nulls in let min_may_null = Nulls.min_elem Possibly nulls in (* if smallest index in sets coincides, only this null byte is kept in both sets *) - if Z.equal min_must_null min_may_null then + if min_must_null =. min_may_null then let nulls = Nulls.precise_singleton min_must_null in (nulls, Idx.of_int ILong (Z.succ min_must_null)) (* else return empty must_nulls_set and keep every index up to smallest index of must_nulls_set included in may_nulls_set *) @@ -1257,6 +1257,8 @@ struct | Some max_size -> ((MustSet.empty (), MaySet.filter (Z.geq min_must_null) (Nulls.get_set Possibly nulls) max_size), Idx.of_int ILong (Z.succ min_must_null)) | None -> if MaySet.is_top (Nulls.get_set Possibly nulls) then + let empty = Nulls.empty () in + let rec add_indexes acc i = if i >. min_must_null then acc @@ -1281,14 +1283,14 @@ struct else add_indexes (Z.succ i) max (MaySet.add i set) in let update_must_indexes min_must_null must_nulls_set = - if Z.equal min_must_null Z.zero then + if min_must_null =. Z.zero then MustSet.bot () else (* if strlen < n, every byte starting from min_must_null is surely also transformed to null *) add_indexes min_must_null n must_nulls_set |> MustSet.M.filter (Z.gt n) in let update_may_indexes min_may_null may_nulls_set = - if Z.equal min_may_null Z.zero then + if min_may_null =. Z.zero then MaySet.top () else (* if minimal strlen < n, every byte starting from minimal may null index may be transformed to null *) @@ -1327,7 +1329,7 @@ struct else if Nulls.is_empty Possibly nulls then let min_may_null = Nulls.min_elem Possibly nulls in warn_no_null Z.zero false min_may_null; - if Z.equal min_may_null Z.zero then + if min_may_null =. Z.zero then Nulls.forget_may nulls else let (must, mays) = Nulls.add_interval Possibly (min_may_null, Z.pred n) nulls in @@ -1338,7 +1340,7 @@ struct (* warn if resulting array may not contain null byte *) warn_no_null min_must_null true min_may_null; (* if min_must_null = min_may_null, remove indexes >= n and add all indexes from minimal must/may null to n - 1 in the sets *) - if Z.equal min_must_null min_may_null then + if min_must_null =. min_may_null then (update_must_indexes min_must_null must_nulls_set, update_may_indexes min_may_null may_nulls_set) else (MustSet.top (), update_may_indexes min_may_null may_nulls_set) @@ -1466,8 +1468,7 @@ struct | _ -> (Nulls.top (), dstsize) let string_concat (nulls1, size1) (nulls2, size2) n = - let (must_nulls_set1, may_nulls_set1) = nulls1 in - let update_sets min_size1 max_size1 max_size1_exists minlen1 maxlen1 maxlen1_exists minlen2 maxlen2 maxlen2_exists must_nulls_set2' may_nulls_set2' = + let update_sets min_size1 max_size1 max_size1_exists minlen1 maxlen1 maxlen1_exists minlen2 maxlen2 maxlen2_exists nulls2' = (* track any potential buffer overflow and issue warning if needed *) (if max_size1_exists && Z.leq max_size1 (Z.add minlen1 minlen2) then warn_past_end @@ -1478,7 +1479,9 @@ struct (* if any must_nulls_set empty, result must_nulls_set also empty; * for all i1, i2 in may_nulls_set1, may_nulls_set2: add i1 + i2 if it is <= strlen(dest) + strlen(src) to new may_nulls_set * and keep indexes > minimal strlen(dest) + strlen(src) of may_nulls_set *) - if MustSet.is_empty must_nulls_set1 || MustSet.is_empty must_nulls_set2' then + if Nulls.is_empty Possibly nulls1 || Nulls.is_empty Possibly nulls2 then + let (must_nulls_set1, may_nulls_set1) = nulls1 in + let (must_nulls_set2', may_nulls_set2') = nulls2' in let may_nulls_set_result = if max_size1_exists then MaySet.filter (fun x -> if maxlen1_exists && maxlen2_exists then Z.leq x (Z.add maxlen1 maxlen2) else true) may_nulls_set1 max_size1 @@ -1500,9 +1503,10 @@ struct MaySet.top () in ((MustSet.top (), may_nulls_set_result), size1) (* if minimal must null = minimal may null in ar1 and ar2, add them together and keep indexes > strlen(dest) + strlen(src) of ar1 *) - else if Z.equal (MustSet.min_elt must_nulls_set1) (MaySet.min_elt may_nulls_set1) && Z.equal (MustSet.min_elt must_nulls_set2') (MaySet.min_elt may_nulls_set2') then - let min_i1 = MustSet.min_elt must_nulls_set1 in - let min_i2 = MustSet.min_elt must_nulls_set2' in + else if Nulls.min_elem_precise (nulls1) && Nulls.min_elem_precise nulls2' then + let (must_nulls_set1, may_nulls_set1) = nulls1 in + let min_i1 = Nulls.min_elem Definitely nulls1 in + let min_i2 = Nulls.min_elem Definitely nulls2' in let min_i = Z.add min_i1 min_i2 in let must_nulls_set_result = MustSet.filter (Z.lt min_i) must_nulls_set1 min_size1 @@ -1518,6 +1522,8 @@ struct ((must_nulls_set_result, may_nulls_set_result), size1) (* else only add all may nulls together <= strlen(dest) + strlen(src) *) else + let (must_nulls_set1, may_nulls_set1) = nulls1 in + let (must_nulls_set2', may_nulls_set2') = nulls2' in let min_i2 = MustSet.min_elt must_nulls_set2' in let may_nulls_set2'_until_min_i2 = match idx_maximal size2 with @@ -1544,27 +1550,27 @@ struct MaySet.top () in ((must_nulls_set_result, may_nulls_set_result), size1) in - let compute_concat (must_nulls_set2',may_nulls_set2') = + let compute_concat nulls2' = let strlen1 = to_string_length (nulls1, size1) in - let strlen2 = to_string_length ((must_nulls_set2', may_nulls_set2'), size2) in + let strlen2 = to_string_length (nulls2', size2) in match Idx.minimal size1, Idx.minimal strlen1, Idx.minimal strlen2 with | Some min_size1, Some minlen1, Some minlen2 -> begin match idx_maximal size1, idx_maximal strlen1, idx_maximal strlen2 with | Some max_size1, Some maxlen1, Some maxlen2 -> - update_sets min_size1 max_size1 true minlen1 maxlen1 true minlen2 maxlen2 true must_nulls_set2' may_nulls_set2' + update_sets min_size1 max_size1 true minlen1 maxlen1 true minlen2 maxlen2 true nulls2' (* no upper bound for length of concatenation *) | Some max_size1, None, Some _ | Some max_size1, Some _, None | Some max_size1, None, None -> - update_sets min_size1 max_size1 true minlen1 Z.zero false minlen2 Z.zero false must_nulls_set2' may_nulls_set2' + update_sets min_size1 max_size1 true minlen1 Z.zero false minlen2 Z.zero false nulls2' (* no upper bound for size of dest *) | None, Some maxlen1, Some maxlen2 -> - update_sets min_size1 Z.zero false minlen1 maxlen1 true minlen2 maxlen2 true must_nulls_set2' may_nulls_set2' + update_sets min_size1 Z.zero false minlen1 maxlen1 true minlen2 maxlen2 true nulls2' (* no upper bound for size of dest and length of concatenation *) | None, None, Some _ | None, Some _, None | None, None, None -> - update_sets min_size1 Z.zero false minlen1 Z.zero false minlen2 Z.zero false must_nulls_set2' may_nulls_set2' + update_sets min_size1 Z.zero false minlen1 Z.zero false minlen2 Z.zero false nulls2' end (* any other case shouldn't happen as minimal index is always >= 0 *) | _ -> (Nulls.top (), size1) in @@ -1612,7 +1618,7 @@ struct let string_comparison (nulls1, size1) (nulls2, size2) n = let compare n n_exists = (* if s1 = s2 = empty string, i.e. certain null byte at index 0, or n = 0, return 0 *) - if (Nulls.mem Definitely Z.zero nulls1 && Nulls.mem Definitely Z.zero nulls2) || (n_exists && Z.equal Z.zero n) then + if (Nulls.mem Definitely Z.zero nulls1 && Nulls.mem Definitely Z.zero nulls2) || (n_exists && n =. Z.zero) then Idx.of_int IInt Z.zero (* if only s1 = empty string, return negative integer *) else if Nulls.mem Definitely Z.zero nulls1 && not (Nulls.mem Possibly Z.zero nulls2) then @@ -1624,9 +1630,9 @@ struct try let min_must1 = Nulls.min_elem Definitely nulls1 in let min_must2 = Nulls.min_elem Definitely nulls2 in - if not (Z.equal min_must1 min_must2) - && Z.equal min_must1 (Nulls.min_elem Possibly nulls1) - && Z.equal min_must2 (Nulls.min_elem Possibly nulls2) + if not (min_must1 =. min_must2) + && min_must1 =.(Nulls.min_elem Possibly nulls1) + && min_must2 =. (Nulls.min_elem Possibly nulls2) && (not n_exists || min_must1 <. n || min_must2 <. n) then (* if first null bytes are certain, have different indexes and are before index n if n present, return integer <> 0 *) diff --git a/src/cdomains/nullByteSet.ml b/src/cdomains/nullByteSet.ml index 320126b517..b1580d5717 100644 --- a/src/cdomains/nullByteSet.ml +++ b/src/cdomains/nullByteSet.ml @@ -78,6 +78,9 @@ module MustMaySet = struct | Definitely -> MustSet.min_elt musts | Possibly -> MaySet.min_elt mays + let min_elem_precise x = + Z.equal (min_elem Definitely x) (min_elem Possibly x) + let mem mode i (musts, mays) = match mode with | Definitely -> MustSet.mem i musts @@ -149,7 +152,7 @@ module MustMaySet = struct let precise_set s = (s,s) let make_all_must () = (MustSet.bot (), MaySet.top ()) - let make_none_may () = (MustSet.top (), MaySet.bot ()) + let empty () = (MustSet.top (), MaySet.bot ()) let exists mode f (musts, mays) = match mode with From 984165f479fdf23be021f7b04d35190d89225ab7 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 26 Nov 2023 18:47:46 +0100 Subject: [PATCH 1140/1312] Alias for Z.add --- src/cdomains/arrayDomain.ml | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index f720e2cb9b..17bdd50a3f 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1007,6 +1007,7 @@ struct let (>.) = Z.gt let (>=.) = Z.geq let (=.) = Z.equal + let (+.) = Z.add (* (Must Null Set, May Null Set, Array Size) *) include Lattice.Prod (Nulls) (Idx) @@ -1470,10 +1471,10 @@ struct let string_concat (nulls1, size1) (nulls2, size2) n = let update_sets min_size1 max_size1 max_size1_exists minlen1 maxlen1 maxlen1_exists minlen2 maxlen2 maxlen2_exists nulls2' = (* track any potential buffer overflow and issue warning if needed *) - (if max_size1_exists && Z.leq max_size1 (Z.add minlen1 minlen2) then + (if max_size1_exists && max_size1 <=. (minlen1 +. minlen2) then warn_past_end "The length of the concatenation of the strings in src and dest is greater than the allocated size for dest" - else if (maxlen1_exists && maxlen2_exists && Z.leq min_size1 (Z.add maxlen1 maxlen2)) || not maxlen1_exists || not maxlen2_exists then + else if (maxlen1_exists && maxlen2_exists && min_size1 <=. (maxlen1 +. maxlen2)) || not maxlen1_exists || not maxlen2_exists then warn_past_end "The length of the concatenation of the strings in src and dest may be greater than the allocated size for dest"); (* if any must_nulls_set empty, result must_nulls_set also empty; @@ -1484,30 +1485,30 @@ struct let (must_nulls_set2', may_nulls_set2') = nulls2' in let may_nulls_set_result = if max_size1_exists then - MaySet.filter (fun x -> if maxlen1_exists && maxlen2_exists then Z.leq x (Z.add maxlen1 maxlen2) else true) may_nulls_set1 max_size1 + MaySet.filter (fun x -> if maxlen1_exists && maxlen2_exists then x <=. (maxlen1 +. maxlen2) else true) may_nulls_set1 max_size1 |> MaySet.elements (* if may_nulls_set2' is top, limit it to max_size1 *) |> BatList.cartesian_product (MaySet.elements (MaySet.filter (fun x -> true) may_nulls_set2' max_size1)) - |> List.map (fun (i1, i2) -> Z.add i1 i2) + |> List.map (fun (i1, i2) -> i1 +. i2) |> MaySet.of_list - |> MaySet.union (MaySet.filter (Z.lt (Z.add minlen1 minlen2)) may_nulls_set1 max_size1) + |> MaySet.union (MaySet.filter (Z.lt (minlen1 +. minlen2)) may_nulls_set1 max_size1) |> MaySet.M.filter (Z.gt max_size1) else if not (MaySet.is_top may_nulls_set1) && not (MaySet.is_top may_nulls_set2') && maxlen1_exists && maxlen2_exists then - MaySet.M.filter (fun x -> if maxlen1_exists && maxlen2_exists then Z.leq x (Z.add maxlen1 maxlen2) else true) may_nulls_set1 + MaySet.M.filter (fun x -> if maxlen1_exists && maxlen2_exists then x <=. (maxlen1 +. maxlen2) else true) may_nulls_set1 |> MaySet.elements |> BatList.cartesian_product (MaySet.elements may_nulls_set2') - |> List.map (fun (i1, i2) -> Z.add i1 i2) + |> List.map (fun (i1, i2) -> i1 +. i2) |> MaySet.of_list - |> MaySet.union (MaySet.M.filter (Z.lt (Z.add minlen1 minlen2)) may_nulls_set1) + |> MaySet.union (MaySet.M.filter (Z.lt (minlen1 +. minlen2)) may_nulls_set1) else MaySet.top () in ((MustSet.top (), may_nulls_set_result), size1) (* if minimal must null = minimal may null in ar1 and ar2, add them together and keep indexes > strlen(dest) + strlen(src) of ar1 *) else if Nulls.min_elem_precise (nulls1) && Nulls.min_elem_precise nulls2' then - let (must_nulls_set1, may_nulls_set1) = nulls1 in let min_i1 = Nulls.min_elem Definitely nulls1 in let min_i2 = Nulls.min_elem Definitely nulls2' in - let min_i = Z.add min_i1 min_i2 in + let min_i = min_i1 +. min_i2 in + let (must_nulls_set1, may_nulls_set1) = nulls1 in let must_nulls_set_result = MustSet.filter (Z.lt min_i) must_nulls_set1 min_size1 |> MustSet.add min_i @@ -1522,30 +1523,30 @@ struct ((must_nulls_set_result, may_nulls_set_result), size1) (* else only add all may nulls together <= strlen(dest) + strlen(src) *) else + let min_i2 = Nulls.min_elem Definitely nulls2' in let (must_nulls_set1, may_nulls_set1) = nulls1 in let (must_nulls_set2', may_nulls_set2') = nulls2' in - let min_i2 = MustSet.min_elt must_nulls_set2' in let may_nulls_set2'_until_min_i2 = match idx_maximal size2 with | Some max_size2 -> MaySet.filter (Z.geq min_i2) may_nulls_set2' max_size2 | None -> MaySet.filter (Z.geq min_i2) may_nulls_set2' (Z.succ min_i2) in - let must_nulls_set_result = MustSet.filter (fun x -> if maxlen1_exists && maxlen2_exists then (Z.add maxlen1 maxlen2) <. x else false) must_nulls_set1 min_size1 in + let must_nulls_set_result = MustSet.filter (fun x -> if maxlen1_exists && maxlen2_exists then (maxlen1 +. maxlen2) <. x else false) must_nulls_set1 min_size1 in let may_nulls_set_result = if max_size1_exists then - MaySet.filter (fun x -> if maxlen1_exists && maxlen2_exists then Z.leq x (Z.add maxlen1 maxlen2) else true) may_nulls_set1 max_size1 + MaySet.filter (fun x -> if maxlen1_exists && maxlen2_exists then x <=. (maxlen1 +. maxlen2) else true) may_nulls_set1 max_size1 |> MaySet.elements |> BatList.cartesian_product (MaySet.elements may_nulls_set2'_until_min_i2) - |> List.map (fun (i1, i2) -> Z.add i1 i2) + |> List.map (fun (i1, i2) -> i1 +. i2) |> MaySet.of_list - |> MaySet.union (MaySet.filter (Z.lt (Z.add minlen1 minlen2)) may_nulls_set1 max_size1) + |> MaySet.union (MaySet.filter (Z.lt (minlen1 +. minlen2)) may_nulls_set1 max_size1) |> MaySet.M.filter (fun x -> if max_size1_exists then max_size1 >. x else true) else if not (MaySet.is_top may_nulls_set1) then - MaySet.M.filter (fun x -> if maxlen1_exists && maxlen2_exists then Z.leq x (Z.add maxlen1 maxlen2) else true) may_nulls_set1 + MaySet.M.filter (fun x -> if maxlen1_exists && maxlen2_exists then x <=. (maxlen1 +. maxlen2) else true) may_nulls_set1 |> MaySet.elements |> BatList.cartesian_product (MaySet.elements may_nulls_set2'_until_min_i2) - |> List.map (fun (i1, i2) -> Z.add i1 i2) + |> List.map (fun (i1, i2) -> i1 +. i2) |> MaySet.of_list - |> MaySet.union (MaySet.M.filter (Z.lt (Z.add minlen1 minlen2)) may_nulls_set1) + |> MaySet.union (MaySet.M.filter (Z.lt (minlen1 +. minlen2)) may_nulls_set1) else MaySet.top () in ((must_nulls_set_result, may_nulls_set_result), size1) in From 2135296baac27aeabc5b3d48796dc6e73fc0115d Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 26 Nov 2023 18:51:47 +0100 Subject: [PATCH 1141/1312] More reuse --- src/cdomains/arrayDomain.ml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 17bdd50a3f..7d37396ede 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1259,13 +1259,7 @@ struct | None -> if MaySet.is_top (Nulls.get_set Possibly nulls) then let empty = Nulls.empty () in - - let rec add_indexes acc i = - if i >. min_must_null then - acc - else - add_indexes (MaySet.add i acc) (Z.succ i) in - ((MustSet.empty (), add_indexes (MaySet.empty ()) Z.zero), Idx.of_int ILong (Z.succ min_must_null)) + (Nulls.add_interval Possibly (Z.zero, min_must_null) empty, Idx.of_int ILong (Z.succ min_must_null)) else ((MustSet.empty (), MaySet.M.filter (Z.geq min_must_null) (Nulls.get_set Possibly nulls)), Idx.of_int ILong (Z.succ min_must_null)) From 34d2e1cf8f4f6bfde663ee624eda08e6d6287ec9 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 26 Nov 2023 19:59:54 +0100 Subject: [PATCH 1142/1312] `to_string` free of direct set manipulation --- src/cdomains/arrayDomain.ml | 76 +++++++++++++++++++------------------ src/cdomains/nullByteSet.ml | 30 +++++++++------ 2 files changed, 58 insertions(+), 48 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 7d37396ede..813a69d47f 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1137,7 +1137,7 @@ struct (if Val.is_null v = Null && idx_maximal size = None then match idx_maximal size with (* ... and there is no maximal size, modify may_nulls_set to top *) - | None -> Nulls.forget_may nulls + | None -> Nulls.add_all Possibly nulls (* ... and there is a maximal size, add all i from minimal index to maximal size to may_nulls_set *) | Some max_size -> Nulls.add_interval Possibly (min_i, Z.pred max_size) nulls (* ... and value <> null, only keep indexes < minimal index in must_nulls_set *) @@ -1150,11 +1150,11 @@ struct | None, None -> Nulls.top () (* ... and only minimal size known, remove all indexes < minimal size from must_nulls_set and modify may_nulls_set to top *) | Some min_size, None -> - let nulls = Nulls.forget_may nulls in + let nulls = Nulls.add_all Possibly nulls in Nulls.filter_musts (Z.gt min_size) min_size nulls (* ... and only maximal size known, modify must_nulls_set to top and add all i from minimal index to maximal size to may_nulls_set *) | None, Some max_size -> - let nulls = Nulls.forget_must nulls in + let nulls = Nulls.remove_all Possibly nulls in Nulls.add_interval Possibly (min_i, Z.pred max_size) nulls (* ... and size is known, remove all indexes < minimal size from must_nulls_set and add all i from minimal index to maximal size to may_nulls_set *) | Some min_size, Some max_size -> @@ -1214,7 +1214,7 @@ struct (* if f(null) = null, all values in must_nulls_set still are surely null; * assume top for may_nulls_set as checking effect of f for every possible value is unfeasbile *) match Val.is_null (f (Val.null ())) with - | Null -> (Nulls.forget_may nulls, size) + | Null -> (Nulls.add_all Possibly nulls, size) | _ -> (Nulls.top (), size) (* else also return top for must_nulls_set *) let fold_left f acc _ = f acc (Val.top ()) @@ -1252,16 +1252,18 @@ struct if min_must_null =. min_may_null then let nulls = Nulls.precise_singleton min_must_null in (nulls, Idx.of_int ILong (Z.succ min_must_null)) - (* else return empty must_nulls_set and keep every index up to smallest index of must_nulls_set included in may_nulls_set *) + (* else return empty must_nulls_set and keep every index up to smallest index of must_nulls_set included in may_nulls_set *) else match idx_maximal size with - | Some max_size -> ((MustSet.empty (), MaySet.filter (Z.geq min_must_null) (Nulls.get_set Possibly nulls) max_size), Idx.of_int ILong (Z.succ min_must_null)) - | None -> - if MaySet.is_top (Nulls.get_set Possibly nulls) then - let empty = Nulls.empty () in - (Nulls.add_interval Possibly (Z.zero, min_must_null) empty, Idx.of_int ILong (Z.succ min_must_null)) - else - ((MustSet.empty (), MaySet.M.filter (Z.geq min_must_null) (Nulls.get_set Possibly nulls)), Idx.of_int ILong (Z.succ min_must_null)) + | Some max_size -> + let nulls' = Nulls.remove_all Possibly nulls in + (Nulls.filter ~max_size (Z.leq min_must_null) nulls', Idx.of_int ILong (Z.succ min_must_null)) + | None when not (Nulls.may_can_benefit_from_filter nulls) -> + let empty = Nulls.empty () in + (Nulls.add_interval Possibly (Z.zero, min_must_null) empty, Idx.of_int ILong (Z.succ min_must_null)) + | None -> + let nulls' = Nulls.remove_all Possibly nulls in + (Nulls.filter (Z.leq min_must_null) nulls', Idx.of_int ILong (Z.succ min_must_null)) (** [to_n_string index_set n] returns an abstract value with a potential null byte * marking the end of the string and if needed followed by further null bytes to obtain @@ -1325,7 +1327,7 @@ struct let min_may_null = Nulls.min_elem Possibly nulls in warn_no_null Z.zero false min_may_null; if min_may_null =. Z.zero then - Nulls.forget_may nulls + Nulls.add_all Possibly nulls else let (must, mays) = Nulls.add_interval Possibly (min_may_null, Z.pred n) nulls in (must, mays |> MaySet.M.filter (Z.gt n)) (* TODO: this makes little sense *) @@ -1372,15 +1374,15 @@ struct let must_nulls_set_result = let min_size2 = BatOption.default Z.zero (Idx.minimal truncatedsize) in (* get must nulls from src string < minimal size of dest *) - MustSet.filter (Z.gt min_dstsize) must_nulls_set2' min_size2 + MustSet.filter ~min_size:min_size2 (Z.gt min_dstsize) must_nulls_set2' (* and keep indexes of dest >= maximal strlen of src *) - |> MustSet.union (MustSet.filter (Z.leq max_srclen) must_nulls_set1 min_dstsize) in + |> MustSet.union (MustSet.filter ~min_size:min_dstsize (Z.leq max_srclen) must_nulls_set1) in let may_nulls_set_result = let max_size2 = BatOption.default max_dstsize (idx_maximal truncatedsize) in (* get may nulls from src string < maximal size of dest *) - MaySet.filter (Z.gt max_dstsize) may_nulls_set2' max_size2 + MaySet.filter ~max_size: max_size2 (Z.gt max_dstsize) may_nulls_set2' (* and keep indexes of dest >= minimal strlen of src *) - |> MaySet.union (MaySet.filter (Z.leq min_srclen) may_nulls_set1 max_dstsize) in + |> MaySet.union (MaySet.filter ~max_size:max_dstsize (Z.leq min_srclen) may_nulls_set1) in ((must_nulls_set_result, may_nulls_set_result), dstsize) @@ -1389,12 +1391,12 @@ struct warn_past_end "The length of string src may be greater than the allocated size for dest"); let must_nulls_set_result = let min_size2 = BatOption.default Z.zero (Idx.minimal truncatedsize) in - MustSet.filter (Z.gt min_size1) must_nulls_set2' min_size2 - |> MustSet.union (MustSet.filter (Z.leq max_len2) must_nulls_set1 min_size1) in + MustSet.filter ~min_size: min_size2 (Z.gt min_size1) must_nulls_set2' + |> MustSet.union (MustSet.filter ~min_size:min_size1 (Z.leq max_len2) must_nulls_set1) in let may_nulls_set_result = (* get all may nulls from src string as no maximal size of dest *) may_nulls_set2' - |> MaySet.union (MaySet.filter (Z.leq min_len2) may_nulls_set1 (Z.succ min_len2)) in + |> MaySet.union (MaySet.filter ~max_size:(Z.succ min_len2) (Z.leq min_len2) may_nulls_set1) in ((must_nulls_set_result, may_nulls_set_result), dstsize) | Some min_size1, Some max_size1, Some min_len2, None -> (if max_size1 <. min_len2 then @@ -1404,11 +1406,11 @@ struct (* do not keep any index of dest as no maximal strlen of src *) let must_nulls_set_result = let min_size2 = BatOption.default Z.zero (Idx.minimal truncatedsize) in - MustSet.filter (Z.gt min_size1) must_nulls_set2' min_size2 in + MustSet.filter ~min_size:min_size2 (Z.gt min_size1) must_nulls_set2' in let may_nulls_set_result = let max_size2 = BatOption.default max_size1 (idx_maximal truncatedsize) in - MaySet.filter (Z.gt max_size1) may_nulls_set2' max_size2 - |> MaySet.union (MaySet.filter (Z.leq min_len2) may_nulls_set1 max_size1) in + MaySet.filter ~max_size:max_size2 (Z.gt max_size1) may_nulls_set2' + |> MaySet.union (MaySet.filter ~max_size:max_size1 (Z.leq min_len2) may_nulls_set1) in ((must_nulls_set_result, may_nulls_set_result), dstsize) | Some min_size1, None, Some min_len2, None -> (if min_size1 <. min_len2 then @@ -1416,11 +1418,11 @@ struct (* do not keep any index of dest as no maximal strlen of src *) let must_nulls_set_result = let min_size2 = BatOption.default Z.zero (Idx.minimal truncatedsize) in - MustSet.filter (Z.gt min_size1) must_nulls_set2' min_size2 in + MustSet.filter ~min_size:min_size2 (Z.gt min_size1) must_nulls_set2' in let may_nulls_set_result = (* get all may nulls from src string as no maximal size of dest *) may_nulls_set2' - |> MaySet.union (MaySet.filter (Z.leq min_len2) may_nulls_set1 (Z.succ min_len2)) in + |> MaySet.union (MaySet.filter ~max_size:(Z.succ min_len2) (Z.leq min_len2) may_nulls_set1) in ((must_nulls_set_result, may_nulls_set_result), dstsize) (* any other case shouldn't happen as minimal index is always >= 0 *) | _ -> (Nulls.top (), dstsize) in @@ -1479,13 +1481,13 @@ struct let (must_nulls_set2', may_nulls_set2') = nulls2' in let may_nulls_set_result = if max_size1_exists then - MaySet.filter (fun x -> if maxlen1_exists && maxlen2_exists then x <=. (maxlen1 +. maxlen2) else true) may_nulls_set1 max_size1 + MaySet.filter ~max_size:max_size1 (fun x -> if maxlen1_exists && maxlen2_exists then x <=. (maxlen1 +. maxlen2) else true) may_nulls_set1 |> MaySet.elements (* if may_nulls_set2' is top, limit it to max_size1 *) - |> BatList.cartesian_product (MaySet.elements (MaySet.filter (fun x -> true) may_nulls_set2' max_size1)) + |> BatList.cartesian_product (MaySet.elements (MaySet.filter ~max_size:max_size1 (fun x -> true) may_nulls_set2')) |> List.map (fun (i1, i2) -> i1 +. i2) |> MaySet.of_list - |> MaySet.union (MaySet.filter (Z.lt (minlen1 +. minlen2)) may_nulls_set1 max_size1) + |> MaySet.union (MaySet.filter ~max_size:max_size1 (Z.lt (minlen1 +. minlen2)) may_nulls_set1) |> MaySet.M.filter (Z.gt max_size1) else if not (MaySet.is_top may_nulls_set1) && not (MaySet.is_top may_nulls_set2') && maxlen1_exists && maxlen2_exists then MaySet.M.filter (fun x -> if maxlen1_exists && maxlen2_exists then x <=. (maxlen1 +. maxlen2) else true) may_nulls_set1 @@ -1504,12 +1506,12 @@ struct let min_i = min_i1 +. min_i2 in let (must_nulls_set1, may_nulls_set1) = nulls1 in let must_nulls_set_result = - MustSet.filter (Z.lt min_i) must_nulls_set1 min_size1 + MustSet.filter ~min_size:min_size1 (Z.lt min_i) must_nulls_set1 |> MustSet.add min_i |> MustSet.M.filter (Z.gt min_size1) in let may_nulls_set_result = if max_size1_exists then - MaySet.filter (Z.lt min_i) may_nulls_set1 max_size1 + MaySet.filter ~max_size:max_size1 (Z.lt min_i) may_nulls_set1 |> MaySet.add min_i |> MaySet.M.filter (fun x -> if max_size1_exists then max_size1 >. x else true) else @@ -1522,17 +1524,17 @@ struct let (must_nulls_set2', may_nulls_set2') = nulls2' in let may_nulls_set2'_until_min_i2 = match idx_maximal size2 with - | Some max_size2 -> MaySet.filter (Z.geq min_i2) may_nulls_set2' max_size2 - | None -> MaySet.filter (Z.geq min_i2) may_nulls_set2' (Z.succ min_i2) in - let must_nulls_set_result = MustSet.filter (fun x -> if maxlen1_exists && maxlen2_exists then (maxlen1 +. maxlen2) <. x else false) must_nulls_set1 min_size1 in + | Some max_size2 -> MaySet.filter ~max_size:max_size2 (Z.geq min_i2) may_nulls_set2' + | None -> MaySet.filter ~max_size:(Z.succ min_i2) (Z.geq min_i2) may_nulls_set2' in + let must_nulls_set_result = MustSet.filter ~min_size:min_size1 (fun x -> if maxlen1_exists && maxlen2_exists then (maxlen1 +. maxlen2) <. x else false) must_nulls_set1 in let may_nulls_set_result = if max_size1_exists then - MaySet.filter (fun x -> if maxlen1_exists && maxlen2_exists then x <=. (maxlen1 +. maxlen2) else true) may_nulls_set1 max_size1 + MaySet.filter ~max_size:max_size1 (fun x -> if maxlen1_exists && maxlen2_exists then x <=. (maxlen1 +. maxlen2) else true) may_nulls_set1 |> MaySet.elements |> BatList.cartesian_product (MaySet.elements may_nulls_set2'_until_min_i2) |> List.map (fun (i1, i2) -> i1 +. i2) |> MaySet.of_list - |> MaySet.union (MaySet.filter (Z.lt (minlen1 +. minlen2)) may_nulls_set1 max_size1) + |> MaySet.union (MaySet.filter ~max_size:max_size1 (Z.lt (minlen1 +. minlen2)) may_nulls_set1) |> MaySet.M.filter (fun x -> if max_size1_exists then max_size1 >. x else true) else if not (MaySet.is_top may_nulls_set1) then MaySet.M.filter (fun x -> if maxlen1_exists && maxlen2_exists then x <=. (maxlen1 +. maxlen2) else true) may_nulls_set1 @@ -1585,11 +1587,11 @@ struct Nulls.precise_singleton n else if not (Nulls.exists Definitely (Z.gt n) nulls2) then let max_size2 = BatOption.default (Z.succ n) (idx_maximal size2) in - (MustSet.empty (), MaySet.add n (MaySet.filter (Z.geq n) may_nulls_set2 max_size2)) + (MustSet.empty (), MaySet.add n (MaySet.filter ~max_size:max_size2 (Z.geq n) may_nulls_set2)) else let min_size2 = BatOption.default Z.zero (Idx.minimal size2) in let max_size2 = BatOption.default n (idx_maximal size2) in - (MustSet.filter (Z.gt n) must_nulls_set2 min_size2, MaySet.filter (Z.gt n) may_nulls_set2 max_size2) + (MustSet.filter ~min_size: min_size2 (Z.gt n) must_nulls_set2, MaySet.filter ~max_size:max_size2 (Z.gt n) may_nulls_set2) in compute_concat nulls2' | _ -> (Nulls.top (), size1) diff --git a/src/cdomains/nullByteSet.ml b/src/cdomains/nullByteSet.ml index b1580d5717..b704b9fee0 100644 --- a/src/cdomains/nullByteSet.ml +++ b/src/cdomains/nullByteSet.ml @@ -12,9 +12,11 @@ module MustSet = struct else M.remove i must_nulls_set - let filter cond must_nulls_set min_size = + let filter ?min_size cond must_nulls_set = if M.is_bot must_nulls_set then - M.filter cond (compute_set min_size) + match min_size with + | Some min_size -> M.filter cond (compute_set min_size) + | _ -> M.empty () else M.filter cond must_nulls_set @@ -50,9 +52,11 @@ module MaySet = struct else M.remove i may_nulls_set - let filter cond may_nulls_set max_size = + let filter ?max_size cond may_nulls_set = if M.is_top may_nulls_set then - M.filter cond (MustSet.compute_set max_size) + match max_size with + | Some max_size -> M.filter cond (MustSet.compute_set max_size) + | _ -> may_nulls_set else M.filter cond may_nulls_set @@ -68,6 +72,8 @@ module MustMaySet = struct type mode = Definitely | Possibly + let empty () = (MustSet.top (), MaySet.bot ()) + let is_empty mode (musts, mays) = match mode with | Definitely -> MaySet.is_empty mays @@ -124,7 +130,7 @@ module MustMaySet = struct if Z.equal l Z.zero && Z.geq u min_size then (MustSet.top (), mays) else - (MustSet.filter (fun x -> (Z.lt x l || Z.gt x u) && Z.lt x min_size) musts min_size, mays) + (MustSet.filter ~min_size (fun x -> (Z.lt x l || Z.gt x u) && Z.lt x min_size) musts, mays) let add_all mode (musts, mays) = match mode with @@ -133,8 +139,8 @@ module MustMaySet = struct let remove_all mode (musts, mays) = match mode with - | Definitely -> (MustSet.top (), mays) - | Possibly -> failwith "todo" + | Possibly -> (MustSet.top (), mays) + | Definitely -> empty () let is_full_set mode (musts, mays) = match mode with @@ -152,14 +158,16 @@ module MustMaySet = struct let precise_set s = (s,s) let make_all_must () = (MustSet.bot (), MaySet.top ()) - let empty () = (MustSet.top (), MaySet.bot ()) + + let may_can_benefit_from_filter (musts, mays) = not (MaySet.is_top mays) let exists mode f (musts, mays) = match mode with | Definitely -> MustSet.exists f musts | Possibly -> MaySet.exists f mays - let forget_may (musts, mays) = (musts, MaySet.top ()) - let forget_must (musts, mays) = (MustSet.top (), mays) - let filter_musts f min_size (musts, mays) = (MustSet.filter f musts min_size, mays) + let filter ?min_size ?max_size f (must, mays):t = + (MustSet.filter ?min_size f must, MaySet.filter ?max_size f mays) + + let filter_musts f min_size (musts, mays) = (MustSet.filter ~min_size f musts, mays) end From df10ad6dc5c03b547c743ee81dc91808863895e2 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 26 Nov 2023 21:01:38 +0100 Subject: [PATCH 1143/1312] Move to operations on Nulls --- src/cdomains/arrayDomain.ml | 48 +++++++++++++++++++++---------------- src/cdomains/nullByteSet.ml | 19 +++++++++++++++ 2 files changed, 46 insertions(+), 21 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 813a69d47f..8f966d0fad 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1380,7 +1380,7 @@ struct let may_nulls_set_result = let max_size2 = BatOption.default max_dstsize (idx_maximal truncatedsize) in (* get may nulls from src string < maximal size of dest *) - MaySet.filter ~max_size: max_size2 (Z.gt max_dstsize) may_nulls_set2' + MaySet.filter ~max_size:max_size2 (Z.gt max_dstsize) may_nulls_set2' (* and keep indexes of dest >= minimal strlen of src *) |> MaySet.union (MaySet.filter ~max_size:max_dstsize (Z.leq min_srclen) may_nulls_set1) in ((must_nulls_set_result, may_nulls_set_result), dstsize) @@ -1477,28 +1477,34 @@ struct * for all i1, i2 in may_nulls_set1, may_nulls_set2: add i1 + i2 if it is <= strlen(dest) + strlen(src) to new may_nulls_set * and keep indexes > minimal strlen(dest) + strlen(src) of may_nulls_set *) if Nulls.is_empty Possibly nulls1 || Nulls.is_empty Possibly nulls2 then - let (must_nulls_set1, may_nulls_set1) = nulls1 in - let (must_nulls_set2', may_nulls_set2') = nulls2' in - let may_nulls_set_result = - if max_size1_exists then - MaySet.filter ~max_size:max_size1 (fun x -> if maxlen1_exists && maxlen2_exists then x <=. (maxlen1 +. maxlen2) else true) may_nulls_set1 - |> MaySet.elements - (* if may_nulls_set2' is top, limit it to max_size1 *) - |> BatList.cartesian_product (MaySet.elements (MaySet.filter ~max_size:max_size1 (fun x -> true) may_nulls_set2')) + if max_size1_exists then + let nulls1_no_must = Nulls.remove_all Possibly nulls1 in + let r = + nulls1_no_must + (* filter ensures we have the concete representation *) + |> Nulls.filter ~max_size:max_size1 (fun x -> if maxlen1_exists && maxlen2_exists then x <=. (maxlen1 +. maxlen2) else true) + |> Nulls.elements ~max_size:max_size1 Possibly + |> BatList.cartesian_product (Nulls.elements ~max_size:max_size1 Possibly nulls2') |> List.map (fun (i1, i2) -> i1 +. i2) - |> MaySet.of_list - |> MaySet.union (MaySet.filter ~max_size:max_size1 (Z.lt (minlen1 +. minlen2)) may_nulls_set1) - |> MaySet.M.filter (Z.gt max_size1) - else if not (MaySet.is_top may_nulls_set1) && not (MaySet.is_top may_nulls_set2') && maxlen1_exists && maxlen2_exists then - MaySet.M.filter (fun x -> if maxlen1_exists && maxlen2_exists then x <=. (maxlen1 +. maxlen2) else true) may_nulls_set1 - |> MaySet.elements - |> BatList.cartesian_product (MaySet.elements may_nulls_set2') + |> (fun x -> Nulls.add_list Possibly x (Nulls.filter ~max_size:max_size1 (Z.lt (minlen1 +. minlen2)) nulls1_no_must)) + |> Nulls.filter (Z.gt max_size1) + in + (r, size1) + else if Nulls.may_can_benefit_from_filter nulls1 && Nulls.may_can_benefit_from_filter nulls2 && maxlen1_exists && maxlen2_exists then + let nulls1_no_must = Nulls.remove_all Possibly nulls1 in + let r = + nulls1_no_must + (* filter ensures we have the concete representation *) + |> Nulls.filter (fun x -> x <=. (maxlen1 +. maxlen2)) + |> Nulls.elements Possibly + |> BatList.cartesian_product (Nulls.elements Possibly nulls2') |> List.map (fun (i1, i2) -> i1 +. i2) - |> MaySet.of_list - |> MaySet.union (MaySet.M.filter (Z.lt (minlen1 +. minlen2)) may_nulls_set1) - else - MaySet.top () in - ((MustSet.top (), may_nulls_set_result), size1) + |> (fun x -> Nulls.add_list Possibly x (Nulls.filter (Z.lt (minlen1 +. minlen2)) nulls1_no_must)) + in + (r, size1) + else + (Nulls.top (), size1) + (* if minimal must null = minimal may null in ar1 and ar2, add them together and keep indexes > strlen(dest) + strlen(src) of ar1 *) else if Nulls.min_elem_precise (nulls1) && Nulls.min_elem_precise nulls2' then let min_i1 = Nulls.min_elem Definitely nulls1 in diff --git a/src/cdomains/nullByteSet.ml b/src/cdomains/nullByteSet.ml index b704b9fee0..54284f6ab5 100644 --- a/src/cdomains/nullByteSet.ml +++ b/src/cdomains/nullByteSet.ml @@ -46,6 +46,14 @@ module MaySet = struct module M = SetDomain.ToppedSet (IntDomain.BigInt) (struct let topname = "All Null" end) include M + let elements ?max_size may_nulls_set = + if M.is_top may_nulls_set then + match max_size with + | Some max_size -> M.elements @@ MustSet.compute_set max_size + | _ -> failwith "top and no max size supplied" + else + M.elements may_nulls_set + let remove i may_nulls_set max_size = if M.is_top may_nulls_set then M.remove i (MustSet.compute_set max_size) @@ -107,6 +115,11 @@ module MustMaySet = struct | Definitely -> (MustSet.add i musts, MaySet.add i mays) | Possibly -> (musts, MaySet.add i mays) + let add_list mode l (musts, mays) = + match mode with + | Definitely -> failwith "todo" + | Possibly -> (musts, MaySet.union (MaySet.of_list l) mays) + let add_interval ?maxfull mode (l,u) (musts, mays) = match mode with | Definitely -> failwith "todo" @@ -152,6 +165,12 @@ module MustMaySet = struct | Definitely -> musts | Possibly -> mays + let elements ?max_size ?min_size mode (musts, mays) = + match mode with + | Definitely ->failwith "todo" + | Possibly -> MaySet.elements ?max_size mays + + let precise_singleton i = (MustSet.singleton i, MaySet.singleton i) From ed5f25947405096d5b1851084c230aa2ccf87cf6 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 27 Nov 2023 10:16:41 +0200 Subject: [PATCH 1144/1312] Revert "Disable pins for v2.3.0 release" This reverts commit dbd6479a53dbf76f351f853bbc9092d659a8a631. --- goblint.opam | 6 +++--- goblint.opam.locked | 7 +++++++ goblint.opam.template | 6 +++--- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/goblint.opam b/goblint.opam index 842c03933f..669b2d9c40 100644 --- a/goblint.opam +++ b/goblint.opam @@ -74,12 +74,12 @@ dev-repo: "git+https://github.com/goblint/analyzer.git" # on `dune build` goblint.opam will be generated from goblint.opam.template and dune-project # also remember to generate/adjust goblint.opam.locked! available: os-distribution != "alpine" & arch != "arm64" -# pin-depends: [ +pin-depends: [ # published goblint-cil 2.0.3 is currently up-to-date, so no pin needed # [ "goblint-cil.2.0.3" "git+https://github.com/goblint/cil.git#d2760bacfbfdb25a374254de44f2ff1cb5f42abd" ] # TODO: add back after release, only pinned for optimization (https://github.com/ocaml-ppx/ppx_deriving/pull/252) - # [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] -# ] + [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] +] post-messages: [ "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} ] diff --git a/goblint.opam.locked b/goblint.opam.locked index aba9f38bda..02eac0bb75 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -128,3 +128,10 @@ conflicts: [ post-messages: [ "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} ] +# TODO: manually reordered to avoid opam pin crash: https://github.com/ocaml/opam/issues/4936 +pin-depends: [ + [ + "ppx_deriving.5.2.1" + "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" + ] +] diff --git a/goblint.opam.template b/goblint.opam.template index 95f90bcbd1..ca2796b3c7 100644 --- a/goblint.opam.template +++ b/goblint.opam.template @@ -1,12 +1,12 @@ # on `dune build` goblint.opam will be generated from goblint.opam.template and dune-project # also remember to generate/adjust goblint.opam.locked! available: os-distribution != "alpine" & arch != "arm64" -# pin-depends: [ +pin-depends: [ # published goblint-cil 2.0.3 is currently up-to-date, so no pin needed # [ "goblint-cil.2.0.3" "git+https://github.com/goblint/cil.git#d2760bacfbfdb25a374254de44f2ff1cb5f42abd" ] # TODO: add back after release, only pinned for optimization (https://github.com/ocaml-ppx/ppx_deriving/pull/252) - # [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] -# ] + [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] +] post-messages: [ "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} ] From 6d4f9e78f219edbf81563ac0a37d6fa147fd4bab Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 27 Nov 2023 10:20:36 +0200 Subject: [PATCH 1145/1312] Add opam pin revert step to releasing guide --- docs/developer-guide/releasing.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/developer-guide/releasing.md b/docs/developer-guide/releasing.md index 4f49399f13..fcf69ea533 100644 --- a/docs/developer-guide/releasing.md +++ b/docs/developer-guide/releasing.md @@ -57,6 +57,7 @@ 15. Create an opam package: `dune-release opam pkg`. 16. Submit the opam package to opam-repository: `dune-release opam submit`. +17. Revert temporary removal of opam pins. ## SV-COMP From 975b502b684c01d4f988ba62a20d4a64d7b52b36 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 27 Nov 2023 10:27:36 +0200 Subject: [PATCH 1146/1312] Add few people to .mailmap --- .mailmap | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.mailmap b/.mailmap index 9153d55765..9aa2d0cc02 100644 --- a/.mailmap +++ b/.mailmap @@ -23,6 +23,7 @@ Kerem Çakırer Sarah Tilscher <66023521+stilscher@users.noreply.github.com> Karoliine Holter <44437975+karoliineh@users.noreply.github.com> + Elias Brandstetter <15275491+superbr4in@users.noreply.github.com> wherekonshade <80516286+Wherekonshade@users.noreply.github.com> @@ -37,3 +38,6 @@ Mireia Cano Pujol Felix Krayer Felix Krayer <91671586+FelixKrayer@users.noreply.github.com> Manuel Pietsch +Tim Ortel <100865202+TimOrtel@users.noreply.github.com> +Tomáš Dacík + <43824605+TDacik@users.noreply.github.com> From 0d5e145c66365d9f7196a17acddf7eceada00a0c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 27 Nov 2023 10:42:44 +0200 Subject: [PATCH 1147/1312] Refactor StringDomain to use ResettableLazy --- src/cdomains/stringDomain.ml | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/cdomains/stringDomain.ml b/src/cdomains/stringDomain.ml index 978482a503..0621f37eb6 100644 --- a/src/cdomains/stringDomain.ml +++ b/src/cdomains/stringDomain.ml @@ -4,23 +4,20 @@ let name () = "string" type string_domain = Unit | Disjoint | Flat -let string_domain = ref None +let string_domain: string_domain ResettableLazy.t = + ResettableLazy.from_fun (fun () -> + match GobConfig.get_string "ana.base.strings.domain" with + | "unit" -> Unit + | "disjoint" -> Disjoint + | "flat" -> Flat + | _ -> failwith "ana.base.strings.domain: illegal value" + ) -let string_domain_config = "ana.base.strings.domain" - -let parse config = match config with - | "unit" -> Unit - | "disjoint" -> Disjoint - | "flat" -> Flat - | _ -> raise @@ GobConfig.ConfigError ("Invalid option for " ^ string_domain_config) - -let get_string_domain () = - if !string_domain = None then - string_domain := Some (parse (GobConfig.get_string string_domain_config)); - Option.get !string_domain +let get_string_domain () = ResettableLazy.force string_domain let reset_lazy () = - string_domain := None + ResettableLazy.reset string_domain + type t = string option [@@deriving eq, ord, hash] From 717b6a85cb99521936a1d7e3b5c952dcfafb43af Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 27 Nov 2023 12:18:53 +0200 Subject: [PATCH 1148/1312] Add pthread_join_N to Klever library --- src/analyses/libraryFunctions.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index c9afb83617..b94f751568 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -1008,6 +1008,7 @@ let rtnl_lock = AddrOf (Cil.var (Cilfacade.create_var (makeGlobalVar "[rtnl_lock (** LDV Klever functions. *) let klever_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("pthread_create_N", special [__ "thread" [w]; drop "attr" [r]; __ "start_routine" [s]; __ "arg" []] @@ fun thread start_routine arg -> ThreadCreate { thread; start_routine; arg }); (* TODO: add multiple flag to ThreadCreate *) + ("pthread_join_N", special [__ "thread" []; __ "retval" [w]] @@ fun thread retval -> ThreadJoin {thread; ret_var = retval}); ("ldv_mutex_model_lock", special [__ "lock" []; drop "sign" []] @@ fun lock -> Lock { lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true }); ("ldv_mutex_model_unlock", special [__ "lock" []; drop "sign" []] @@ fun lock -> Unlock lock); ("ldv_spin_model_lock", unknown [drop "sign" []]); From 670e7cfe1665a527ce1c124bad8494e30d452bd4 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 27 Nov 2023 12:23:06 +0200 Subject: [PATCH 1149/1312] Add test for Klever's multiple threads --- .../51-threadjoins/07-klever-multiple.c | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 tests/regression/51-threadjoins/07-klever-multiple.c diff --git a/tests/regression/51-threadjoins/07-klever-multiple.c b/tests/regression/51-threadjoins/07-klever-multiple.c new file mode 100644 index 0000000000..24b2c0b1ca --- /dev/null +++ b/tests/regression/51-threadjoins/07-klever-multiple.c @@ -0,0 +1,24 @@ +//PARAM: --set ana.activated[+] threadJoins --set lib.activated[+] klever +#include +#include + +int g = 0; + +void *t_fun(void *arg) { + g++; // RACE! + return NULL; +} + +int main() { + pthread_t id; + pthread_create_N(&id, NULL, t_fun, NULL); // spawns multiple threads + pthread_join(id, NULL); + + g++; // RACE! + + pthread_join_N(id, NULL); // TODO: should this join one (do nothing) or all (like assume join)? + + g++; // RACE! + + return 0; +} From 8a95c8a2d4fa776624da179a803c021058563577 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 27 Nov 2023 12:29:07 +0200 Subject: [PATCH 1150/1312] Add multiple flag to ThreadCreate --- src/analyses/base.ml | 22 +++++++++++----------- src/analyses/libraryDesc.ml | 2 +- src/analyses/libraryFunctions.ml | 4 ++-- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index bdae887b4a..3630395282 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1953,8 +1953,8 @@ struct - let forkfun (ctx:(D.t, G.t, C.t, V.t) Analyses.ctx) (lv: lval option) (f: varinfo) (args: exp list) : (lval option * varinfo * exp list) list * bool = - let create_thread lval arg v = + let forkfun (ctx:(D.t, G.t, C.t, V.t) Analyses.ctx) (lv: lval option) (f: varinfo) (args: exp list) : (lval option * varinfo * exp list * bool) list = + let create_thread ~multiple lval arg v = try (* try to get function declaration *) let fd = Cilfacade.find_varinfo_fundec v in @@ -1963,7 +1963,7 @@ struct | Some x -> [x] | None -> List.map (fun x -> MyCFG.unknown_exp) fd.sformals in - Some (lval, v, args) + Some (lval, v, args, multiple) with Not_found -> if LF.use_special f.vname then None (* we handle this function *) else if isFunctionType v.vtype then @@ -1973,7 +1973,7 @@ struct | Some x -> [x] | None -> List.map (fun x -> MyCFG.unknown_exp) (Cil.argsToList v_args) in - Some (lval, v, args) + Some (lval, v, args, multiple) else ( M.debug ~category:Analyzer "Not creating a thread from %s because its type is %a" v.vname d_type v.vtype; None @@ -1982,7 +1982,7 @@ struct let desc = LF.find f in match desc.special args, f.vname with (* handling thread creations *) - | ThreadCreate { thread = id; start_routine = start; arg = ptc_arg }, _ -> begin + | ThreadCreate { thread = id; start_routine = start; arg = ptc_arg; multiple }, _ -> begin (* extra sync so that we do not analyze new threads with bottom global invariant *) publish_all ctx `Thread; (* Collect the threads. *) @@ -1994,7 +1994,7 @@ struct else start_funvars in - List.filter_map (create_thread (Some (Mem id, NoOffset)) (Some ptc_arg)) start_funvars_with_unknown, false + List.filter_map (create_thread ~multiple (Some (Mem id, NoOffset)) (Some ptc_arg)) start_funvars_with_unknown end | _, _ when get_bool "sem.unknown_function.spawn" -> (* TODO: Remove sem.unknown_function.spawn check because it is (and should be) really done in LibraryFunctions. @@ -2008,8 +2008,8 @@ struct let flist = shallow_flist @ deep_flist in let addrs = List.concat_map AD.to_var_may flist in if addrs <> [] then M.debug ~category:Analyzer "Spawning non-unique functions from unknown function: %a" (d_list ", " CilType.Varinfo.pretty) addrs; - List.filter_map (create_thread None None) addrs, true - | _, _ -> [], false + List.filter_map (create_thread ~multiple:true None None) addrs + | _, _ -> [] let assert_fn ctx e refine = (* make the state meet the assertion in the rest of the code *) @@ -2140,9 +2140,9 @@ struct let addr = eval_lv (Analyses.ask_of_ctx ctx) ctx.global ctx.local lval in (addr, AD.type_of addr) in - let forks, multiple = forkfun ctx lv f args in - if M.tracing then if not (List.is_empty forks) then M.tracel "spawn" "Base.special %s: spawning functions %a\n" f.vname (d_list "," CilType.Varinfo.pretty) (List.map BatTuple.Tuple3.second forks); - List.iter (BatTuple.Tuple3.uncurry (ctx.spawn ~multiple)) forks; + let forks = forkfun ctx lv f args in + if M.tracing then if not (List.is_empty forks) then M.tracel "spawn" "Base.special %s: spawning functions %a\n" f.vname (d_list "," CilType.Varinfo.pretty) (List.map BatTuple.Tuple4.second forks); + List.iter (fun (lval, f, args, multiple) -> ctx.spawn ~multiple lval f args) forks; let st: store = ctx.local in let gs = ctx.global in let desc = LF.find f in diff --git a/src/analyses/libraryDesc.ml b/src/analyses/libraryDesc.ml index 4997b306a9..e426c32235 100644 --- a/src/analyses/libraryDesc.ml +++ b/src/analyses/libraryDesc.ml @@ -53,7 +53,7 @@ type special = | Assert of { exp: Cil.exp; check: bool; refine: bool; } | Lock of { lock: Cil.exp; try_: bool; write: bool; return_on_success: bool; } | Unlock of Cil.exp - | ThreadCreate of { thread: Cil.exp; start_routine: Cil.exp; arg: Cil.exp; } + | ThreadCreate of { thread: Cil.exp; start_routine: Cil.exp; arg: Cil.exp; multiple: bool } | ThreadJoin of { thread: Cil.exp; ret_var: Cil.exp; } | ThreadExit of { ret_val: Cil.exp; } | Signal of Cil.exp diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index b94f751568..ab0d04d3ec 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -422,7 +422,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ (** Pthread functions. *) let pthread_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ - ("pthread_create", special [__ "thread" [w]; drop "attr" [r]; __ "start_routine" [s]; __ "arg" []] @@ fun thread start_routine arg -> ThreadCreate { thread; start_routine; arg }); (* For precision purposes arg is not considered accessed here. Instead all accesses (if any) come from actually analyzing start_routine. *) + ("pthread_create", special [__ "thread" [w]; drop "attr" [r]; __ "start_routine" [s]; __ "arg" []] @@ fun thread start_routine arg -> ThreadCreate { thread; start_routine; arg; multiple = false }); (* For precision purposes arg is not considered accessed here. Instead all accesses (if any) come from actually analyzing start_routine. *) ("pthread_exit", special [__ "retval" []] @@ fun retval -> ThreadExit { ret_val = retval }); (* Doesn't dereference the void* itself, but just passes to pthread_join. *) ("pthread_join", special [__ "thread" []; __ "retval" [w]] @@ fun thread retval -> ThreadJoin {thread; ret_var = retval}); ("pthread_kill", unknown [drop "thread" []; drop "sig" []]); @@ -1007,7 +1007,7 @@ let rtnl_lock = AddrOf (Cil.var (Cilfacade.create_var (makeGlobalVar "[rtnl_lock (** LDV Klever functions. *) let klever_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ - ("pthread_create_N", special [__ "thread" [w]; drop "attr" [r]; __ "start_routine" [s]; __ "arg" []] @@ fun thread start_routine arg -> ThreadCreate { thread; start_routine; arg }); (* TODO: add multiple flag to ThreadCreate *) + ("pthread_create_N", special [__ "thread" [w]; drop "attr" [r]; __ "start_routine" [s]; __ "arg" []] @@ fun thread start_routine arg -> ThreadCreate { thread; start_routine; arg; multiple = true }); ("pthread_join_N", special [__ "thread" []; __ "retval" [w]] @@ fun thread retval -> ThreadJoin {thread; ret_var = retval}); ("ldv_mutex_model_lock", special [__ "lock" []; drop "sign" []] @@ fun lock -> Lock { lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true }); ("ldv_mutex_model_unlock", special [__ "lock" []; drop "sign" []] @@ fun lock -> Unlock lock); From 0e64a8f2abff122c78e5fbea2d3f338ee73db7fe Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 27 Nov 2023 12:36:24 +0200 Subject: [PATCH 1151/1312] Fix old indentation in YamlWitness --- src/witness/yamlWitness.ml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index 635ba4ad72..253ee5eecd 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -136,9 +136,9 @@ struct let precondition_loop_invariant_certificate ~target ~(certification): Entry.t = { entry_type = PreconditionLoopInvariantCertificate { - target; - certification; - }; + target; + certification; + }; metadata = metadata (); } end From 778d8838b2ae77a4673869c430b77eb764895ac8 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 27 Nov 2023 12:36:32 +0200 Subject: [PATCH 1152/1312] Fix indentation in MemLeak --- src/analyses/memLeak.ml | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index 05e18e2e39..1253cd6763 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -205,12 +205,12 @@ struct | Malloc _ | Calloc _ | Realloc _ -> - (ctx.sideg () true; + ctx.sideg () true; begin match ctx.ask (Queries.AllocVar {on_stack = false}) with | `Lifted var -> ToppedVarInfoSet.add var state | _ -> state - end) + end | Free ptr -> begin match ctx.ask (Queries.MayPointTo ptr) with | ad when (not (Queries.AD.is_top ad)) && Queries.AD.cardinal ad = 1 -> @@ -233,16 +233,15 @@ struct | a when Queries.ID.is_bot a -> M.warn ~category:Assert "assert expression %a is bottom" d_exp exp | a -> begin match Queries.ID.to_bool a with - | Some b -> ( + | Some b -> (* If we know for sure that the expression in "assert" is false => need to check for memory leaks *) - if b = false then ( - warn_for_multi_threaded_due_to_abort ctx; - check_for_mem_leak ctx - ) - else ()) + if b = false then ( + warn_for_multi_threaded_due_to_abort ctx; + check_for_mem_leak ctx + ) | None -> - (warn_for_multi_threaded_due_to_abort ctx; - check_for_mem_leak ctx ~assert_exp_imprecise:true ~exp:(Some exp)) + warn_for_multi_threaded_due_to_abort ctx; + check_for_mem_leak ctx ~assert_exp_imprecise:true ~exp:(Some exp) end in warn_for_assert_exp; From 00e1685c0af89cf5f6c3a968211e1d1d4bb3081d Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 27 Nov 2023 13:12:21 +0200 Subject: [PATCH 1153/1312] Generalize abs invariant in base --- src/analyses/baseInvariant.ml | 36 ++++++++--------------------------- 1 file changed, 8 insertions(+), 28 deletions(-) diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index f391231628..0e02d38f6f 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -726,9 +726,16 @@ struct | `Lifted (Isless (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))) st | `Lifted (Islessequal (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Le, xFloat, yFloat, (typeOf xFloat))) st | `Lifted (Islessgreater (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (LOr, (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))), (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))), (TInt (IBool, [])))) st + | `Lifted (Abs (_ik, xInt)) -> + inv_exp (Int (ID.join c (ID.neg c))) xInt st (* TODO: deduplicate *) + | _ -> update_lval c x c' ID.pretty + end + | None -> + begin match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs)) with + | `Lifted (Abs (_ik, xInt)) -> + inv_exp (Int (ID.join c (ID.neg c))) xInt st (* TODO: deduplicate *) | _ -> update_lval c x c' ID.pretty end - | None -> update_lval c x c' ID.pretty end | _ -> update_lval c x c' ID.pretty end @@ -821,31 +828,4 @@ struct FD.top_of fk in inv_exp (Float ftv) exp st - - let invariant ctx a gs st exp tv: D.t = - let refine0 = invariant ctx a gs st exp tv in - (* bodge for abs(...); To be removed once we have a clean solution *) - let refineAbs op absargexp valexp = - let flip op = match op with | Le -> Ge | Lt -> Gt | _ -> failwith "impossible" in - (* e.g. |arg| <= 40 *) - (* arg <= e (arg <= 40) *) - let le = BinOp (op, absargexp, valexp, intType) in - (* arg >= -e (arg >= -40) *) - let gt = BinOp(flip op, absargexp, UnOp (Neg, valexp, Cilfacade.typeOf valexp), intType) in - let one = invariant ctx (Analyses.ask_of_ctx ctx) ctx.global refine0 le tv in - invariant ctx (Analyses.ask_of_ctx ctx) ctx.global one gt tv - in - match exp with - | BinOp ((Lt|Le) as op, CastE(t, Lval (Var v, NoOffset)), e,_) when tv -> - begin match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil NoOffset)) with - | `Lifted (Abs (ik, arg)) -> refineAbs op (CastE (t, arg)) e - | _ -> refine0 - end - | BinOp ((Lt|Le) as op, Lval (Var v, NoOffset), e, _) when tv -> - begin match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil NoOffset)) with - | `Lifted (Abs (ik, arg)) -> refineAbs op arg e - | _ -> refine0 - end - | _ -> refine0 - end From d3b73fa4d1bed6574227007b1cd54778368daa6b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 27 Nov 2023 13:28:19 +0200 Subject: [PATCH 1154/1312] Deduplicate Abs cases in BaseInvariant --- src/analyses/baseInvariant.ml | 40 +++++++++++++++++------------------ 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index 0e02d38f6f..974439d826 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -714,27 +714,25 @@ struct begin match x with | ((Var v), offs) -> if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs))); - let tv_opt = ID.to_bool c in - begin match tv_opt with - | Some tv -> - begin match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs)) with - | `Lifted (Isfinite xFloat) when tv -> inv_exp (Float (FD.finite (unroll_fk_of_exp xFloat))) xFloat st - | `Lifted (Isnan xFloat) when tv -> inv_exp (Float (FD.nan_of (unroll_fk_of_exp xFloat))) xFloat st - (* should be correct according to C99 standard*) - | `Lifted (Isgreater (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))) st - | `Lifted (Isgreaterequal (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Ge, xFloat, yFloat, (typeOf xFloat))) st - | `Lifted (Isless (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))) st - | `Lifted (Islessequal (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Le, xFloat, yFloat, (typeOf xFloat))) st - | `Lifted (Islessgreater (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (LOr, (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))), (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))), (TInt (IBool, [])))) st - | `Lifted (Abs (_ik, xInt)) -> - inv_exp (Int (ID.join c (ID.neg c))) xInt st (* TODO: deduplicate *) - | _ -> update_lval c x c' ID.pretty - end - | None -> - begin match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs)) with - | `Lifted (Abs (_ik, xInt)) -> - inv_exp (Int (ID.join c (ID.neg c))) xInt st (* TODO: deduplicate *) - | _ -> update_lval c x c' ID.pretty + begin match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs)) with + | `Lifted (Abs (_ik, xInt)) -> + inv_exp (Int (ID.join c (ID.neg c))) xInt st (* TODO: deduplicate *) + | tmpSpecial -> + let tv_opt = ID.to_bool c in (* TODO: simplify *) + begin match tv_opt with + | Some tv -> + begin match tmpSpecial with + | `Lifted (Isfinite xFloat) when tv -> inv_exp (Float (FD.finite (unroll_fk_of_exp xFloat))) xFloat st + | `Lifted (Isnan xFloat) when tv -> inv_exp (Float (FD.nan_of (unroll_fk_of_exp xFloat))) xFloat st + (* should be correct according to C99 standard*) + | `Lifted (Isgreater (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))) st + | `Lifted (Isgreaterequal (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Ge, xFloat, yFloat, (typeOf xFloat))) st + | `Lifted (Isless (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))) st + | `Lifted (Islessequal (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Le, xFloat, yFloat, (typeOf xFloat))) st + | `Lifted (Islessgreater (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (LOr, (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))), (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))), (TInt (IBool, [])))) st + | _ -> update_lval c x c' ID.pretty + end + | None -> update_lval c x c' ID.pretty end end | _ -> update_lval c x c' ID.pretty From a82266729858c7e67c16348d8798ff0a35c3ee31 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 27 Nov 2023 13:32:14 +0200 Subject: [PATCH 1155/1312] Reduce tmpSpecial nested matching in BaseInvariant --- src/analyses/baseInvariant.ml | 76 ++++++++++++++++------------------- 1 file changed, 34 insertions(+), 42 deletions(-) diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index 974439d826..dc4dff540a 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -709,35 +709,31 @@ struct | _ -> Int c in (* handle special calls *) - begin match t with - | TInt (ik, _) -> - begin match x with - | ((Var v), offs) -> - if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs))); - begin match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs)) with - | `Lifted (Abs (_ik, xInt)) -> - inv_exp (Int (ID.join c (ID.neg c))) xInt st (* TODO: deduplicate *) - | tmpSpecial -> - let tv_opt = ID.to_bool c in (* TODO: simplify *) - begin match tv_opt with - | Some tv -> - begin match tmpSpecial with - | `Lifted (Isfinite xFloat) when tv -> inv_exp (Float (FD.finite (unroll_fk_of_exp xFloat))) xFloat st - | `Lifted (Isnan xFloat) when tv -> inv_exp (Float (FD.nan_of (unroll_fk_of_exp xFloat))) xFloat st - (* should be correct according to C99 standard*) - | `Lifted (Isgreater (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))) st - | `Lifted (Isgreaterequal (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Ge, xFloat, yFloat, (typeOf xFloat))) st - | `Lifted (Isless (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))) st - | `Lifted (Islessequal (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Le, xFloat, yFloat, (typeOf xFloat))) st - | `Lifted (Islessgreater (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (LOr, (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))), (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))), (TInt (IBool, [])))) st - | _ -> update_lval c x c' ID.pretty - end - | None -> update_lval c x c' ID.pretty + begin match x, t with + | (Var v, offs), TInt (ik, _) -> + if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs))); + begin match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs)) with + | `Lifted (Abs (_ik, xInt)) -> + inv_exp (Int (ID.join c (ID.neg c))) xInt st (* TODO: deduplicate *) + | tmpSpecial -> + let tv_opt = ID.to_bool c in (* TODO: simplify *) + begin match tv_opt with + | Some tv -> + begin match tmpSpecial with + | `Lifted (Isfinite xFloat) when tv -> inv_exp (Float (FD.finite (unroll_fk_of_exp xFloat))) xFloat st + | `Lifted (Isnan xFloat) when tv -> inv_exp (Float (FD.nan_of (unroll_fk_of_exp xFloat))) xFloat st + (* should be correct according to C99 standard*) + | `Lifted (Isgreater (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))) st + | `Lifted (Isgreaterequal (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Ge, xFloat, yFloat, (typeOf xFloat))) st + | `Lifted (Isless (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))) st + | `Lifted (Islessequal (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Le, xFloat, yFloat, (typeOf xFloat))) st + | `Lifted (Islessgreater (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (LOr, (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))), (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))), (TInt (IBool, [])))) st + | _ -> update_lval c x c' ID.pretty end + | None -> update_lval c x c' ID.pretty end - | _ -> update_lval c x c' ID.pretty end - | _ -> update_lval c x c' ID.pretty + | _, _ -> update_lval c x c' ID.pretty end | Float c -> let c' = match t with @@ -749,22 +745,18 @@ struct | _ -> Float c in (* handle special calls *) - begin match t with - | TFloat (fk, _) -> - begin match x with - | ((Var v), offs) -> - if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs))); - begin match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs)) with - | `Lifted (Ceil (ret_fk, xFloat)) -> inv_exp (Float (FD.inv_ceil (FD.cast_to ret_fk c))) xFloat st - | `Lifted (Floor (ret_fk, xFloat)) -> inv_exp (Float (FD.inv_floor (FD.cast_to ret_fk c))) xFloat st - | `Lifted (Fabs (ret_fk, xFloat)) -> - let inv = FD.inv_fabs (FD.cast_to ret_fk c) in - if FD.is_bot inv then - raise Analyses.Deadcode - else - inv_exp (Float inv) xFloat st - | _ -> update_lval c x c' FD.pretty - end + begin match x, t with + | (Var v, offs), TFloat (fk, _) -> + if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs))); + begin match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs)) with + | `Lifted (Ceil (ret_fk, xFloat)) -> inv_exp (Float (FD.inv_ceil (FD.cast_to ret_fk c))) xFloat st + | `Lifted (Floor (ret_fk, xFloat)) -> inv_exp (Float (FD.inv_floor (FD.cast_to ret_fk c))) xFloat st + | `Lifted (Fabs (ret_fk, xFloat)) -> + let inv = FD.inv_fabs (FD.cast_to ret_fk c) in + if FD.is_bot inv then + raise Analyses.Deadcode + else + inv_exp (Float inv) xFloat st | _ -> update_lval c x c' FD.pretty end | _ -> update_lval c x c' FD.pretty From b2d65f11380f023f73e7af0a0349e9c1d176a99b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 27 Nov 2023 13:34:07 +0200 Subject: [PATCH 1156/1312] Deduplicate TmpSpecial query in BaseInvariant --- src/analyses/baseInvariant.ml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index dc4dff540a..0d79aa8969 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -711,8 +711,9 @@ struct (* handle special calls *) begin match x, t with | (Var v, offs), TInt (ik, _) -> - if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs))); - begin match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs)) with + let tmpSpecial = ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs)) in + if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty tmpSpecial; + begin match tmpSpecial with | `Lifted (Abs (_ik, xInt)) -> inv_exp (Int (ID.join c (ID.neg c))) xInt st (* TODO: deduplicate *) | tmpSpecial -> @@ -747,8 +748,9 @@ struct (* handle special calls *) begin match x, t with | (Var v, offs), TFloat (fk, _) -> - if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs))); - begin match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs)) with + let tmpSpecial = ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs)) in + if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty tmpSpecial; + begin match tmpSpecial with | `Lifted (Ceil (ret_fk, xFloat)) -> inv_exp (Float (FD.inv_ceil (FD.cast_to ret_fk c))) xFloat st | `Lifted (Floor (ret_fk, xFloat)) -> inv_exp (Float (FD.inv_floor (FD.cast_to ret_fk c))) xFloat st | `Lifted (Fabs (ret_fk, xFloat)) -> From 1730aa71eaa6ba4dfcf6492f3bdf79eeb677f54b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 27 Nov 2023 14:03:50 +0200 Subject: [PATCH 1157/1312] Remove BaseInvariant tmpSpecial TODOs --- src/analyses/baseInvariant.ml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index 0d79aa8969..304d3e55ad 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -714,16 +714,17 @@ struct let tmpSpecial = ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs)) in if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty tmpSpecial; begin match tmpSpecial with - | `Lifted (Abs (_ik, xInt)) -> - inv_exp (Int (ID.join c (ID.neg c))) xInt st (* TODO: deduplicate *) + | `Lifted (Abs (ik, xInt)) -> + let c' = ID.cast_to ik c in (* different ik! *) + inv_exp (Int (ID.join c' (ID.neg c'))) xInt st | tmpSpecial -> - let tv_opt = ID.to_bool c in (* TODO: simplify *) - begin match tv_opt with + begin match ID.to_bool c with | Some tv -> begin match tmpSpecial with | `Lifted (Isfinite xFloat) when tv -> inv_exp (Float (FD.finite (unroll_fk_of_exp xFloat))) xFloat st | `Lifted (Isnan xFloat) when tv -> inv_exp (Float (FD.nan_of (unroll_fk_of_exp xFloat))) xFloat st (* should be correct according to C99 standard*) + (* The following do to_bool and of_bool to convert Not{0} into 1 for downstream float inversions *) | `Lifted (Isgreater (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))) st | `Lifted (Isgreaterequal (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Ge, xFloat, yFloat, (typeOf xFloat))) st | `Lifted (Isless (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))) st From 5a6362e1c0bda3ad0feb3332bf64e95fcea810b8 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 27 Nov 2023 14:32:47 +0200 Subject: [PATCH 1158/1312] Fix LibraryDslTest compilation --- unittest/analyses/libraryDslTest.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unittest/analyses/libraryDslTest.ml b/unittest/analyses/libraryDslTest.ml index e1fa23281c..077b81b8fa 100644 --- a/unittest/analyses/libraryDslTest.ml +++ b/unittest/analyses/libraryDslTest.ml @@ -11,7 +11,7 @@ let pthread_mutex_lock_desc: LibraryDesc.t = LibraryDsl.( ) let pthread_create_desc: LibraryDesc.t = LibraryDsl.( - special [__ "thread" [w]; drop "attr" [r]; __ "start_routine" [r]; __ "arg" [r]] @@ fun thread start_routine arg -> ThreadCreate { thread; start_routine; arg } + special [__ "thread" [w]; drop "attr" [r]; __ "start_routine" [r]; __ "arg" [r]] @@ fun thread start_routine arg -> ThreadCreate { thread; start_routine; arg; multiple = false } ) let realloc_desc: LibraryDesc.t = LibraryDsl.( From 209a5607204f960a0de6d6d7f81c754354306211 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 27 Nov 2023 15:18:49 +0100 Subject: [PATCH 1159/1312] Reduce activated analsyses and add test --- tests/regression/74-invalid_deref/31-multithreaded.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/regression/74-invalid_deref/31-multithreaded.c b/tests/regression/74-invalid_deref/31-multithreaded.c index e0dc146ba8..8a0c12350b 100644 --- a/tests/regression/74-invalid_deref/31-multithreaded.c +++ b/tests/regression/74-invalid_deref/31-multithreaded.c @@ -1,4 +1,4 @@ -//PARAM: --set ana.activated[+] useAfterFree --set ana.activated[+] threadJoins --set ana.path_sens[+] threadflag --set ana.activated[+] memOutOfBounds --enable ana.int.interval --set ana.base.arrays.domain partitioned --set ana.base.privatization mutex-meet-tid +//PARAM: --set ana.path_sens[+] threadflag --set ana.activated[+] memOutOfBounds --set ana.base.privatization mutex-meet-tid #include int data; @@ -15,7 +15,7 @@ int main() { pthread_create(&id, ((void *)0), t_fun, ((void *)0)); q = p; pthread_mutex_lock(&mutex); - *q = 8; + *q = 8; //NOWARN pthread_mutex_unlock(&mutex); return 0; } From 6b1dce9ab0faf763cf3f2d12e4de8bc0a27f2aa1 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 28 Nov 2023 11:11:39 +0200 Subject: [PATCH 1160/1312] Fix tracing call in base --- src/analyses/base.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index bdae887b4a..7c741e227e 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1486,7 +1486,7 @@ struct Priv.read_global a priv_getg st x in let new_value = update_offset old_value in - M.tracel "hgh" "update_offset %a -> %a\n" VD.pretty old_value VD.pretty new_value; + if M.tracing then M.tracel "set" "update_offset %a -> %a\n" VD.pretty old_value VD.pretty new_value; let r = Priv.write_global ~invariant a priv_getg (priv_sideg ctx.sideg) st x new_value in if M.tracing then M.tracel "set" ~var:x.vname "update_one_addr: updated a global var '%s' \nstate:%a\n\n" x.vname D.pretty r; r From cdf0dee88bccfbb623a914e37e5fd9264de8bef3 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 28 Nov 2023 11:16:21 +0200 Subject: [PATCH 1161/1312] Add test for general abs refinement --- tests/regression/39-signed-overflows/06-abs.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/regression/39-signed-overflows/06-abs.c b/tests/regression/39-signed-overflows/06-abs.c index e56cc9ff7d..1323434cbc 100644 --- a/tests/regression/39-signed-overflows/06-abs.c +++ b/tests/regression/39-signed-overflows/06-abs.c @@ -17,6 +17,13 @@ int main() { __goblint_check(-100 <= data); int result = data * data; //NOWARN } + + if(abs(data) - 1 <= 99) + { + __goblint_check(data <= 100); + __goblint_check(-100 <= data); + int result = data * data; //NOWARN + } } return 8; } \ No newline at end of file From deb12f492905a3d849fe746ca203f78c4610a0dc Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 28 Nov 2023 13:01:48 +0200 Subject: [PATCH 1162/1312] Suppress no-cmx-file warning --- src/build-info/dune | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/build-info/dune b/src/build-info/dune index c1de250263..ff8d68671b 100644 --- a/src/build-info/dune +++ b/src/build-info/dune @@ -27,3 +27,6 @@ (mode (promote (until-clean) (only configOcaml.ml))) ; replace existing file in source tree, even if releasing (only overrides) (action (write-file %{target} "(* Automatically regenerated, changes do not persist! *)\nlet flambda = \"%{ocaml-config:flambda}\""))) +(env + (_ + (flags (:standard -w -no-cmx-file)))) ; suppress warning from flambda compiler bug: https://github.com/ocaml/dune/issues/3277 From 1a0fdb98421a8712ccd51256ec8f116c467db51b Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 28 Nov 2023 17:09:33 +0100 Subject: [PATCH 1163/1312] Annotate faialing test as TODO --- tests/regression/73-strings/09-malloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression/73-strings/09-malloc.c b/tests/regression/73-strings/09-malloc.c index 913ec821c0..a050032885 100644 --- a/tests/regression/73-strings/09-malloc.c +++ b/tests/regression/73-strings/09-malloc.c @@ -11,6 +11,6 @@ int main () { s2[0] = 'a'; // Use size_t to avoid integer warnings hiding the lack of string warnings - size_t len1 = strlen(s1); //WARN + size_t len1 = strlen(s1); //TODO size_t len2 = strlen(s2); //WARN } From 2b8e3faaddde24ab8e767d097f133d0dfde38344 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 28 Nov 2023 18:18:49 +0100 Subject: [PATCH 1164/1312] Simplify --- src/cdomains/arrayDomain.ml | 149 ++++++++++++++++------------------- src/cdomains/arrayDomain.mli | 24 +++--- src/cdomains/valueDomain.ml | 14 ++-- 3 files changed, 87 insertions(+), 100 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 8f966d0fad..00d9107211 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -74,7 +74,7 @@ module type Str = sig include S0 - type ret = Null | NotNull | Top + type ret = Null | NotNull | Maybe type substr = IsNotSubstr | IsSubstrAtIndex0 | IsMaybeSubstr val get: VDQ.t -> t -> Basetype.CilExp.t option * idx -> ret @@ -95,7 +95,7 @@ sig val get: ?checkBounds:bool -> VDQ.t -> t -> Basetype.CilExp.t option * idx -> value end -module type LatticeWithInvalidate = +module type LatticeWithInvalidate = sig include Lattice.S val invalidate_abstract_value: t -> t @@ -112,10 +112,10 @@ end module type LatticeWithNull = sig include LatticeWithSmartOps + type retnull = Null | NotNull | Maybe val null: unit -> t - val is_null: t -> bool - val is_not_null: t -> bool + val is_null: t -> retnull val get_ikind: t -> Cil.ikind option val zero_of_ikind: Cil.ikind -> t @@ -1016,18 +1016,7 @@ struct type idx = Idx.t type value = Val.t - type ret = Null | NotNull | Top - module Val = struct - include Val - - let is_null v = - if is_not_null v then - NotNull - else if is_null v then - Null - else - Top - end + type ret = Null | NotNull | Maybe type substr = IsNotSubstr | IsSubstrAtIndex0 | IsMaybeSubstr @@ -1056,7 +1045,7 @@ struct NotNull (* ... else return Top *) else - Top + Maybe (* if there is no maximum size *) | Some max_i, None when max_i >=. Z.zero -> (* ... and maximum value in index interval < minimal size, return Null if all numbers in index interval are in must_nulls_set *) @@ -1066,7 +1055,7 @@ struct else if not (Nulls.exists Possibly (fun x -> x >=. min_i && x <=. max_i) nulls) then NotNull else - Top + Maybe | Some max_i, Some max_size when max_i >=. Z.zero -> (* if maximum value in index interval < minimal size, return Null if all numbers in index interval are in must_nulls_set *) if max_i <. min_size && Nulls.interval_mem Definitely (min_i, max_i) nulls then @@ -1075,9 +1064,9 @@ struct else if max_i <. max_size && not (Nulls.exists Possibly (fun x -> x >=. min_i && x <=. max_i) nulls) then NotNull else - Top + Maybe (* if maximum number in interval is invalid, i.e. negative, return Top of value *) - | _ -> Top + | _ -> Maybe let set (ask: VDQ.t) (nulls, size) (e, i) v = let min interval = Z.max Z.zero (BatOption.default Z.zero (Idx.minimal interval)) in @@ -1089,7 +1078,7 @@ struct let set_exact_nulls i = match idx_maximal size with (* if size has no upper limit *) - | None -> + | None -> (match Val.is_null v with | NotNull -> Nulls.remove (if Nulls.is_full_set Possibly nulls then Possibly else Definitely) i nulls min_size @@ -1098,7 +1087,7 @@ struct Nulls.add (if i <. min_size then Definitely else Possibly) i nulls (* i < minimal size and value = null, add i to must_nulls_set and may_nulls_set *) (* i >= minimal size and value = null, add i only to may_nulls_set *) - | Top -> + | Maybe -> let removed = Nulls.remove Possibly i nulls min_size in Nulls.add Possibly i removed) | Some max_size -> @@ -1110,7 +1099,7 @@ struct Nulls.add Definitely i nulls | Null when i <. max_size -> Nulls.add Possibly i nulls - | Top when i <. max_size -> + | Maybe when i <. max_size -> let removed = Nulls.remove Possibly i nulls min_size in Nulls.add Possibly i removed | _ -> nulls @@ -1123,9 +1112,9 @@ struct match Val.is_null v with | NotNull -> Nulls.remove_interval Possibly (min_i, max_i) min_size nulls | Null -> Nulls.add_interval ~maxfull:(idx_maximal size) Possibly (min_i, max_i) nulls - | Top -> + | Maybe -> let nulls = Nulls.add_interval ~maxfull:(idx_maximal size) Possibly (min_i, max_i) nulls in - Nulls.remove_interval Possibly (min_i, max_i) min_size nulls + Nulls.remove_interval Possibly (min_i, max_i) min_size nulls in (* warn if index is (potentially) out of bounds *) @@ -1141,7 +1130,7 @@ struct (* ... and there is a maximal size, add all i from minimal index to maximal size to may_nulls_set *) | Some max_size -> Nulls.add_interval Possibly (min_i, Z.pred max_size) nulls (* ... and value <> null, only keep indexes < minimal index in must_nulls_set *) - else if Val.is_not_null v then + else if Val.is_null v = NotNull then Nulls.filter_musts (Z.gt min_i) min_size nulls (*..., value unknown *) else @@ -1149,15 +1138,15 @@ struct (* ... and size unknown, modify both sets to top *) | None, None -> Nulls.top () (* ... and only minimal size known, remove all indexes < minimal size from must_nulls_set and modify may_nulls_set to top *) - | Some min_size, None -> + | Some min_size, None -> let nulls = Nulls.add_all Possibly nulls in Nulls.filter_musts (Z.gt min_size) min_size nulls (* ... and only maximal size known, modify must_nulls_set to top and add all i from minimal index to maximal size to may_nulls_set *) - | None, Some max_size -> + | None, Some max_size -> let nulls = Nulls.remove_all Possibly nulls in Nulls.add_interval Possibly (min_i, Z.pred max_size) nulls (* ... and size is known, remove all indexes < minimal size from must_nulls_set and add all i from minimal index to maximal size to may_nulls_set *) - | Some min_size, Some max_size -> + | Some min_size, Some max_size -> let nulls = Nulls.filter_musts (Z.gt min_size) min_size nulls in Nulls.add_interval Possibly (min_i, Z.pred max_size) nulls ) @@ -1169,7 +1158,7 @@ struct (* if maximum number in interval is invalid, i.e. negative, return tuple unmodified *) | _ -> nulls in - (nulls, size) + (nulls, size) let make ?(varAttr=[]) ?(typAttr=[]) i v = @@ -1195,13 +1184,13 @@ struct Z.zero, None) else min_i, None - | None, None -> Z.zero, None + | None, None -> Z.zero, None in let size = BatOption.map_default (fun max -> Idx.of_interval ILong (min_i, max)) (Idx.starting ILong min_i) max_i in match Val.is_null v with | Null -> (Nulls.make_all_must (), size) | NotNull -> (Nulls.empty (), size) - | Top -> (Nulls.top (), size) + | Maybe -> (Nulls.top (), size) let length (_, size) = Some size @@ -1211,7 +1200,7 @@ struct let get_vars_in_e _ = [] let map f (nulls, size) = - (* if f(null) = null, all values in must_nulls_set still are surely null; + (* if f(null) = null, all values in must_nulls_set still are surely null; * assume top for may_nulls_set as checking effect of f for every possible value is unfeasbile *) match Val.is_null (f (Val.null ())) with | Null -> (Nulls.add_all Possibly nulls, size) @@ -1227,7 +1216,7 @@ struct let to_null_byte_domain s = let last_null = Z.of_int (String.length s) in - let rec build_set i set = + let rec build_set i set = if (Z.of_int i) >=. last_null then MaySet.add last_null set else @@ -1255,7 +1244,7 @@ struct (* else return empty must_nulls_set and keep every index up to smallest index of must_nulls_set included in may_nulls_set *) else match idx_maximal size with - | Some max_size -> + | Some max_size -> let nulls' = Nulls.remove_all Possibly nulls in (Nulls.filter ~max_size (Z.leq min_must_null) nulls', Idx.of_int ILong (Z.succ min_must_null)) | None when not (Nulls.may_can_benefit_from_filter nulls) -> @@ -1266,7 +1255,7 @@ struct (Nulls.filter (Z.leq min_must_null) nulls', Idx.of_int ILong (Z.succ min_must_null)) (** [to_n_string index_set n] returns an abstract value with a potential null byte - * marking the end of the string and if needed followed by further null bytes to obtain + * marking the end of the string and if needed followed by further null bytes to obtain * an n bytes string. *) let to_n_string (nulls, size) n:t = let must_nulls_set, may_nulls_set = nulls in @@ -1312,16 +1301,16 @@ struct if n >. max_size then warn_past_end "Array size is smaller than n bytes; can cause a buffer overflow" | None, None -> ()); - let nulls = + let nulls = (* if definitely no null byte in array, i.e. must_nulls_set = may_nulls_set = empty set *) if Nulls.is_empty Definitely nulls then - (warn_past_end + (warn_past_end "Resulting string might not be null-terminated because src doesn't contain a null byte"; match idx_maximal size with (* ... there *may* be null bytes from maximal size to n - 1 if maximal size < n (i.e. past end) *) | Some max_size when Z.geq max_size Z.zero -> Nulls.add_interval Possibly (max_size, Z.pred n) nulls | _ -> nulls) - (* if only must_nulls_set empty, remove indexes >= n from may_nulls_set and add all indexes from minimal may null index to n - 1; + (* if only must_nulls_set empty, remove indexes >= n from may_nulls_set and add all indexes from minimal may null index to n - 1; * warn as in any case, resulting array not guaranteed to contain null byte *) else if Nulls.is_empty Possibly nulls then let min_may_null = Nulls.min_elem Possibly nulls in @@ -1367,44 +1356,44 @@ struct let must_nulls_set2',may_nulls_set2' = truncatednulls in match Idx.minimal dstsize, idx_maximal dstsize, Idx.minimal len2, idx_maximal len2 with | Some min_dstsize, Some max_dstsize, Some min_srclen, Some max_srclen -> - (if max_dstsize <. min_srclen then - warn_past_end "The length of string src is greater than the allocated size for dest" + (if max_dstsize <. min_srclen then + warn_past_end "The length of string src is greater than the allocated size for dest" else if min_dstsize <. max_srclen then warn_past_end "The length of string src may be greater than the allocated size for dest"); - let must_nulls_set_result = + let must_nulls_set_result = let min_size2 = BatOption.default Z.zero (Idx.minimal truncatedsize) in (* get must nulls from src string < minimal size of dest *) MustSet.filter ~min_size:min_size2 (Z.gt min_dstsize) must_nulls_set2' (* and keep indexes of dest >= maximal strlen of src *) |> MustSet.union (MustSet.filter ~min_size:min_dstsize (Z.leq max_srclen) must_nulls_set1) in - let may_nulls_set_result = + let may_nulls_set_result = let max_size2 = BatOption.default max_dstsize (idx_maximal truncatedsize) in (* get may nulls from src string < maximal size of dest *) MaySet.filter ~max_size:max_size2 (Z.gt max_dstsize) may_nulls_set2' (* and keep indexes of dest >= minimal strlen of src *) |> MaySet.union (MaySet.filter ~max_size:max_dstsize (Z.leq min_srclen) may_nulls_set1) in ((must_nulls_set_result, may_nulls_set_result), dstsize) - + | Some min_size1, None, Some min_len2, Some max_len2 -> (if min_size1 <. max_len2 then warn_past_end "The length of string src may be greater than the allocated size for dest"); - let must_nulls_set_result = + let must_nulls_set_result = let min_size2 = BatOption.default Z.zero (Idx.minimal truncatedsize) in MustSet.filter ~min_size: min_size2 (Z.gt min_size1) must_nulls_set2' |> MustSet.union (MustSet.filter ~min_size:min_size1 (Z.leq max_len2) must_nulls_set1) in - let may_nulls_set_result = + let may_nulls_set_result = (* get all may nulls from src string as no maximal size of dest *) may_nulls_set2' |> MaySet.union (MaySet.filter ~max_size:(Z.succ min_len2) (Z.leq min_len2) may_nulls_set1) in ((must_nulls_set_result, may_nulls_set_result), dstsize) | Some min_size1, Some max_size1, Some min_len2, None -> - (if max_size1 <. min_len2 then - warn_past_end "The length of string src is greater than the allocated size for dest" + (if max_size1 <. min_len2 then + warn_past_end "The length of string src is greater than the allocated size for dest" else if min_size1 <. min_len2 then warn_past_end"The length of string src may be greater than the allocated size for dest"); (* do not keep any index of dest as no maximal strlen of src *) - let must_nulls_set_result = + let must_nulls_set_result = let min_size2 = BatOption.default Z.zero (Idx.minimal truncatedsize) in MustSet.filter ~min_size:min_size2 (Z.gt min_size1) must_nulls_set2' in let may_nulls_set_result = @@ -1416,10 +1405,10 @@ struct (if min_size1 <. min_len2 then warn_past_end "The length of string src may be greater than the allocated size for dest"); (* do not keep any index of dest as no maximal strlen of src *) - let must_nulls_set_result = + let must_nulls_set_result = let min_size2 = BatOption.default Z.zero (Idx.minimal truncatedsize) in MustSet.filter ~min_size:min_size2 (Z.gt min_size1) must_nulls_set2' in - let may_nulls_set_result = + let may_nulls_set_result = (* get all may nulls from src string as no maximal size of dest *) may_nulls_set2' |> MaySet.union (MaySet.filter ~max_size:(Z.succ min_len2) (Z.leq min_len2) may_nulls_set1) in @@ -1465,21 +1454,21 @@ struct | _ -> (Nulls.top (), dstsize) let string_concat (nulls1, size1) (nulls2, size2) n = - let update_sets min_size1 max_size1 max_size1_exists minlen1 maxlen1 maxlen1_exists minlen2 maxlen2 maxlen2_exists nulls2' = + let update_sets min_size1 max_size1 max_size1_exists minlen1 maxlen1 maxlen1_exists minlen2 maxlen2 maxlen2_exists nulls2' = (* track any potential buffer overflow and issue warning if needed *) (if max_size1_exists && max_size1 <=. (minlen1 +. minlen2) then warn_past_end "The length of the concatenation of the strings in src and dest is greater than the allocated size for dest" else if (maxlen1_exists && maxlen2_exists && min_size1 <=. (maxlen1 +. maxlen2)) || not maxlen1_exists || not maxlen2_exists then - warn_past_end + warn_past_end "The length of the concatenation of the strings in src and dest may be greater than the allocated size for dest"); - (* if any must_nulls_set empty, result must_nulls_set also empty; + (* if any must_nulls_set empty, result must_nulls_set also empty; * for all i1, i2 in may_nulls_set1, may_nulls_set2: add i1 + i2 if it is <= strlen(dest) + strlen(src) to new may_nulls_set * and keep indexes > minimal strlen(dest) + strlen(src) of may_nulls_set *) if Nulls.is_empty Possibly nulls1 || Nulls.is_empty Possibly nulls2 then if max_size1_exists then let nulls1_no_must = Nulls.remove_all Possibly nulls1 in - let r = + let r = nulls1_no_must (* filter ensures we have the concete representation *) |> Nulls.filter ~max_size:max_size1 (fun x -> if maxlen1_exists && maxlen2_exists then x <=. (maxlen1 +. maxlen2) else true) @@ -1488,11 +1477,11 @@ struct |> List.map (fun (i1, i2) -> i1 +. i2) |> (fun x -> Nulls.add_list Possibly x (Nulls.filter ~max_size:max_size1 (Z.lt (minlen1 +. minlen2)) nulls1_no_must)) |> Nulls.filter (Z.gt max_size1) - in + in (r, size1) else if Nulls.may_can_benefit_from_filter nulls1 && Nulls.may_can_benefit_from_filter nulls2 && maxlen1_exists && maxlen2_exists then let nulls1_no_must = Nulls.remove_all Possibly nulls1 in - let r = + let r = nulls1_no_must (* filter ensures we have the concete representation *) |> Nulls.filter (fun x -> x <=. (maxlen1 +. maxlen2)) @@ -1500,7 +1489,7 @@ struct |> BatList.cartesian_product (Nulls.elements Possibly nulls2') |> List.map (fun (i1, i2) -> i1 +. i2) |> (fun x -> Nulls.add_list Possibly x (Nulls.filter (Z.lt (minlen1 +. minlen2)) nulls1_no_must)) - in + in (r, size1) else (Nulls.top (), size1) @@ -1511,15 +1500,15 @@ struct let min_i2 = Nulls.min_elem Definitely nulls2' in let min_i = min_i1 +. min_i2 in let (must_nulls_set1, may_nulls_set1) = nulls1 in - let must_nulls_set_result = + let must_nulls_set_result = MustSet.filter ~min_size:min_size1 (Z.lt min_i) must_nulls_set1 |> MustSet.add min_i |> MustSet.M.filter (Z.gt min_size1) in - let may_nulls_set_result = + let may_nulls_set_result = if max_size1_exists then MaySet.filter ~max_size:max_size1 (Z.lt min_i) may_nulls_set1 |> MaySet.add min_i - |> MaySet.M.filter (fun x -> if max_size1_exists then max_size1 >. x else true) + |> MaySet.M.filter (fun x -> if max_size1_exists then max_size1 >. x else true) else MaySet.top () in ((must_nulls_set_result, may_nulls_set_result), size1) @@ -1528,12 +1517,12 @@ struct let min_i2 = Nulls.min_elem Definitely nulls2' in let (must_nulls_set1, may_nulls_set1) = nulls1 in let (must_nulls_set2', may_nulls_set2') = nulls2' in - let may_nulls_set2'_until_min_i2 = + let may_nulls_set2'_until_min_i2 = match idx_maximal size2 with | Some max_size2 -> MaySet.filter ~max_size:max_size2 (Z.geq min_i2) may_nulls_set2' | None -> MaySet.filter ~max_size:(Z.succ min_i2) (Z.geq min_i2) may_nulls_set2' in let must_nulls_set_result = MustSet.filter ~min_size:min_size1 (fun x -> if maxlen1_exists && maxlen2_exists then (maxlen1 +. maxlen2) <. x else false) must_nulls_set1 in - let may_nulls_set_result = + let may_nulls_set_result = if max_size1_exists then MaySet.filter ~max_size:max_size1 (fun x -> if maxlen1_exists && maxlen2_exists then x <=. (maxlen1 +. maxlen2) else true) may_nulls_set1 |> MaySet.elements @@ -1541,7 +1530,7 @@ struct |> List.map (fun (i1, i2) -> i1 +. i2) |> MaySet.of_list |> MaySet.union (MaySet.filter ~max_size:max_size1 (Z.lt (minlen1 +. minlen2)) may_nulls_set1) - |> MaySet.M.filter (fun x -> if max_size1_exists then max_size1 >. x else true) + |> MaySet.M.filter (fun x -> if max_size1_exists then max_size1 >. x else true) else if not (MaySet.is_top may_nulls_set1) then MaySet.M.filter (fun x -> if maxlen1_exists && maxlen2_exists then x <=. (maxlen1 +. maxlen2) else true) may_nulls_set1 |> MaySet.elements @@ -1557,14 +1546,14 @@ struct let strlen1 = to_string_length (nulls1, size1) in let strlen2 = to_string_length (nulls2', size2) in match Idx.minimal size1, Idx.minimal strlen1, Idx.minimal strlen2 with - | Some min_size1, Some minlen1, Some minlen2 -> + | Some min_size1, Some minlen1, Some minlen2 -> begin match idx_maximal size1, idx_maximal strlen1, idx_maximal strlen2 with | Some max_size1, Some maxlen1, Some maxlen2 -> update_sets min_size1 max_size1 true minlen1 maxlen1 true minlen2 maxlen2 true nulls2' (* no upper bound for length of concatenation *) | Some max_size1, None, Some _ | Some max_size1, Some _, None - | Some max_size1, None, None -> + | Some max_size1, None, None -> update_sets min_size1 max_size1 true minlen1 Z.zero false minlen2 Z.zero false nulls2' (* no upper bound for size of dest *) | None, Some maxlen1, Some maxlen2 -> @@ -1584,7 +1573,7 @@ struct let nulls2', _ = to_string (nulls2, size2) in compute_concat nulls2' (* strncat *) - | Some n when n >= 0 -> + | Some n when n >= 0 -> let n = Z.of_int n in (* take at most n bytes from src; if no null byte among them, add null byte at index n *) let nulls2' = @@ -1597,7 +1586,7 @@ struct else let min_size2 = BatOption.default Z.zero (Idx.minimal size2) in let max_size2 = BatOption.default n (idx_maximal size2) in - (MustSet.filter ~min_size: min_size2 (Z.gt n) must_nulls_set2, MaySet.filter ~max_size:max_size2 (Z.gt n) may_nulls_set2) + (MustSet.filter ~min_size: min_size2 (Z.gt n) must_nulls_set2, MaySet.filter ~max_size:max_size2 (Z.gt n) may_nulls_set2) in compute_concat nulls2' | _ -> (Nulls.top (), size1) @@ -1608,7 +1597,7 @@ struct IsSubstrAtIndex0 else let haystack_len = to_string_length haystack in - let needle_len = to_string_length needle in + let needle_len = to_string_length needle in match idx_maximal haystack_len, Idx.minimal needle_len with | Some haystack_max, Some needle_min -> (* if strlen(haystack) < strlen(needle), needle can never be substring of haystack => return None *) @@ -1630,15 +1619,15 @@ struct else if Nulls.mem Definitely Z.zero nulls2 then Idx.starting IInt Z.one else - try + try let min_must1 = Nulls.min_elem Definitely nulls1 in let min_must2 = Nulls.min_elem Definitely nulls2 in - if not (min_must1 =. min_must2) + if not (min_must1 =. min_must2) && min_must1 =.(Nulls.min_elem Possibly nulls1) && min_must2 =. (Nulls.min_elem Possibly nulls2) && (not n_exists || min_must1 <. n || min_must2 <. n) then - (* if first null bytes are certain, have different indexes and are before index n if n present, return integer <> 0 *) + (* if first null bytes are certain, have different indexes and are before index n if n present, return integer <> 0 *) Idx.of_excl_list IInt [Z.zero] else Idx.top_of IInt @@ -1828,12 +1817,12 @@ struct type idx = Idx.t type value = Val.t - type ret = Null | NotNull | Top + type ret = Null | NotNull | Maybe type substr = N.substr = IsNotSubstr | IsSubstrAtIndex0 | IsMaybeSubstr let domain_of_t (t_f, _) = A.domain_of_t t_f - let get ?(checkBounds=true) (ask: VDQ.t) (t_f, t_n) i = + let get ?(checkBounds=true) (ask: VDQ.t) (t_f, t_n) i = let f_get = A.get ~checkBounds ask t_f i in if get_bool "ana.base.arrays.nullbytes" then let n_get = N.get ask t_n i in @@ -1864,7 +1853,7 @@ struct let string_copy = string_op N.string_copy let string_concat = string_op N.string_concat - let extract op default (_, t_n1) (_, t_n2) n = + let extract op default (_, t_n1) (_, t_n2) n = if get_bool "ana.base.arrays.nullbytes" then op t_n1 t_n2 n else @@ -1873,9 +1862,9 @@ struct default () let substring_extraction x y = extract (fun x y _ -> N.substring_extraction x y) (fun () -> IsMaybeSubstr) x y None - let string_comparison = extract N.string_comparison (fun () -> Idx.top_of IInt) + let string_comparison = extract N.string_comparison (fun () -> Idx.top_of IInt) - let length (t_f, t_n) = + let length (t_f, t_n) = if get_bool "ana.base.arrays.nullbytes" then N.length t_n else @@ -1884,18 +1873,18 @@ struct let get_vars_in_e (t_f, _) = A.get_vars_in_e t_f let fold_left f acc (t_f, _) = A.fold_left f acc t_f - let smart_leq x y (t_f1, t_n1) (t_f2, t_n2) = + let smart_leq x y (t_f1, t_n1) (t_f2, t_n2) = if get_bool "ana.base.arrays.nullbytes" then A.smart_leq x y t_f1 t_f2 && N.smart_leq x y t_n1 t_n2 else A.smart_leq x y t_f1 t_f2 - let to_null_byte_domain s = + let to_null_byte_domain s = if get_bool "ana.base.arrays.nullbytes" then (A.make (Idx.top_of ILong) (Val.meet (Val.not_zero_of_ikind IChar) (Val.zero_of_ikind IChar)), N.to_null_byte_domain s) else (A.top (), N.top ()) - let to_string_length (_, t_n) = + let to_string_length (_, t_n) = if get_bool "ana.base.arrays.nullbytes" then N.to_string_length t_n else diff --git a/src/cdomains/arrayDomain.mli b/src/cdomains/arrayDomain.mli index fef063f765..0fe08f2cfb 100644 --- a/src/cdomains/arrayDomain.mli +++ b/src/cdomains/arrayDomain.mli @@ -71,7 +71,7 @@ module type Str = sig include S0 - type ret = Null | NotNull | Top + type ret = Null | NotNull | Maybe type substr = IsNotSubstr | IsSubstrAtIndex0 | IsMaybeSubstr val get: VDQ.t -> t -> Basetype.CilExp.t option * idx -> ret @@ -88,17 +88,17 @@ sig * into array [dest], taking at most [n] bytes of [src] if present *) val string_concat: t -> t -> int option -> t - (** [string_concat s1 s2 n] returns a new abstract value representing the string + (** [string_concat s1 s2 n] returns a new abstract value representing the string * concatenation of the input abstract values [s1] and [s2], taking at most [n] bytes of * [s2] if present *) val substring_extraction: t -> t -> substr - (** [substring_extraction haystack needle] returns [IsNotSubstr] if the string represented by - * the abstract value [needle] surely isn't a substring of [haystack], [IsSubstrAtIndex0] if + (** [substring_extraction haystack needle] returns [IsNotSubstr] if the string represented by + * the abstract value [needle] surely isn't a substring of [haystack], [IsSubstrAtIndex0] if * [needle] is the empty string, else [Unknown] *) val string_comparison: t -> t -> int option -> idx - (** [string_comparison s1 s2 n] returns a negative / positive idx element if the string + (** [string_comparison s1 s2 n] returns a negative / positive idx element if the string * represented by [s1] is less / greater than the one by [s2] or zero if they are equal; * only compares the first [n] bytes if present *) end @@ -112,7 +112,7 @@ sig val get: ?checkBounds:bool -> VDQ.t -> t -> Basetype.CilExp.t option * idx -> value end -module type LatticeWithInvalidate = +module type LatticeWithInvalidate = sig include Lattice.S val invalidate_abstract_value: t -> t @@ -129,10 +129,10 @@ end module type LatticeWithNull = sig include LatticeWithSmartOps + type retnull = Null | NotNull | Maybe val null: unit -> t - val is_null: t -> bool - val is_not_null: t -> bool + val is_null: t -> retnull val get_ikind: t -> Cil.ikind option val zero_of_ikind: Cil.ikind -> t @@ -162,8 +162,8 @@ module PartitionedWithLength (Val: LatticeWithSmartOps) (Idx:IntDomain.Z): S wit module NullByte (Val: LatticeWithNull) (Idx: IntDomain.Z): Str with type value = Val.t and type idx = Idx.t (** This functor creates an array representation by the indexes of all null bytes * the array must and may contain. This is useful to analyze strings, i.e. null- - * terminated char arrays, and particularly to determine if operations on strings - * could lead to a buffer overflow. Concrete values from Val are not interesting + * terminated char arrays, and particularly to determine if operations on strings + * could lead to a buffer overflow. Concrete values from Val are not interesting * for this domain. It additionally tracks the array size. *) @@ -171,6 +171,6 @@ module AttributeConfiguredArrayDomain (Val: LatticeWithSmartOps) (Idx: IntDomain (** Switches between PartitionedWithLength, TrivialWithLength and Unroll based on variable, type, and flag. *) module AttributeConfiguredAndNullByteArrayDomain (Val: LatticeWithNull) (Idx: IntDomain.Z): StrWithDomain with type value = Val.t and type idx = Idx.t -(** Like FlagHelperAttributeConfiguredArrayDomain but additionally runs NullByte - * in parallel if flag "ana.base.arrays.nullbytes" is set. +(** Like FlagHelperAttributeConfiguredArrayDomain but additionally runs NullByte + * in parallel if flag "ana.base.arrays.nullbytes" is set. *) diff --git a/src/cdomains/valueDomain.ml b/src/cdomains/valueDomain.ml index 985d7cca8b..9dfc65a1f1 100644 --- a/src/cdomains/valueDomain.ml +++ b/src/cdomains/valueDomain.ml @@ -39,9 +39,9 @@ sig val is_top_value: t -> typ -> bool val zero_init_value: ?varAttr:attributes -> typ -> t + type retnull = Null | NotNull | Maybe val null: unit -> t - val is_null: t -> bool - val is_not_null: t -> bool + val is_null: t -> retnull val get_ikind: t -> Cil.ikind option val zero_of_ikind: Cil.ikind -> t @@ -276,15 +276,13 @@ struct let null () = Int (ID.of_int IChar Z.zero) + type retnull = Null | NotNull | Maybe let is_null = function - | Int n -> GobOption.exists (Z.equal Z.zero) (ID.to_int n) - | _ -> false - - let is_not_null = function + | Int n when GobOption.exists (Z.equal Z.zero) (ID.to_int n) -> Null | Int n -> let zero_ik = ID.of_int (ID.ikind n) Z.zero in - ID.to_bool (ID.ne n zero_ik) = Some true - | _ -> false (* we don't know anything *) + if ID.to_bool (ID.ne n zero_ik) = Some true then NotNull else Maybe + | _ -> Maybe let get_ikind = function | Int n -> Some (ID.ikind n) From f51d60f306b40b69a497a872d6b6c35b48722ead Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 28 Nov 2023 18:34:20 +0100 Subject: [PATCH 1165/1312] Simplify --- src/cdomains/arrayDomain.ml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 00d9107211..d2d1d80c7d 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1335,17 +1335,15 @@ struct let to_string_length (nulls, size) = (* if must_nulls_set and min_nulls_set empty, definitely no null byte in array => return interval [size, inf) and warn *) - (* TODO: check of must set really needed? *) if Nulls.is_empty Definitely nulls then (warn_past_end "Array doesn't contain a null byte: buffer overflow"; - match Idx.minimal size with - | Some min_size -> Idx.starting !Cil.kindOfSizeOf min_size - | None -> Idx.starting !Cil.kindOfSizeOf Z.zero) - (* if only must_nulls_set empty, no guarantee that null ever encountered in array => return interval [minimal may null, inf) and *) + Idx.starting !Cil.kindOfSizeOf (BatOption.default Z.zero (Idx.minimal size)) + ) + (* if only must_nulls_set empty, no guarantee that null ever encountered in array => return interval [minimal may null, inf) and *) else if Nulls.is_empty Possibly nulls then (warn_past_end "Array might not contain a null byte: potential buffer overflow"; Idx.starting !Cil.kindOfSizeOf (Nulls.min_elem Possibly nulls)) - (* else return interval [minimal may null, minimal must null] *) + (* else return interval [minimal may null, minimal must null] *) else Idx.of_interval !Cil.kindOfSizeOf (Nulls.min_elem Possibly nulls, Nulls.min_elem Definitely nulls) From 0a47ea24c19a87740a67ec50b55c7adcd14218dd Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 28 Nov 2023 18:43:09 +0100 Subject: [PATCH 1166/1312] Simplify --- src/cdomains/arrayDomain.ml | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index d2d1d80c7d..6fe801fd79 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1236,23 +1236,25 @@ struct (warn_past_end "May access array past end: potential buffer overflow"; x) else let min_must_null = Nulls.min_elem Definitely nulls in + let new_size = Idx.of_int ILong (Z.succ min_must_null) in let min_may_null = Nulls.min_elem Possibly nulls in (* if smallest index in sets coincides, only this null byte is kept in both sets *) - if min_must_null =. min_may_null then - let nulls = Nulls.precise_singleton min_must_null in - (nulls, Idx.of_int ILong (Z.succ min_must_null)) - (* else return empty must_nulls_set and keep every index up to smallest index of must_nulls_set included in may_nulls_set *) - else - match idx_maximal size with - | Some max_size -> - let nulls' = Nulls.remove_all Possibly nulls in - (Nulls.filter ~max_size (Z.leq min_must_null) nulls', Idx.of_int ILong (Z.succ min_must_null)) - | None when not (Nulls.may_can_benefit_from_filter nulls) -> - let empty = Nulls.empty () in - (Nulls.add_interval Possibly (Z.zero, min_must_null) empty, Idx.of_int ILong (Z.succ min_must_null)) - | None -> - let nulls' = Nulls.remove_all Possibly nulls in - (Nulls.filter (Z.leq min_must_null) nulls', Idx.of_int ILong (Z.succ min_must_null)) + let nulls = + if min_must_null =. min_may_null then + Nulls.precise_singleton min_must_null + (* else return empty must_nulls_set and keep every index up to smallest index of must_nulls_set included in may_nulls_set *) + else + match idx_maximal size with + | Some max_size -> + let nulls' = Nulls.remove_all Possibly nulls in + Nulls.filter ~max_size (Z.leq min_must_null) nulls' + | None when not (Nulls.may_can_benefit_from_filter nulls) -> + Nulls.add_interval Possibly (Z.zero, min_must_null) (Nulls.empty ()) + | None -> + let nulls' = Nulls.remove_all Possibly nulls in + Nulls.filter (Z.leq min_must_null) nulls' + in + (nulls, new_size) (** [to_n_string index_set n] returns an abstract value with a potential null byte * marking the end of the string and if needed followed by further null bytes to obtain From 272e496cd69151c88c79eb356c83a455e6a48c36 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 28 Nov 2023 18:50:03 +0100 Subject: [PATCH 1167/1312] Simplify --- src/cdomains/arrayDomain.ml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 6fe801fd79..08bdcc6224 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1039,13 +1039,12 @@ struct match max_i, idx_maximal size with (* if there is no maximum value in index interval *) - | None, _ -> + | None, _ when not (Nulls.exists Possibly ((<=.) min_i) nulls) -> (* ... return NotNull if no i >= min_i in may_nulls_set *) - if not (Nulls.exists Possibly ((<=.) min_i) nulls) then - NotNull - (* ... else return Top *) - else - Maybe + NotNull + | None, _ -> + (* ... else return Top *) + Maybe (* if there is no maximum size *) | Some max_i, None when max_i >=. Z.zero -> (* ... and maximum value in index interval < minimal size, return Null if all numbers in index interval are in must_nulls_set *) From 3ebc74da421cc1160c123726b0188fd49b5abd33 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 28 Nov 2023 19:08:14 +0100 Subject: [PATCH 1168/1312] Remove `idx_maximal` hack --- src/cdomains/arrayDomain.ml | 55 +++++++++----------- tests/regression/73-strings/05-char_arrays.c | 2 +- 2 files changed, 26 insertions(+), 31 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 08bdcc6224..4eae0a2747 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1023,21 +1023,16 @@ struct module ArrayOobMessage = M.Category.Behavior.Undefined.ArrayOutOfBounds let warn_past_end = M.error ~category:ArrayOobMessage.past_end - (* helper: returns Idx.maximal except for Overflows that are mapped to None *) - let idx_maximal i = match Idx.maximal i with - | Some i when Z.fits_int i -> Some i - | _ -> None - let get (ask: VDQ.t) (nulls, size) (e, i) = let min interval = match Idx.minimal interval with | Some min_num when min_num >=. Z.zero -> min_num | _ -> Z.zero in (* assume worst case minimal natural number *) let min_i = min i in - let max_i = idx_maximal i in + let max_i = Idx.maximal i in let min_size = min size in - match max_i, idx_maximal size with + match max_i, Idx.maximal size with (* if there is no maximum value in index interval *) | None, _ when not (Nulls.exists Possibly ((<=.) min_i) nulls) -> (* ... return NotNull if no i >= min_i in may_nulls_set *) @@ -1072,10 +1067,10 @@ struct let min_size = min size in let min_i = min i in - let max_i = idx_maximal i in + let max_i = Idx.maximal i in let set_exact_nulls i = - match idx_maximal size with + match Idx.maximal size with (* if size has no upper limit *) | None -> (match Val.is_null v with @@ -1107,12 +1102,12 @@ struct let set_interval min_i max_i = (* Update max_i so it is capped at the maximum size *) - let max_i = BatOption.map_default (fun x -> Z.min max_i @@ Z.pred x) max_i (idx_maximal size) in + let max_i = BatOption.map_default (fun x -> Z.min max_i @@ Z.pred x) max_i (Idx.maximal size) in match Val.is_null v with | NotNull -> Nulls.remove_interval Possibly (min_i, max_i) min_size nulls - | Null -> Nulls.add_interval ~maxfull:(idx_maximal size) Possibly (min_i, max_i) nulls + | Null -> Nulls.add_interval ~maxfull:(Idx.maximal size) Possibly (min_i, max_i) nulls | Maybe -> - let nulls = Nulls.add_interval ~maxfull:(idx_maximal size) Possibly (min_i, max_i) nulls in + let nulls = Nulls.add_interval ~maxfull:(Idx.maximal size) Possibly (min_i, max_i) nulls in Nulls.remove_interval Possibly (min_i, max_i) min_size nulls in @@ -1122,8 +1117,8 @@ struct (* if no maximum number in index interval *) | None -> (* ..., value = null *) - (if Val.is_null v = Null && idx_maximal size = None then - match idx_maximal size with + (if Val.is_null v = Null && Idx.maximal size = None then + match Idx.maximal size with (* ... and there is no maximal size, modify may_nulls_set to top *) | None -> Nulls.add_all Possibly nulls (* ... and there is a maximal size, add all i from minimal index to maximal size to may_nulls_set *) @@ -1133,7 +1128,7 @@ struct Nulls.filter_musts (Z.gt min_i) min_size nulls (*..., value unknown *) else - match Idx.minimal size, idx_maximal size with + match Idx.minimal size, Idx.maximal size with (* ... and size unknown, modify both sets to top *) | None, None -> Nulls.top () (* ... and only minimal size known, remove all indexes < minimal size from must_nulls_set and modify may_nulls_set to top *) @@ -1161,7 +1156,7 @@ struct let make ?(varAttr=[]) ?(typAttr=[]) i v = - let min_i, max_i = match Idx.minimal i, idx_maximal i with + let min_i, max_i = match Idx.minimal i, Idx.maximal i with | Some min_i, Some max_i -> if min_i <. Z.zero && max_i <. Z.zero then (M.error ~category:ArrayOobMessage.before_start "Tries to create an array of negative size"; @@ -1243,7 +1238,7 @@ struct Nulls.precise_singleton min_must_null (* else return empty must_nulls_set and keep every index up to smallest index of must_nulls_set included in may_nulls_set *) else - match idx_maximal size with + match Idx.maximal size with | Some max_size -> let nulls' = Nulls.remove_all Possibly nulls in Nulls.filter ~max_size (Z.leq min_must_null) nulls' @@ -1289,7 +1284,7 @@ struct else if (exists_min_must_null && (min_must_null >=. n) || min_must_null >. min_may_null) || not exists_min_must_null then M.warn "Resulting string might not be null-terminated because src might not contain a null byte in the first n bytes" in - (match Idx.minimal size, idx_maximal size with + (match Idx.minimal size, Idx.maximal size with | Some min_size, Some max_size -> if n >. max_size then warn_past_end "Array size is smaller than n bytes; can cause a buffer overflow" @@ -1307,7 +1302,7 @@ struct if Nulls.is_empty Definitely nulls then (warn_past_end "Resulting string might not be null-terminated because src doesn't contain a null byte"; - match idx_maximal size with + match Idx.maximal size with (* ... there *may* be null bytes from maximal size to n - 1 if maximal size < n (i.e. past end) *) | Some max_size when Z.geq max_size Z.zero -> Nulls.add_interval Possibly (max_size, Z.pred n) nulls | _ -> nulls) @@ -1353,7 +1348,7 @@ struct (* filter out indexes before strlen(src) from dest sets and after strlen(src) from src sets and build union, keep size of dest *) let update_sets (truncatednulls, truncatedsize) len2 = let must_nulls_set2',may_nulls_set2' = truncatednulls in - match Idx.minimal dstsize, idx_maximal dstsize, Idx.minimal len2, idx_maximal len2 with + match Idx.minimal dstsize, Idx.maximal dstsize, Idx.minimal len2, Idx.maximal len2 with | Some min_dstsize, Some max_dstsize, Some min_srclen, Some max_srclen -> (if max_dstsize <. min_srclen then warn_past_end "The length of string src is greater than the allocated size for dest" @@ -1366,7 +1361,7 @@ struct (* and keep indexes of dest >= maximal strlen of src *) |> MustSet.union (MustSet.filter ~min_size:min_dstsize (Z.leq max_srclen) must_nulls_set1) in let may_nulls_set_result = - let max_size2 = BatOption.default max_dstsize (idx_maximal truncatedsize) in + let max_size2 = BatOption.default max_dstsize (Idx.maximal truncatedsize) in (* get may nulls from src string < maximal size of dest *) MaySet.filter ~max_size:max_size2 (Z.gt max_dstsize) may_nulls_set2' (* and keep indexes of dest >= minimal strlen of src *) @@ -1396,7 +1391,7 @@ struct let min_size2 = BatOption.default Z.zero (Idx.minimal truncatedsize) in MustSet.filter ~min_size:min_size2 (Z.gt min_size1) must_nulls_set2' in let may_nulls_set_result = - let max_size2 = BatOption.default max_size1 (idx_maximal truncatedsize) in + let max_size2 = BatOption.default max_size1 (Idx.maximal truncatedsize) in MaySet.filter ~max_size:max_size2 (Z.gt max_size1) may_nulls_set2' |> MaySet.union (MaySet.filter ~max_size:max_size1 (Z.leq min_len2) may_nulls_set1) in ((must_nulls_set_result, may_nulls_set_result), dstsize) @@ -1417,7 +1412,7 @@ struct (* warn if size of dest is (potentially) smaller than size of src and the latter (potentially) has no null byte at index < size of dest *) let sizes_warning srcsize = - (match Idx.minimal dstsize, idx_maximal dstsize, Idx.minimal srcsize, idx_maximal srcsize with + (match Idx.minimal dstsize, Idx.maximal dstsize, Idx.minimal srcsize, Idx.maximal srcsize with | Some min_dstsize, _, Some min_srcsize, _ when min_dstsize <. min_srcsize -> if not (Nulls.exists Possibly (Z.gt min_dstsize) srcnulls) then warn_past_end "src doesn't contain a null byte at an index smaller than the size of dest" @@ -1517,7 +1512,7 @@ struct let (must_nulls_set1, may_nulls_set1) = nulls1 in let (must_nulls_set2', may_nulls_set2') = nulls2' in let may_nulls_set2'_until_min_i2 = - match idx_maximal size2 with + match Idx.maximal size2 with | Some max_size2 -> MaySet.filter ~max_size:max_size2 (Z.geq min_i2) may_nulls_set2' | None -> MaySet.filter ~max_size:(Z.succ min_i2) (Z.geq min_i2) may_nulls_set2' in let must_nulls_set_result = MustSet.filter ~min_size:min_size1 (fun x -> if maxlen1_exists && maxlen2_exists then (maxlen1 +. maxlen2) <. x else false) must_nulls_set1 in @@ -1546,7 +1541,7 @@ struct let strlen2 = to_string_length (nulls2', size2) in match Idx.minimal size1, Idx.minimal strlen1, Idx.minimal strlen2 with | Some min_size1, Some minlen1, Some minlen2 -> - begin match idx_maximal size1, idx_maximal strlen1, idx_maximal strlen2 with + begin match Idx.maximal size1, Idx.maximal strlen1, Idx.maximal strlen2 with | Some max_size1, Some maxlen1, Some maxlen2 -> update_sets min_size1 max_size1 true minlen1 maxlen1 true minlen2 maxlen2 true nulls2' (* no upper bound for length of concatenation *) @@ -1580,11 +1575,11 @@ struct if not (Nulls.exists Possibly (Z.gt n) nulls2) then Nulls.precise_singleton n else if not (Nulls.exists Definitely (Z.gt n) nulls2) then - let max_size2 = BatOption.default (Z.succ n) (idx_maximal size2) in + let max_size2 = BatOption.default (Z.succ n) (Idx.maximal size2) in (MustSet.empty (), MaySet.add n (MaySet.filter ~max_size:max_size2 (Z.geq n) may_nulls_set2)) else let min_size2 = BatOption.default Z.zero (Idx.minimal size2) in - let max_size2 = BatOption.default n (idx_maximal size2) in + let max_size2 = BatOption.default n (Idx.maximal size2) in (MustSet.filter ~min_size: min_size2 (Z.gt n) must_nulls_set2, MaySet.filter ~max_size:max_size2 (Z.gt n) may_nulls_set2) in compute_concat nulls2' @@ -1597,7 +1592,7 @@ struct else let haystack_len = to_string_length haystack in let needle_len = to_string_length needle in - match idx_maximal haystack_len, Idx.minimal needle_len with + match Idx.maximal haystack_len, Idx.minimal needle_len with | Some haystack_max, Some needle_min -> (* if strlen(haystack) < strlen(needle), needle can never be substring of haystack => return None *) if haystack_max <. needle_min then @@ -1653,7 +1648,7 @@ struct let min_size1 = BatOption.default Z.zero (Idx.minimal size1) in let min_size2 = BatOption.default Z.zero (Idx.minimal size2) in (* issue a warning if n is (potentially) smaller than array sizes *) - (match idx_maximal size1 with + (match Idx.maximal size1 with | Some max_size1 -> if n >. max_size1 then warn_past_end"The size of the array of string 1 is smaller than n bytes" @@ -1663,7 +1658,7 @@ struct if n >. min_size1 then warn_past_end "The size of the array of string 1 might be smaller than n bytes" ); - (match idx_maximal size2 with + (match Idx.maximal size2 with | Some max_size2 -> if n >. max_size2 then warn_past_end "The size of the array of string 2 is smaller than n bytes" diff --git a/tests/regression/73-strings/05-char_arrays.c b/tests/regression/73-strings/05-char_arrays.c index e5c7596063..cbf1916ca9 100644 --- a/tests/regression/73-strings/05-char_arrays.c +++ b/tests/regression/73-strings/05-char_arrays.c @@ -337,7 +337,7 @@ example16() { if (rand()) i = 3; else - i = 1/0; + i = 4; char s[5] = "abab"; __goblint_check(s[i] != '\0'); // UNKNOWN From 63bd31a0c31342fdf638b24ce86bb653fdb476eb Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 28 Nov 2023 19:17:07 +0100 Subject: [PATCH 1169/1312] Simplify --- src/cdomains/arrayDomain.ml | 35 +++++++++++++---------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 4eae0a2747..ffb567209f 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1645,28 +1645,19 @@ struct (* strncmp *) | Some n when n >= 0 -> let n = Z.of_int n in - let min_size1 = BatOption.default Z.zero (Idx.minimal size1) in - let min_size2 = BatOption.default Z.zero (Idx.minimal size2) in - (* issue a warning if n is (potentially) smaller than array sizes *) - (match Idx.maximal size1 with - | Some max_size1 -> - if n >. max_size1 then - warn_past_end"The size of the array of string 1 is smaller than n bytes" - else if n >. min_size1 then - warn_past_end "The size of the array of string 1 might be smaller than n bytes" - | None -> - if n >. min_size1 then - warn_past_end "The size of the array of string 1 might be smaller than n bytes" - ); - (match Idx.maximal size2 with - | Some max_size2 -> - if n >. max_size2 then - warn_past_end "The size of the array of string 2 is smaller than n bytes" - else if n >. min_size2 then - warn_past_end "The size of the array of string 2 might be smaller than n bytes" - | None -> - if n >. min_size2 then - warn_past_end "The size of the array of string 2 might be smaller than n bytes"); + let warn_size size name = + let min = BatOption.default Z.zero (Idx.minimal size) in + match Idx.maximal size with + | Some max when n >. max -> + warn_past_end "The size of the array of string %s is smaller than n bytes" name + | Some max when n >. min -> + warn_past_end "The size of the array of string %s might be smaller than n bytes" name + | None when n >. min -> + warn_past_end "The size of the array of string %s might be smaller than n bytes" name + | _ -> () + in + warn_size size1 "1"; + warn_size size2 "2"; (* compute abstract value for result of strncmp *) compare n true | _ -> Idx.top_of IInt From 71bce3cf316f99b71565533ea49b67da697bbebc Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 28 Nov 2023 19:20:06 +0100 Subject: [PATCH 1170/1312] Simplify --- src/cdomains/arrayDomain.ml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index ffb567209f..974da1bf6f 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1632,14 +1632,14 @@ struct (* strcmp *) | None -> (* track any potential buffer overflow and issue warning if needed *) - (if Nulls.is_empty Definitely nulls1 && Nulls.is_empty Possibly nulls1 then - warn_past_end "Array of string 1 doesn't contain a null byte: buffer overflow" - else if Nulls.is_empty Possibly nulls1 then - warn_past_end "Array of string 1 might not contain a null byte: potential buffer overflow"); - (if Nulls.is_empty Definitely nulls2 && Nulls.is_empty Possibly nulls2 then - warn_past_end "Array of string 2 doesn't contain a null byte: buffer overflow" - else if Nulls.is_empty Possibly nulls2 then - warn_past_end "Array of string 2 might not contain a null byte: potential buffer overflow"); + let warn_missing_nulls nulls name = + if Nulls.is_empty Definitely nulls then + warn_past_end "Array of string %s doesn't contain a null byte: buffer overflow" name + else if Nulls.is_empty Possibly nulls then + warn_past_end "Array of string %s might not contain a null byte: potential buffer overflow" name + in + warn_missing_nulls nulls1 "1"; + warn_missing_nulls nulls2 "2"; (* compute abstract value for result of strcmp *) compare Z.zero false (* strncmp *) From 0b3ff1545b40092d4b4f7bfec61e81d0c151a73c Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 28 Nov 2023 19:30:04 +0100 Subject: [PATCH 1171/1312] Remove `n_exists` construction --- src/cdomains/arrayDomain.ml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 974da1bf6f..d1ffa46ca8 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1602,9 +1602,9 @@ struct | _ -> IsMaybeSubstr let string_comparison (nulls1, size1) (nulls2, size2) n = - let compare n n_exists = + let cmp n = (* if s1 = s2 = empty string, i.e. certain null byte at index 0, or n = 0, return 0 *) - if (Nulls.mem Definitely Z.zero nulls1 && Nulls.mem Definitely Z.zero nulls2) || (n_exists && n =. Z.zero) then + if (Nulls.mem Definitely Z.zero nulls1 && Nulls.mem Definitely Z.zero nulls2) || (BatOption.map_default (Z.equal Z.zero) false n) then Idx.of_int IInt Z.zero (* if only s1 = empty string, return negative integer *) else if Nulls.mem Definitely Z.zero nulls1 && not (Nulls.mem Possibly Z.zero nulls2) then @@ -1619,7 +1619,7 @@ struct if not (min_must1 =. min_must2) && min_must1 =.(Nulls.min_elem Possibly nulls1) && min_must2 =. (Nulls.min_elem Possibly nulls2) - && (not n_exists || min_must1 <. n || min_must2 <. n) + && (BatOption.map_default (fun x -> min_must1 <. x || min_must2 <. x) true n) then (* if first null bytes are certain, have different indexes and are before index n if n present, return integer <> 0 *) Idx.of_excl_list IInt [Z.zero] @@ -1641,7 +1641,7 @@ struct warn_missing_nulls nulls1 "1"; warn_missing_nulls nulls2 "2"; (* compute abstract value for result of strcmp *) - compare Z.zero false + cmp None (* strncmp *) | Some n when n >= 0 -> let n = Z.of_int n in @@ -1659,7 +1659,7 @@ struct warn_size size1 "1"; warn_size size2 "2"; (* compute abstract value for result of strncmp *) - compare n true + cmp (Some n) | _ -> Idx.top_of IInt let update_length new_size (nulls, size) = (nulls, new_size) From 320cc90a3e6c4d932ce22b1185615fe612be45b1 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 28 Nov 2023 19:33:46 +0100 Subject: [PATCH 1172/1312] Simplify --- src/cdomains/arrayDomain.ml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index d1ffa46ca8..5f4c917df2 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1593,12 +1593,9 @@ struct let haystack_len = to_string_length haystack in let needle_len = to_string_length needle in match Idx.maximal haystack_len, Idx.minimal needle_len with - | Some haystack_max, Some needle_min -> + | Some haystack_max, Some needle_min when haystack_max <. needle_min -> (* if strlen(haystack) < strlen(needle), needle can never be substring of haystack => return None *) - if haystack_max <. needle_min then - IsNotSubstr - else - IsMaybeSubstr + IsNotSubstr | _ -> IsMaybeSubstr let string_comparison (nulls1, size1) (nulls2, size2) n = From b4bb3c1827a2fdaa29114ea71cef41bf902d24ea Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 28 Nov 2023 19:42:14 +0100 Subject: [PATCH 1173/1312] Steps towards removing ops on raw sets --- src/cdomains/arrayDomain.ml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 5f4c917df2..508bbcd50d 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1571,16 +1571,18 @@ struct let n = Z.of_int n in (* take at most n bytes from src; if no null byte among them, add null byte at index n *) let nulls2' = - let ((must_nulls_set2, may_nulls_set2) as nulls2), size2 = to_string (nulls2, size2) in + let (nulls2, size2) = to_string (nulls2, size2) in if not (Nulls.exists Possibly (Z.gt n) nulls2) then Nulls.precise_singleton n else if not (Nulls.exists Definitely (Z.gt n) nulls2) then - let max_size2 = BatOption.default (Z.succ n) (Idx.maximal size2) in - (MustSet.empty (), MaySet.add n (MaySet.filter ~max_size:max_size2 (Z.geq n) may_nulls_set2)) + let max_size = BatOption.default (Z.succ n) (Idx.maximal size2) in + let nulls2 = Nulls.remove_all Possibly nulls2 in + let nulls2 = Nulls.filter ~max_size (Z.geq n) nulls2 in + Nulls.add Possibly n nulls2 else - let min_size2 = BatOption.default Z.zero (Idx.minimal size2) in - let max_size2 = BatOption.default n (Idx.maximal size2) in - (MustSet.filter ~min_size: min_size2 (Z.gt n) must_nulls_set2, MaySet.filter ~max_size:max_size2 (Z.gt n) may_nulls_set2) + let min_size = BatOption.default Z.zero (Idx.minimal size2) in + let max_size = BatOption.default n (Idx.maximal size2) in + Nulls.filter ~max_size ~min_size (Z.gt n) nulls2 in compute_concat nulls2' | _ -> (Nulls.top (), size1) From 55a0dd4e603087ff472bc856e3e2c6906c3bc168 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 28 Nov 2023 20:11:10 +0100 Subject: [PATCH 1174/1312] Replace exists types with options --- src/cdomains/arrayDomain.ml | 76 +++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 36 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 508bbcd50d..f81c3096c4 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1448,24 +1448,25 @@ struct | _ -> (Nulls.top (), dstsize) let string_concat (nulls1, size1) (nulls2, size2) n = - let update_sets min_size1 max_size1 max_size1_exists minlen1 maxlen1 maxlen1_exists minlen2 maxlen2 maxlen2_exists nulls2' = + let update_sets min_size1 max_size1 minlen1 (maxlen1: Z.t option) minlen2 maxlen2 maxlen2_exists nulls2' = (* track any potential buffer overflow and issue warning if needed *) - (if max_size1_exists && max_size1 <=. (minlen1 +. minlen2) then + (if GobOption.exists (fun x -> x <=. (minlen1 +. minlen2)) max_size1 then warn_past_end "The length of the concatenation of the strings in src and dest is greater than the allocated size for dest" - else if (maxlen1_exists && maxlen2_exists && min_size1 <=. (maxlen1 +. maxlen2)) || not maxlen1_exists || not maxlen2_exists then + else if (GobOption.for_all (fun x -> min_size1 <=. (x +. maxlen2)) maxlen1) && maxlen2_exists || not maxlen2_exists then warn_past_end "The length of the concatenation of the strings in src and dest may be greater than the allocated size for dest"); (* if any must_nulls_set empty, result must_nulls_set also empty; * for all i1, i2 in may_nulls_set1, may_nulls_set2: add i1 + i2 if it is <= strlen(dest) + strlen(src) to new may_nulls_set * and keep indexes > minimal strlen(dest) + strlen(src) of may_nulls_set *) if Nulls.is_empty Possibly nulls1 || Nulls.is_empty Possibly nulls2 then - if max_size1_exists then + match max_size1 with + | Some max_size1 -> let nulls1_no_must = Nulls.remove_all Possibly nulls1 in let r = nulls1_no_must (* filter ensures we have the concete representation *) - |> Nulls.filter ~max_size:max_size1 (fun x -> if maxlen1_exists && maxlen2_exists then x <=. (maxlen1 +. maxlen2) else true) + |> Nulls.filter ~max_size:max_size1 (fun x -> GobOption.for_all (fun maxlen1 -> if maxlen2_exists then x <=. (maxlen1 +. maxlen2) else true) maxlen1) |> Nulls.elements ~max_size:max_size1 Possibly |> BatList.cartesian_product (Nulls.elements ~max_size:max_size1 Possibly nulls2') |> List.map (fun (i1, i2) -> i1 +. i2) @@ -1473,22 +1474,23 @@ struct |> Nulls.filter (Z.gt max_size1) in (r, size1) - else if Nulls.may_can_benefit_from_filter nulls1 && Nulls.may_can_benefit_from_filter nulls2 && maxlen1_exists && maxlen2_exists then - let nulls1_no_must = Nulls.remove_all Possibly nulls1 in - let r = - nulls1_no_must - (* filter ensures we have the concete representation *) - |> Nulls.filter (fun x -> x <=. (maxlen1 +. maxlen2)) - |> Nulls.elements Possibly - |> BatList.cartesian_product (Nulls.elements Possibly nulls2') - |> List.map (fun (i1, i2) -> i1 +. i2) - |> (fun x -> Nulls.add_list Possibly x (Nulls.filter (Z.lt (minlen1 +. minlen2)) nulls1_no_must)) - in - (r, size1) - else - (Nulls.top (), size1) - - (* if minimal must null = minimal may null in ar1 and ar2, add them together and keep indexes > strlen(dest) + strlen(src) of ar1 *) + | None when Nulls.may_can_benefit_from_filter nulls1 && Nulls.may_can_benefit_from_filter nulls2 -> + (match maxlen1, Some maxlen2 with + | Some maxlen1, Some maxlen2 when maxlen2_exists -> + let nulls1_no_must = Nulls.remove_all Possibly nulls1 in + let r = + nulls1_no_must + (* filter ensures we have the concete representation *) + |> Nulls.filter (fun x -> x <=. (maxlen1 +. maxlen2)) + |> Nulls.elements Possibly + |> BatList.cartesian_product (Nulls.elements Possibly nulls2') + |> List.map (fun (i1, i2) -> i1 +. i2) + |> (fun x -> Nulls.add_list Possibly x (Nulls.filter (Z.lt (minlen1 +. minlen2)) nulls1_no_must)) + in + (r, size1) + | _ -> (Nulls.top (), size1)) + | _ -> (Nulls.top (), size1) + (* if minimal must null = minimal may null in ar1 and ar2, add them together and keep indexes > strlen(dest) + strlen(src) of ar1 *) else if Nulls.min_elem_precise (nulls1) && Nulls.min_elem_precise nulls2' then let min_i1 = Nulls.min_elem Definitely nulls1 in let min_i2 = Nulls.min_elem Definitely nulls2' in @@ -1499,12 +1501,13 @@ struct |> MustSet.add min_i |> MustSet.M.filter (Z.gt min_size1) in let may_nulls_set_result = - if max_size1_exists then + match max_size1 with + | Some max_size1 -> MaySet.filter ~max_size:max_size1 (Z.lt min_i) may_nulls_set1 |> MaySet.add min_i - |> MaySet.M.filter (fun x -> if max_size1_exists then max_size1 >. x else true) - else - MaySet.top () in + |> MaySet.M.filter (fun x -> max_size1 >. x) + | _ -> MaySet.top () + in ((must_nulls_set_result, may_nulls_set_result), size1) (* else only add all may nulls together <= strlen(dest) + strlen(src) *) else @@ -1515,24 +1518,25 @@ struct match Idx.maximal size2 with | Some max_size2 -> MaySet.filter ~max_size:max_size2 (Z.geq min_i2) may_nulls_set2' | None -> MaySet.filter ~max_size:(Z.succ min_i2) (Z.geq min_i2) may_nulls_set2' in - let must_nulls_set_result = MustSet.filter ~min_size:min_size1 (fun x -> if maxlen1_exists && maxlen2_exists then (maxlen1 +. maxlen2) <. x else false) must_nulls_set1 in + let must_nulls_set_result = MustSet.filter ~min_size:min_size1 (fun x -> GobOption.exists (fun maxlen1 -> if maxlen2_exists then (maxlen1 +. maxlen2) <. x else false) maxlen1) must_nulls_set1 in let may_nulls_set_result = - if max_size1_exists then - MaySet.filter ~max_size:max_size1 (fun x -> if maxlen1_exists && maxlen2_exists then x <=. (maxlen1 +. maxlen2) else true) may_nulls_set1 + match max_size1 with + | Some max_size1 -> + MaySet.filter ~max_size:max_size1 (fun x -> GobOption.for_all (fun maxlen1 -> if maxlen2_exists then x <=. (maxlen1 +. maxlen2) else true) maxlen1) may_nulls_set1 |> MaySet.elements |> BatList.cartesian_product (MaySet.elements may_nulls_set2'_until_min_i2) |> List.map (fun (i1, i2) -> i1 +. i2) |> MaySet.of_list |> MaySet.union (MaySet.filter ~max_size:max_size1 (Z.lt (minlen1 +. minlen2)) may_nulls_set1) - |> MaySet.M.filter (fun x -> if max_size1_exists then max_size1 >. x else true) - else if not (MaySet.is_top may_nulls_set1) then - MaySet.M.filter (fun x -> if maxlen1_exists && maxlen2_exists then x <=. (maxlen1 +. maxlen2) else true) may_nulls_set1 + |> MaySet.M.filter (fun x -> max_size1 >. x) + | None when not (MaySet.is_top may_nulls_set1) -> + MaySet.M.filter (fun x -> GobOption.for_all (fun maxlen1 -> if maxlen2_exists then x <=. (maxlen1 +. maxlen2) else true) maxlen1) may_nulls_set1 |> MaySet.elements |> BatList.cartesian_product (MaySet.elements may_nulls_set2'_until_min_i2) |> List.map (fun (i1, i2) -> i1 +. i2) |> MaySet.of_list |> MaySet.union (MaySet.M.filter (Z.lt (minlen1 +. minlen2)) may_nulls_set1) - else + | _ -> MaySet.top () in ((must_nulls_set_result, may_nulls_set_result), size1) in @@ -1543,20 +1547,20 @@ struct | Some min_size1, Some minlen1, Some minlen2 -> begin match Idx.maximal size1, Idx.maximal strlen1, Idx.maximal strlen2 with | Some max_size1, Some maxlen1, Some maxlen2 -> - update_sets min_size1 max_size1 true minlen1 maxlen1 true minlen2 maxlen2 true nulls2' + update_sets min_size1 (Some max_size1) minlen1 (Some maxlen1) minlen2 maxlen2 true nulls2' (* no upper bound for length of concatenation *) | Some max_size1, None, Some _ | Some max_size1, Some _, None | Some max_size1, None, None -> - update_sets min_size1 max_size1 true minlen1 Z.zero false minlen2 Z.zero false nulls2' + update_sets min_size1 (Some max_size1) minlen1 None minlen2 Z.zero false nulls2' (* no upper bound for size of dest *) | None, Some maxlen1, Some maxlen2 -> - update_sets min_size1 Z.zero false minlen1 maxlen1 true minlen2 maxlen2 true nulls2' + update_sets min_size1 None minlen1 (Some maxlen1) minlen2 maxlen2 true nulls2' (* no upper bound for size of dest and length of concatenation *) | None, None, Some _ | None, Some _, None | None, None, None -> - update_sets min_size1 Z.zero false minlen1 Z.zero false minlen2 Z.zero false nulls2' + update_sets min_size1 None minlen1 None minlen2 Z.zero false nulls2' end (* any other case shouldn't happen as minimal index is always >= 0 *) | _ -> (Nulls.top (), size1) in From 7a2e9bad75a494c33f50b74198151647523fd9be Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 28 Nov 2023 20:36:40 +0100 Subject: [PATCH 1175/1312] Make types in `string_concat` make sense --- src/cdomains/arrayDomain.ml | 58 ++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index f81c3096c4..cbb6e145c5 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1448,14 +1448,17 @@ struct | _ -> (Nulls.top (), dstsize) let string_concat (nulls1, size1) (nulls2, size2) n = - let update_sets min_size1 max_size1 minlen1 (maxlen1: Z.t option) minlen2 maxlen2 maxlen2_exists nulls2' = + let update_sets min_size1 max_size1 minlen1 maxlen1 minlen2 (maxlen2: Z.t option) nulls2' = (* track any potential buffer overflow and issue warning if needed *) (if GobOption.exists (fun x -> x <=. (minlen1 +. minlen2)) max_size1 then warn_past_end "The length of the concatenation of the strings in src and dest is greater than the allocated size for dest" - else if (GobOption.for_all (fun x -> min_size1 <=. (x +. maxlen2)) maxlen1) && maxlen2_exists || not maxlen2_exists then - warn_past_end - "The length of the concatenation of the strings in src and dest may be greater than the allocated size for dest"); + else + (match maxlen1, maxlen2 with + | Some maxlen1, Some maxlen2 when min_size1 >. (maxlen1 +. maxlen2) -> () + | _ -> warn_past_end + "The length of the concatenation of the strings in src and dest may be greater than the allocated size for dest") + ); (* if any must_nulls_set empty, result must_nulls_set also empty; * for all i1, i2 in may_nulls_set1, may_nulls_set2: add i1 + i2 if it is <= strlen(dest) + strlen(src) to new may_nulls_set * and keep indexes > minimal strlen(dest) + strlen(src) of may_nulls_set *) @@ -1463,10 +1466,14 @@ struct match max_size1 with | Some max_size1 -> let nulls1_no_must = Nulls.remove_all Possibly nulls1 in + let pred = match maxlen1, maxlen2 with + | Some maxlen1, Some maxlen2 -> (fun x -> x <=. (maxlen1 +. maxlen2)) + | _ -> (fun _ -> true) + in let r = nulls1_no_must (* filter ensures we have the concete representation *) - |> Nulls.filter ~max_size:max_size1 (fun x -> GobOption.for_all (fun maxlen1 -> if maxlen2_exists then x <=. (maxlen1 +. maxlen2) else true) maxlen1) + |> Nulls.filter ~max_size:max_size1 pred |> Nulls.elements ~max_size:max_size1 Possibly |> BatList.cartesian_product (Nulls.elements ~max_size:max_size1 Possibly nulls2') |> List.map (fun (i1, i2) -> i1 +. i2) @@ -1475,8 +1482,8 @@ struct in (r, size1) | None when Nulls.may_can_benefit_from_filter nulls1 && Nulls.may_can_benefit_from_filter nulls2 -> - (match maxlen1, Some maxlen2 with - | Some maxlen1, Some maxlen2 when maxlen2_exists -> + (match maxlen1, maxlen2 with + | Some maxlen1, Some maxlen2-> let nulls1_no_must = Nulls.remove_all Possibly nulls1 in let r = nulls1_no_must @@ -1518,11 +1525,21 @@ struct match Idx.maximal size2 with | Some max_size2 -> MaySet.filter ~max_size:max_size2 (Z.geq min_i2) may_nulls_set2' | None -> MaySet.filter ~max_size:(Z.succ min_i2) (Z.geq min_i2) may_nulls_set2' in - let must_nulls_set_result = MustSet.filter ~min_size:min_size1 (fun x -> GobOption.exists (fun maxlen1 -> if maxlen2_exists then (maxlen1 +. maxlen2) <. x else false) maxlen1) must_nulls_set1 in + let must_nulls_set_result = + let pred = match maxlen1, maxlen2 with + | Some maxlen1, Some maxlen2 -> (fun x -> (maxlen1 +. maxlen2) <. x) + | _ -> (fun _ -> false) + in + MustSet.filter ~min_size:min_size1 pred must_nulls_set1 + in let may_nulls_set_result = + let pred = match maxlen1, maxlen2 with + | Some maxlen1, Some maxlen2 -> (fun x -> x <=. (maxlen1 +. maxlen2)) + | _ -> (fun _ -> true) + in match max_size1 with | Some max_size1 -> - MaySet.filter ~max_size:max_size1 (fun x -> GobOption.for_all (fun maxlen1 -> if maxlen2_exists then x <=. (maxlen1 +. maxlen2) else true) maxlen1) may_nulls_set1 + MaySet.filter ~max_size:max_size1 pred may_nulls_set1 |> MaySet.elements |> BatList.cartesian_product (MaySet.elements may_nulls_set2'_until_min_i2) |> List.map (fun (i1, i2) -> i1 +. i2) @@ -1530,7 +1547,7 @@ struct |> MaySet.union (MaySet.filter ~max_size:max_size1 (Z.lt (minlen1 +. minlen2)) may_nulls_set1) |> MaySet.M.filter (fun x -> max_size1 >. x) | None when not (MaySet.is_top may_nulls_set1) -> - MaySet.M.filter (fun x -> GobOption.for_all (fun maxlen1 -> if maxlen2_exists then x <=. (maxlen1 +. maxlen2) else true) maxlen1) may_nulls_set1 + MaySet.M.filter pred may_nulls_set1 |> MaySet.elements |> BatList.cartesian_product (MaySet.elements may_nulls_set2'_until_min_i2) |> List.map (fun (i1, i2) -> i1 +. i2) @@ -1545,22 +1562,11 @@ struct let strlen2 = to_string_length (nulls2', size2) in match Idx.minimal size1, Idx.minimal strlen1, Idx.minimal strlen2 with | Some min_size1, Some minlen1, Some minlen2 -> - begin match Idx.maximal size1, Idx.maximal strlen1, Idx.maximal strlen2 with - | Some max_size1, Some maxlen1, Some maxlen2 -> - update_sets min_size1 (Some max_size1) minlen1 (Some maxlen1) minlen2 maxlen2 true nulls2' - (* no upper bound for length of concatenation *) - | Some max_size1, None, Some _ - | Some max_size1, Some _, None - | Some max_size1, None, None -> - update_sets min_size1 (Some max_size1) minlen1 None minlen2 Z.zero false nulls2' - (* no upper bound for size of dest *) - | None, Some maxlen1, Some maxlen2 -> - update_sets min_size1 None minlen1 (Some maxlen1) minlen2 maxlen2 true nulls2' - (* no upper bound for size of dest and length of concatenation *) - | None, None, Some _ - | None, Some _, None - | None, None, None -> - update_sets min_size1 None minlen1 None minlen2 Z.zero false nulls2' + begin + let f = update_sets min_size1 (Idx.maximal size1) minlen1 in + match Idx.maximal strlen1, Idx.maximal strlen2 with + | (Some _ as maxlen1), (Some _ as maxlen2) -> f maxlen1 minlen2 maxlen2 nulls2' + | _ -> f None minlen2 None nulls2' end (* any other case shouldn't happen as minimal index is always >= 0 *) | _ -> (Nulls.top (), size1) in From 1282af3e507083a65c2854e3ca16627c6e1b563d Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 28 Nov 2023 20:40:52 +0100 Subject: [PATCH 1176/1312] Simplify --- src/cdomains/arrayDomain.ml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index cbb6e145c5..8ee47e44ba 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1024,9 +1024,7 @@ struct let warn_past_end = M.error ~category:ArrayOobMessage.past_end let get (ask: VDQ.t) (nulls, size) (e, i) = - let min interval = match Idx.minimal interval with - | Some min_num when min_num >=. Z.zero -> min_num - | _ -> Z.zero in (* assume worst case minimal natural number *) + let min interval = Z.max Z.zero (BatOption.default Z.zero (Idx.minimal interval)) in let min_i = min i in let max_i = Idx.maximal i in From c85bad9038fd490cfe615b08c5b62e5ce50fd113 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 28 Nov 2023 20:45:55 +0100 Subject: [PATCH 1177/1312] Pull out helper --- src/cdomains/arrayDomain.ml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 8ee47e44ba..7cadd66c19 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1023,12 +1023,12 @@ struct module ArrayOobMessage = M.Category.Behavior.Undefined.ArrayOutOfBounds let warn_past_end = M.error ~category:ArrayOobMessage.past_end - let get (ask: VDQ.t) (nulls, size) (e, i) = - let min interval = Z.max Z.zero (BatOption.default Z.zero (Idx.minimal interval)) in + let min_nat_of_idx i = Z.max Z.zero (BatOption.default Z.zero (Idx.minimal i)) - let min_i = min i in + let get (ask: VDQ.t) (nulls, size) (e, i) = + let min_i = min_nat_of_idx i in let max_i = Idx.maximal i in - let min_size = min size in + let min_size = min_nat_of_idx size in match max_i, Idx.maximal size with (* if there is no maximum value in index interval *) @@ -1061,10 +1061,8 @@ struct | _ -> Maybe let set (ask: VDQ.t) (nulls, size) (e, i) v = - let min interval = Z.max Z.zero (BatOption.default Z.zero (Idx.minimal interval)) in - - let min_size = min size in - let min_i = min i in + let min_size = min_nat_of_idx size in + let min_i = min_nat_of_idx i in let max_i = Idx.maximal i in let set_exact_nulls i = @@ -1653,7 +1651,7 @@ struct | Some n when n >= 0 -> let n = Z.of_int n in let warn_size size name = - let min = BatOption.default Z.zero (Idx.minimal size) in + let min = min_nat_of_idx size in match Idx.maximal size with | Some max when n >. max -> warn_past_end "The size of the array of string %s is smaller than n bytes" name From 20ee375f30ee5072fdfd1f7340fc4dd85358ebe6 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 28 Nov 2023 21:05:29 +0100 Subject: [PATCH 1178/1312] One less May/MustSet --- src/cdomains/arrayDomain.ml | 13 +++++-------- src/cdomains/nullByteSet.ml | 2 ++ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 7cadd66c19..7818f5ac85 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1395,14 +1395,11 @@ struct (if min_size1 <. min_len2 then warn_past_end "The length of string src may be greater than the allocated size for dest"); (* do not keep any index of dest as no maximal strlen of src *) - let must_nulls_set_result = - let min_size2 = BatOption.default Z.zero (Idx.minimal truncatedsize) in - MustSet.filter ~min_size:min_size2 (Z.gt min_size1) must_nulls_set2' in - let may_nulls_set_result = - (* get all may nulls from src string as no maximal size of dest *) - may_nulls_set2' - |> MaySet.union (MaySet.filter ~max_size:(Z.succ min_len2) (Z.leq min_len2) may_nulls_set1) in - ((must_nulls_set_result, may_nulls_set_result), dstsize) + let min_size2 = BatOption.default Z.zero (Idx.minimal truncatedsize) in + let truncatednulls = Nulls.remove_interval Possibly (Z.zero, min_size1) min_size2 truncatednulls in + let filtered_dst = Nulls.filter ~max_size:(Z.succ min_len2) (Z.leq min_len2) dstnulls in + (* get all may nulls from src string as no maximal size of dest *) + (Nulls.union_mays truncatednulls filtered_dst, dstsize) (* any other case shouldn't happen as minimal index is always >= 0 *) | _ -> (Nulls.top (), dstsize) in diff --git a/src/cdomains/nullByteSet.ml b/src/cdomains/nullByteSet.ml index 54284f6ab5..53196bb43c 100644 --- a/src/cdomains/nullByteSet.ml +++ b/src/cdomains/nullByteSet.ml @@ -170,6 +170,8 @@ module MustMaySet = struct | Definitely ->failwith "todo" | Possibly -> MaySet.elements ?max_size mays + let union_mays (must,mays) (_,mays2) = (must, MaySet.join mays mays2) + let precise_singleton i = (MustSet.singleton i, MaySet.singleton i) From d995cc9ebb96833209b1b68b83acd5597509ebe4 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 28 Nov 2023 21:16:15 +0100 Subject: [PATCH 1179/1312] Decouple concrete sets from MaySet --- src/cdomains/arrayDomain.ml | 8 ++++---- src/cdomains/nullByteSet.ml | 4 +++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 7818f5ac85..a7b139a740 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1208,12 +1208,12 @@ struct let last_null = Z.of_int (String.length s) in let rec build_set i set = if (Z.of_int i) >=. last_null then - MaySet.add last_null set + Nulls.Set.add last_null set else match String.index_from_opt s i '\x00' with - | Some i -> build_set (i + 1) (MaySet.add (Z.of_int i) set) - | None -> MaySet.add last_null set in - let set = build_set 0 (MaySet.empty ()) in + | Some i -> build_set (i + 1) (Nulls.Set.add (Z.of_int i) set) + | None -> Nulls.Set.add last_null set in + let set = build_set 0 (Nulls.Set.empty ()) in (Nulls.precise_set set, Idx.of_int ILong (Z.succ last_null)) (** Returns an abstract value with at most one null byte marking the end of the string *) diff --git a/src/cdomains/nullByteSet.ml b/src/cdomains/nullByteSet.ml index 53196bb43c..a7f889ee5a 100644 --- a/src/cdomains/nullByteSet.ml +++ b/src/cdomains/nullByteSet.ml @@ -78,6 +78,8 @@ end module MustMaySet = struct include Lattice.Prod (MustSet) (MaySet) + module Set = SetDomain.Make (IntDomain.BigInt) + type mode = Definitely | Possibly let empty () = (MustSet.top (), MaySet.bot ()) @@ -176,7 +178,7 @@ module MustMaySet = struct let precise_singleton i = (MustSet.singleton i, MaySet.singleton i) - let precise_set s = (s,s) + let precise_set (s:Set.t):t = (`Lifted s,`Lifted s) let make_all_must () = (MustSet.bot (), MaySet.top ()) From 72476fbb1208600b2ff9bdd3bb417f89c6bb48d6 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 28 Nov 2023 21:36:52 +0100 Subject: [PATCH 1180/1312] Simplify --- src/cdomains/arrayDomain.ml | 16 +++++++--------- src/cdomains/nullByteSet.ml | 28 ++++++++++++++++------------ 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index a7b139a740..37d28fc206 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1260,13 +1260,6 @@ struct set else add_indexes (Z.succ i) max (MaySet.add i set) in - let update_must_indexes min_must_null must_nulls_set = - if min_must_null =. Z.zero then - MustSet.bot () - else - (* if strlen < n, every byte starting from min_must_null is surely also transformed to null *) - add_indexes min_must_null n must_nulls_set - |> MustSet.M.filter (Z.gt n) in let update_may_indexes min_may_null may_nulls_set = if min_may_null =. Z.zero then MaySet.top () @@ -1311,7 +1304,7 @@ struct Nulls.add_all Possibly nulls else let (must, mays) = Nulls.add_interval Possibly (min_may_null, Z.pred n) nulls in - (must, mays |> MaySet.M.filter (Z.gt n)) (* TODO: this makes little sense *) + (must, mays |> MaySet.M.filter (fun x -> x <. n)) (* TODO: this makes little sense *) else let min_must_null = Nulls.min_elem Definitely nulls in let min_may_null = Nulls.min_elem Possibly nulls in @@ -1319,7 +1312,12 @@ struct warn_no_null min_must_null true min_may_null; (* if min_must_null = min_may_null, remove indexes >= n and add all indexes from minimal must/may null to n - 1 in the sets *) if min_must_null =. min_may_null then - (update_must_indexes min_must_null must_nulls_set, update_may_indexes min_may_null may_nulls_set) + (if min_must_null =. Z.zero then + Nulls.full_set () + else + let nulls = Nulls.add_interval Definitely (min_must_null, Z.pred n) nulls in + let nulls = Nulls.add_interval Possibly (min_may_null, Z.pred n) nulls in + Nulls.filter (fun x -> x <. n) nulls) else (MustSet.top (), update_may_indexes min_may_null may_nulls_set) in diff --git a/src/cdomains/nullByteSet.ml b/src/cdomains/nullByteSet.ml index a7f889ee5a..38fe5cbda9 100644 --- a/src/cdomains/nullByteSet.ml +++ b/src/cdomains/nullByteSet.ml @@ -84,6 +84,8 @@ module MustMaySet = struct let empty () = (MustSet.top (), MaySet.bot ()) + let full_set () = (MustSet.bot (), MaySet.top ()) + let is_empty mode (musts, mays) = match mode with | Definitely -> MaySet.is_empty mays @@ -123,21 +125,23 @@ module MustMaySet = struct | Possibly -> (musts, MaySet.union (MaySet.of_list l) mays) let add_interval ?maxfull mode (l,u) (musts, mays) = - match mode with - | Definitely -> failwith "todo" - | Possibly -> + let rec add_indexes i max set = + if Z.gt i max then + set + else + add_indexes (Z.succ i) max (MaySet.add i set) + in + let mays = match maxfull with | Some Some maxfull when Z.equal l Z.zero && Z.geq u maxfull -> - (musts, MaySet.top ()) + MaySet.top () | _ -> - let rec add_indexes i max set = - if Z.gt i max then - set - else - add_indexes (Z.succ i) max (MaySet.add i set) - in - (musts, add_indexes l u mays) - + add_indexes l u mays + in + match mode with + | Definitely -> (add_indexes l u musts, mays) + | Possibly -> (musts, mays) + let remove_interval mode (l,u) min_size (musts, mays) = match mode with | Definitely -> failwith "todo" From a73c28d426a33d59202b500baba52e317415ca84 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 28 Nov 2023 21:43:44 +0100 Subject: [PATCH 1181/1312] Lift one more transfer function to work on MustMay --- src/cdomains/arrayDomain.ml | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 37d28fc206..1e75d9f31e 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1250,23 +1250,10 @@ struct * marking the end of the string and if needed followed by further null bytes to obtain * an n bytes string. *) let to_n_string (nulls, size) n:t = - let must_nulls_set, may_nulls_set = nulls in if n < 0 then (Nulls.top (), Idx.top_of ILong) else let n = Z.of_int n in - let rec add_indexes i max set = - if Z.geq i max then - set - else - add_indexes (Z.succ i) max (MaySet.add i set) in - let update_may_indexes min_may_null may_nulls_set = - if min_may_null =. Z.zero then - MaySet.top () - else - (* if minimal strlen < n, every byte starting from minimal may null index may be transformed to null *) - add_indexes min_may_null n may_nulls_set - |> MaySet.M.filter (Z.gt n) in let warn_no_null min_must_null exists_min_must_null min_may_null = if Z.geq min_may_null n then M.warn "Resulting string might not be null-terminated because src doesn't contain a null byte in the first n bytes" @@ -1303,8 +1290,8 @@ struct if min_may_null =. Z.zero then Nulls.add_all Possibly nulls else - let (must, mays) = Nulls.add_interval Possibly (min_may_null, Z.pred n) nulls in - (must, mays |> MaySet.M.filter (fun x -> x <. n)) (* TODO: this makes little sense *) + let nulls = Nulls.add_interval Possibly (min_may_null, Z.pred n) nulls in + Nulls.filter (fun x -> x <. n) nulls else let min_must_null = Nulls.min_elem Definitely nulls in let min_may_null = Nulls.min_elem Possibly nulls in @@ -1318,8 +1305,12 @@ struct let nulls = Nulls.add_interval Definitely (min_must_null, Z.pred n) nulls in let nulls = Nulls.add_interval Possibly (min_may_null, Z.pred n) nulls in Nulls.filter (fun x -> x <. n) nulls) - else - (MustSet.top (), update_may_indexes min_may_null may_nulls_set) + else if min_may_null =. Z.zero then + Nulls.top () + else + let nulls = Nulls.remove_all Possibly nulls in + let nulls = Nulls.add_interval Possibly (min_may_null, Z.pred n) nulls in + Nulls.filter (fun x -> x <. n) nulls in (nulls, Idx.of_int ILong n) From c15ca04f04062425a06d23aca75a5f1b7be2077f Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 28 Nov 2023 21:50:15 +0100 Subject: [PATCH 1182/1312] Use option type --- src/cdomains/arrayDomain.ml | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 1e75d9f31e..954bf757d1 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1254,11 +1254,15 @@ struct (Nulls.top (), Idx.top_of ILong) else let n = Z.of_int n in - let warn_no_null min_must_null exists_min_must_null min_may_null = + let warn_no_null min_must_null min_may_null = if Z.geq min_may_null n then M.warn "Resulting string might not be null-terminated because src doesn't contain a null byte in the first n bytes" - else if (exists_min_must_null && (min_must_null >=. n) || min_must_null >. min_may_null) || not exists_min_must_null then - M.warn "Resulting string might not be null-terminated because src might not contain a null byte in the first n bytes" + else + (match min_must_null with + | Some min_must_null when not (min_must_null >=. n || min_must_null >. min_may_null) -> () + | _ -> + M.warn "Resulting string might not be null-terminated because src might not contain a null byte in the first n bytes" + ) in (match Idx.minimal size, Idx.maximal size with | Some min_size, Some max_size -> @@ -1286,7 +1290,7 @@ struct * warn as in any case, resulting array not guaranteed to contain null byte *) else if Nulls.is_empty Possibly nulls then let min_may_null = Nulls.min_elem Possibly nulls in - warn_no_null Z.zero false min_may_null; + warn_no_null None min_may_null; if min_may_null =. Z.zero then Nulls.add_all Possibly nulls else @@ -1296,15 +1300,15 @@ struct let min_must_null = Nulls.min_elem Definitely nulls in let min_may_null = Nulls.min_elem Possibly nulls in (* warn if resulting array may not contain null byte *) - warn_no_null min_must_null true min_may_null; + warn_no_null (Some min_must_null) min_may_null; (* if min_must_null = min_may_null, remove indexes >= n and add all indexes from minimal must/may null to n - 1 in the sets *) if min_must_null =. min_may_null then - (if min_must_null =. Z.zero then + if min_must_null =. Z.zero then Nulls.full_set () else let nulls = Nulls.add_interval Definitely (min_must_null, Z.pred n) nulls in let nulls = Nulls.add_interval Possibly (min_may_null, Z.pred n) nulls in - Nulls.filter (fun x -> x <. n) nulls) + Nulls.filter (fun x -> x <. n) nulls else if min_may_null =. Z.zero then Nulls.top () else From 1a9ce2c4c16f5feed5d4450ba06c305db99ba446 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 28 Nov 2023 21:56:12 +0100 Subject: [PATCH 1183/1312] Strange parens --- src/cdomains/arrayDomain.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 954bf757d1..d197928f3e 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1484,7 +1484,7 @@ struct | _ -> (Nulls.top (), size1)) | _ -> (Nulls.top (), size1) (* if minimal must null = minimal may null in ar1 and ar2, add them together and keep indexes > strlen(dest) + strlen(src) of ar1 *) - else if Nulls.min_elem_precise (nulls1) && Nulls.min_elem_precise nulls2' then + else if Nulls.min_elem_precise nulls1 && Nulls.min_elem_precise nulls2' then let min_i1 = Nulls.min_elem Definitely nulls1 in let min_i2 = Nulls.min_elem Definitely nulls2' in let min_i = min_i1 +. min_i2 in From 30daf274c92d7bd178920aa4ff089a4d08c077df Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 29 Nov 2023 10:25:53 +0200 Subject: [PATCH 1184/1312] Simplify match in MemLeak Co-authored-by: Michael Schwarz --- src/analyses/memLeak.ml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index 1253cd6763..8fc2cc663a 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -233,12 +233,11 @@ struct | a when Queries.ID.is_bot a -> M.warn ~category:Assert "assert expression %a is bottom" d_exp exp | a -> begin match Queries.ID.to_bool a with - | Some b -> + | Some true -> () + | Some false -> (* If we know for sure that the expression in "assert" is false => need to check for memory leaks *) - if b = false then ( - warn_for_multi_threaded_due_to_abort ctx; - check_for_mem_leak ctx - ) + warn_for_multi_threaded_due_to_abort ctx; + check_for_mem_leak ctx | None -> warn_for_multi_threaded_due_to_abort ctx; check_for_mem_leak ctx ~assert_exp_imprecise:true ~exp:(Some exp) From 7fa7bfd222d464da93db98d10a5377baeb261ce0 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 29 Nov 2023 10:27:34 +0200 Subject: [PATCH 1185/1312] Remove unit statement from MemLeak --- src/analyses/memLeak.ml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index 8fc2cc663a..456d434be7 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -228,8 +228,7 @@ struct warn_for_multi_threaded_due_to_abort ctx; state | Assert { exp; _ } -> - let warn_for_assert_exp = - match ctx.ask (Queries.EvalInt exp) with + begin match ctx.ask (Queries.EvalInt exp) with | a when Queries.ID.is_bot a -> M.warn ~category:Assert "assert expression %a is bottom" d_exp exp | a -> begin match Queries.ID.to_bool a with @@ -242,8 +241,7 @@ struct warn_for_multi_threaded_due_to_abort ctx; check_for_mem_leak ctx ~assert_exp_imprecise:true ~exp:(Some exp) end - in - warn_for_assert_exp; + end; state | ThreadExit _ -> begin match ctx.ask (Queries.CurrentThreadId) with From df60d5262da0d5dde855c7af5a3b849804604645 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 29 Nov 2023 10:49:12 +0200 Subject: [PATCH 1186/1312] Deduplicate ArrayDomain.StrWithDomain declarations --- src/cdomains/arrayDomain.ml | 4 +--- src/cdomains/arrayDomain.mli | 5 +---- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index d197928f3e..142b0dfb93 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -90,9 +90,7 @@ end module type StrWithDomain = sig include Str - - val domain_of_t: t -> domain - val get: ?checkBounds:bool -> VDQ.t -> t -> Basetype.CilExp.t option * idx -> value + include S with type t := t and type idx := idx end module type LatticeWithInvalidate = diff --git a/src/cdomains/arrayDomain.mli b/src/cdomains/arrayDomain.mli index 0fe08f2cfb..2578d961ce 100644 --- a/src/cdomains/arrayDomain.mli +++ b/src/cdomains/arrayDomain.mli @@ -106,10 +106,7 @@ end module type StrWithDomain = sig include Str - - val domain_of_t: t -> domain - (* Returns the domain used for the array *) - val get: ?checkBounds:bool -> VDQ.t -> t -> Basetype.CilExp.t option * idx -> value + include S with type t := t and type idx := idx end module type LatticeWithInvalidate = From 309f000815ab3be1f0fea30d2a57a4cca0142eff Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 29 Nov 2023 10:49:20 +0200 Subject: [PATCH 1187/1312] Remove trailing whitespace in ArrayDomain --- src/cdomains/arrayDomain.ml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 142b0dfb93..f10a55ce9e 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1227,7 +1227,7 @@ struct let new_size = Idx.of_int ILong (Z.succ min_must_null) in let min_may_null = Nulls.min_elem Possibly nulls in (* if smallest index in sets coincides, only this null byte is kept in both sets *) - let nulls = + let nulls = if min_must_null =. min_may_null then Nulls.precise_singleton min_must_null (* else return empty must_nulls_set and keep every index up to smallest index of must_nulls_set included in may_nulls_set *) @@ -1255,7 +1255,7 @@ struct let warn_no_null min_must_null min_may_null = if Z.geq min_may_null n then M.warn "Resulting string might not be null-terminated because src doesn't contain a null byte in the first n bytes" - else + else (match min_must_null with | Some min_must_null when not (min_must_null >=. n || min_must_null >. min_may_null) -> () | _ -> @@ -1309,7 +1309,7 @@ struct Nulls.filter (fun x -> x <. n) nulls else if min_may_null =. Z.zero then Nulls.top () - else + else let nulls = Nulls.remove_all Possibly nulls in let nulls = Nulls.add_interval Possibly (min_may_null, Z.pred n) nulls in Nulls.filter (fun x -> x <. n) nulls @@ -1437,7 +1437,7 @@ struct (if GobOption.exists (fun x -> x <=. (minlen1 +. minlen2)) max_size1 then warn_past_end "The length of the concatenation of the strings in src and dest is greater than the allocated size for dest" - else + else (match maxlen1, maxlen2 with | Some maxlen1, Some maxlen2 when min_size1 >. (maxlen1 +. maxlen2) -> () | _ -> warn_past_end @@ -1451,7 +1451,7 @@ struct | Some max_size1 -> let nulls1_no_must = Nulls.remove_all Possibly nulls1 in let pred = match maxlen1, maxlen2 with - | Some maxlen1, Some maxlen2 -> (fun x -> x <=. (maxlen1 +. maxlen2)) + | Some maxlen1, Some maxlen2 -> (fun x -> x <=. (maxlen1 +. maxlen2)) | _ -> (fun _ -> true) in let r = @@ -1509,16 +1509,16 @@ struct match Idx.maximal size2 with | Some max_size2 -> MaySet.filter ~max_size:max_size2 (Z.geq min_i2) may_nulls_set2' | None -> MaySet.filter ~max_size:(Z.succ min_i2) (Z.geq min_i2) may_nulls_set2' in - let must_nulls_set_result = + let must_nulls_set_result = let pred = match maxlen1, maxlen2 with - | Some maxlen1, Some maxlen2 -> (fun x -> (maxlen1 +. maxlen2) <. x) + | Some maxlen1, Some maxlen2 -> (fun x -> (maxlen1 +. maxlen2) <. x) | _ -> (fun _ -> false) in - MustSet.filter ~min_size:min_size1 pred must_nulls_set1 + MustSet.filter ~min_size:min_size1 pred must_nulls_set1 in let may_nulls_set_result = let pred = match maxlen1, maxlen2 with - | Some maxlen1, Some maxlen2 -> (fun x -> x <=. (maxlen1 +. maxlen2)) + | Some maxlen1, Some maxlen2 -> (fun x -> x <=. (maxlen1 +. maxlen2)) | _ -> (fun _ -> true) in match max_size1 with @@ -1546,7 +1546,7 @@ struct let strlen2 = to_string_length (nulls2', size2) in match Idx.minimal size1, Idx.minimal strlen1, Idx.minimal strlen2 with | Some min_size1, Some minlen1, Some minlen2 -> - begin + begin let f = update_sets min_size1 (Idx.maximal size1) minlen1 in match Idx.maximal strlen1, Idx.maximal strlen2 with | (Some _ as maxlen1), (Some _ as maxlen2) -> f maxlen1 minlen2 maxlen2 nulls2' From 44705f4ad8e987ce92a078a06f31eb394ee8b6ae Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 29 Nov 2023 10:51:20 +0200 Subject: [PATCH 1188/1312] Use ocamldoc references in ArrayDomain.Str --- src/cdomains/arrayDomain.mli | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cdomains/arrayDomain.mli b/src/cdomains/arrayDomain.mli index 2578d961ce..e7db47a708 100644 --- a/src/cdomains/arrayDomain.mli +++ b/src/cdomains/arrayDomain.mli @@ -93,9 +93,9 @@ sig * [s2] if present *) val substring_extraction: t -> t -> substr - (** [substring_extraction haystack needle] returns [IsNotSubstr] if the string represented by - * the abstract value [needle] surely isn't a substring of [haystack], [IsSubstrAtIndex0] if - * [needle] is the empty string, else [Unknown] *) + (** [substring_extraction haystack needle] returns {!IsNotSubstr} if the string represented by + * the abstract value [needle] surely isn't a substring of [haystack], {!IsSubstrAtIndex0} if + * [needle] is the empty string, else {!IsMaybeSubstr} *) val string_comparison: t -> t -> int option -> idx (** [string_comparison s1 s2 n] returns a negative / positive idx element if the string From 80c2694db222710e0e908389ed9e69905350ec64 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 29 Nov 2023 11:00:30 +0200 Subject: [PATCH 1189/1312] Deduplicate Null declarations --- src/cdomains/arrayDomain.ml | 10 ++++++++-- src/cdomains/arrayDomain.mli | 10 ++++++++-- src/cdomains/valueDomain.ml | 8 +------- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index f10a55ce9e..6c47f1e87a 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -107,9 +107,9 @@ sig val smart_leq: (Cil.exp -> BI.t option) -> (Cil.exp -> BI.t option) -> t -> t -> bool end -module type LatticeWithNull = +module type Null = sig - include LatticeWithSmartOps + type t type retnull = Null | NotNull | Maybe val null: unit -> t @@ -120,6 +120,12 @@ sig val not_zero_of_ikind: Cil.ikind -> t end +module type LatticeWithNull = +sig + include LatticeWithSmartOps + include Null with type t := t +end + module Trivial (Val: LatticeWithInvalidate) (Idx: Lattice.S): S with type value = Val.t and type idx = Idx.t = struct include Val diff --git a/src/cdomains/arrayDomain.mli b/src/cdomains/arrayDomain.mli index e7db47a708..9b5a713859 100644 --- a/src/cdomains/arrayDomain.mli +++ b/src/cdomains/arrayDomain.mli @@ -123,9 +123,9 @@ sig val smart_leq: (Cil.exp -> BigIntOps.t option) -> (Cil.exp -> BigIntOps.t option) -> t -> t -> bool end -module type LatticeWithNull = +module type Null = sig - include LatticeWithSmartOps + type t type retnull = Null | NotNull | Maybe val null: unit -> t @@ -136,6 +136,12 @@ sig val not_zero_of_ikind: Cil.ikind -> t end +module type LatticeWithNull = +sig + include LatticeWithSmartOps + include Null with type t := t +end + module Trivial (Val: LatticeWithInvalidate) (Idx: Lattice.S): S with type value = Val.t and type idx = Idx.t (** This functor creates a trivial single cell representation of an array. The * indexing type is taken as a parameter to satisfy the type system, it is not diff --git a/src/cdomains/valueDomain.ml b/src/cdomains/valueDomain.ml index 9dfc65a1f1..4a83447e97 100644 --- a/src/cdomains/valueDomain.ml +++ b/src/cdomains/valueDomain.ml @@ -39,13 +39,7 @@ sig val is_top_value: t -> typ -> bool val zero_init_value: ?varAttr:attributes -> typ -> t - type retnull = Null | NotNull | Maybe - val null: unit -> t - val is_null: t -> retnull - - val get_ikind: t -> Cil.ikind option - val zero_of_ikind: Cil.ikind -> t - val not_zero_of_ikind: Cil.ikind -> t + include ArrayDomain.Null with type t := t val project: VDQ.t -> int_precision option-> ( attributes * attributes ) option -> t -> t val mark_jmpbufs_as_copied: t -> t From 71489df7d186e4a3ad81c88c95adda5a1e55b99a Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 1 Dec 2023 20:12:09 +0100 Subject: [PATCH 1190/1312] Introduce Printable.Either3 --- src/analyses/basePriv.ml | 27 ++++++++++++-------------- src/analyses/commonPriv.ml | 7 +++---- src/common/domains/printable.ml | 34 +++++++++++++++++++++++++++++++++ src/framework/constraints.ml | 12 ++++++------ 4 files changed, 55 insertions(+), 25 deletions(-) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index 3843dda300..e42cd5a309 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -211,12 +211,12 @@ struct let thread_join ?(force=false) ask get e st = st let thread_return ask get set tid st = st - let invariant_global getg g = - match g with - | `Left _ -> (* mutex *) - Invariant.none + let invariant_global getg = function | `Right g' -> (* global *) ValueDomain.invariant_global (read_unprotected_global getg) g' + | _ -> (* mutex *) + Invariant.none + end module PerMutexOplusPriv: S = @@ -625,13 +625,11 @@ struct let get_mutex_inits' = CPA.find x get_mutex_inits in VD.join get_mutex_global_x' get_mutex_inits' - let invariant_global getg g = - match g with - | `Left (`Left _) -> (* mutex *) - Invariant.none - | `Left (`Right g') -> (* global *) - ValueDomain.invariant_global (read_unprotected_global getg) g' - | `Right _ -> (* thread *) + let invariant_global getg = function + | `Middle g -> (* global *) + ValueDomain.invariant_global (read_unprotected_global getg) g + | `Left _ + | `Right _ -> (* mutex or thread *) Invariant.none end @@ -847,16 +845,15 @@ struct open Locksets - let invariant_global getg g = - match g with - | `Left _ -> (* mutex *) - Invariant.none + let invariant_global getg = function | `Right g' -> (* global *) ValueDomain.invariant_global (fun x -> GWeak.fold (fun s' tm acc -> WeakRange.fold_weak VD.join tm acc ) (G.weak (getg (V.global x))) (VD.bot ()) ) g' + | _ -> (* mutex *) + Invariant.none let invariant_vars ask getg st = let module VS = Set.Make (CilType.Varinfo) in diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index 88181000b9..73a2e75de1 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -85,11 +85,10 @@ struct end module V = struct - (* TODO: Either3? *) - include Printable.Either (struct include Printable.Either (VMutex) (VMutexInits) let name () = "mutex" end) (VGlobal) + include Printable.Either3 (VMutex) (VMutexInits) (VGlobal) let name () = "MutexGlobals" - let mutex x: t = `Left (`Left x) - let mutex_inits: t = `Left (`Right ()) + let mutex x: t = `Left x + let mutex_inits: t = `Middle () let global x: t = `Right x end diff --git a/src/common/domains/printable.ml b/src/common/domains/printable.ml index b0755fb730..3499cfdb04 100644 --- a/src/common/domains/printable.ml +++ b/src/common/domains/printable.ml @@ -273,6 +273,40 @@ struct | `Right x -> `Right (Base2.relift x) end +module Either3 (Base1: S) (Base2: S) (Base3: S) = +struct + type t = [`Left of Base1.t | `Middle of Base2.t | `Right of Base3.t] [@@deriving eq, ord, hash] + include Std + + let pretty () (state:t) = + match state with + | `Left n -> Pretty.dprintf "%s:%a" (Base1.name ()) Base1.pretty n + | `Middle n -> Pretty.dprintf "%s:%a" (Base2.name ()) Base2.pretty n + | `Right n -> Pretty.dprintf "%s:%a" (Base3.name ()) Base3.pretty n + + let show state = + match state with + | `Left n -> (Base1.name ()) ^ ":" ^ Base1.show n + | `Middle n -> (Base2.name ()) ^ ":" ^ Base2.show n + | `Right n -> (Base3.name ()) ^ ":" ^ Base3.show n + + let name () = "either " ^ Base1.name () ^ " or " ^ Base2.name () ^ " or " ^ Base3.name () + let printXml f = function + | `Left x -> BatPrintf.fprintf f "\n\nLeft\n\n%a\n\n" Base1.printXml x + | `Middle x -> BatPrintf.fprintf f "\n\nMiddle\n\n%a\n\n" Base2.printXml x + | `Right x -> BatPrintf.fprintf f "\n\nRight\n\n%a\n\n" Base3.printXml x + + let to_yojson = function + | `Left x -> `Assoc [ Base1.name (), Base1.to_yojson x ] + | `Middle x -> `Assoc [ Base2.name (), Base2.to_yojson x ] + | `Right x -> `Assoc [ Base3.name (), Base3.to_yojson x ] + + let relift = function + | `Left x -> `Left (Base1.relift x) + | `Middle x -> `Middle (Base2.relift x) + | `Right x -> `Right (Base3.relift x) +end + module Option (Base: S) (N: Name) = struct type t = Base.t option [@@deriving eq, ord, hash] diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index b1bbc73660..b6046d023b 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1467,14 +1467,14 @@ struct module V = struct - include Printable.Either (S.V) (Printable.Either (Printable.Prod (Node) (C)) (Printable.Prod (CilType.Fundec) (C))) + include Printable.Either3 (S.V) (Printable.Prod (Node) (C)) (Printable.Prod (CilType.Fundec) (C)) let name () = "longjmp" let s x = `Left x - let longjmpto x = `Right (`Left x) - let longjmpret x = `Right (`Right x) + let longjmpto x = `Middle x + let longjmpret x = `Right x let is_write_only = function | `Left x -> S.V.is_write_only x - | `Right _ -> false + | _ -> false end module G = @@ -1511,7 +1511,7 @@ struct begin match g with | `Left g -> S.query (conv ctx) (WarnGlobal (Obj.repr g)) - | `Right g -> + | _ -> Queries.Result.top q end | InvariantGlobal g -> @@ -1519,7 +1519,7 @@ struct begin match g with | `Left g -> S.query (conv ctx) (InvariantGlobal (Obj.repr g)) - | `Right g -> + | _ -> Queries.Result.top q end | IterSysVars (vq, vf) -> From 11516b13fc1070ac0463a51ccf79c46e8cf5ea8f Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 1 Dec 2023 20:23:31 +0100 Subject: [PATCH 1191/1312] Fix name of modifiedSinceSetjmp --- src/analyses/accessAnalysis.ml | 2 +- .../{modifiedSinceLongjmp.ml => modifiedSinceSetjmp.ml} | 6 ++---- src/autoTune.ml | 2 +- src/goblint_lib.ml | 2 +- 4 files changed, 5 insertions(+), 7 deletions(-) rename src/analyses/{modifiedSinceLongjmp.ml => modifiedSinceSetjmp.ml} (96%) diff --git a/src/analyses/accessAnalysis.ml b/src/analyses/accessAnalysis.ml index b181a1c70e..efad8b4c2e 100644 --- a/src/analyses/accessAnalysis.ml +++ b/src/analyses/accessAnalysis.ml @@ -29,7 +29,7 @@ struct let init _ = collect_local := get_bool "witness.yaml.enabled" && get_bool "witness.invariant.accessed"; let activated = get_string_list "ana.activated" in - emit_single_threaded := List.mem (ModifiedSinceLongjmp.Spec.name ()) activated || List.mem (PoisonVariables.Spec.name ()) activated + emit_single_threaded := List.mem (ModifiedSinceSetjmp.Spec.name ()) activated || List.mem (PoisonVariables.Spec.name ()) activated let do_access (ctx: (D.t, G.t, C.t, V.t) ctx) (kind:AccessKind.t) (reach:bool) (e:exp) = if M.tracing then M.trace "access" "do_access %a %a %B\n" d_exp e AccessKind.pretty kind reach; diff --git a/src/analyses/modifiedSinceLongjmp.ml b/src/analyses/modifiedSinceSetjmp.ml similarity index 96% rename from src/analyses/modifiedSinceLongjmp.ml rename to src/analyses/modifiedSinceSetjmp.ml index a129c9f92c..93e55b2a17 100644 --- a/src/analyses/modifiedSinceLongjmp.ml +++ b/src/analyses/modifiedSinceSetjmp.ml @@ -1,6 +1,4 @@ -(** Analysis of variables modified since [setjmp] ([modifiedSinceLongjmp]). *) - -(* TODO: this name is wrong *) +(** Analysis of variables modified since [setjmp] ([modifiedSinceSetjmp]). *) open GoblintCil open Analyses @@ -9,7 +7,7 @@ module Spec = struct include Analyses.IdentitySpec - let name () = "modifiedSinceLongjmp" + let name () = "modifiedSinceSetjmp" module D = JmpBufDomain.LocallyModifiedMap module VS = D.VarSet module C = Lattice.Unit diff --git a/src/autoTune.ml b/src/autoTune.ml index 0c3d3727f0..3cda36a302 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -200,7 +200,7 @@ let reduceThreadAnalyses () = (* This is run independent of the autotuner being enabled or not to be sound in the presence of setjmp/longjmp *) (* It is done this way around to allow enabling some of these analyses also for programs without longjmp *) -let longjmpAnalyses = ["activeLongjmp"; "activeSetjmp"; "taintPartialContexts"; "modifiedSinceLongjmp"; "poisonVariables"; "expsplit"; "vla"] +let longjmpAnalyses = ["activeLongjmp"; "activeSetjmp"; "taintPartialContexts"; "modifiedSinceSetjmp"; "poisonVariables"; "expsplit"; "vla"] let activateLongjmpAnalysesWhenRequired () = let isLongjmp = function diff --git a/src/goblint_lib.ml b/src/goblint_lib.ml index 70f331b5ac..66ab2c76a4 100644 --- a/src/goblint_lib.ml +++ b/src/goblint_lib.ml @@ -130,7 +130,7 @@ module ExtractPthread = ExtractPthread Analyses related to [longjmp] and [setjmp]. *) module ActiveSetjmp = ActiveSetjmp -module ModifiedSinceLongjmp = ModifiedSinceLongjmp +module ModifiedSinceSetjmp = ModifiedSinceSetjmp module ActiveLongjmp = ActiveLongjmp module PoisonVariables = PoisonVariables module Vla = Vla From 89698bc46dd00b27a89cb726c82097419c504f57 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 1 Dec 2023 20:45:22 +0100 Subject: [PATCH 1192/1312] RFC: Remove spec & file analysis --- docs/developer-guide/messaging.md | 13 - scripts/goblint-lib-modules.py | 2 - scripts/spec/check.sh | 27 - scripts/spec/regression.py | 61 --- scripts/spec/regression.sh | 18 - scripts/spec/spec.sh | 10 - src/analyses/fileUse.ml | 296 ----------- src/analyses/spec.ml | 496 ------------------ src/common/util/options.schema.json | 26 - src/goblint_lib.ml | 2 - src/main.camldoc | 2 - src/mainspec.ml | 13 - src/spec/dune | 2 - src/spec/file.dot | 37 -- src/spec/render.sh | 31 -- src/spec/specCore.ml | 152 ------ src/spec/specLexer.mll | 67 --- src/spec/specParser.mly | 116 ---- src/spec/specUtil.ml | 52 -- tests/regression/18-file/01-ok.c | 12 - tests/regression/18-file/02-function.c | 17 - tests/regression/18-file/03-if-close.c | 15 - tests/regression/18-file/04-no-open.c | 10 - tests/regression/18-file/05-open-mode.c | 11 - tests/regression/18-file/06-2open.c | 12 - tests/regression/18-file/07-2close.c | 11 - tests/regression/18-file/08-var-reuse.c | 15 - .../regression/18-file/09-inf-loop-no-close.c | 17 - tests/regression/18-file/10-inf-loop-ok.c | 19 - tests/regression/18-file/11-2if.c | 18 - tests/regression/18-file/12-2close-if.c | 15 - tests/regression/18-file/13-ptr-arith-ok.c | 16 - tests/regression/18-file/14-ptr-arith-close.c | 13 - tests/regression/18-file/15-var-switch.c | 18 - tests/regression/18-file/16-var-reuse-close.c | 14 - tests/regression/18-file/17-myfopen.c | 21 - tests/regression/18-file/18-myfopen-arg.c | 20 - tests/regression/18-file/19-if-close-else.c | 17 - tests/regression/18-file/20-loop-close.c | 18 - tests/regression/18-file/21-for-i.c | 26 - tests/regression/18-file/22-f_int.c | 13 - tests/regression/18-file/23-f_str.c | 13 - tests/regression/18-file/24-f_wstr.c | 14 - tests/regression/18-file/25-mem-ok.c | 29 - tests/regression/18-file/26-open-error-ok.c | 15 - tests/regression/18-file/27-open-error.c | 13 - tests/regression/18-file/28-multiple-exits.c | 14 - tests/regression/18-file/29-alias-global.c | 22 - tests/regression/18-file/30-ptr-of-ptr.c | 14 - tests/regression/18-file/31-var-reuse-fun.c | 16 - tests/regression/18-file/32-multi-ptr-close.c | 25 - tests/regression/18-file/33-multi-ptr-open.c | 23 - .../regression/18-file/34-multi-alias-close.c | 25 - .../regression/18-file/35-multi-alias-open.c | 23 - tests/regression/18-file/36-fun-ptr.c | 14 - .../regression/18-file/37-var-switch-alias.c | 18 - tests/regression/18-file/README.md | 2 + tests/regression/18-file/file.c | 44 -- tests/regression/18-file/file.optimistic.spec | 34 -- tests/regression/18-file/file.spec | 57 -- tests/regression/19-spec/01-malloc-free.c | 19 - tests/regression/19-spec/02-mutex_rc.c | 23 - tests/regression/19-spec/README.md | 2 + .../regression/19-spec/malloc.optimistic.spec | 23 - tests/regression/19-spec/malloc.spec | 26 - tests/regression/19-spec/mutex-lock.spec | 31 -- 66 files changed, 4 insertions(+), 2306 deletions(-) delete mode 100755 scripts/spec/check.sh delete mode 100755 scripts/spec/regression.py delete mode 100755 scripts/spec/regression.sh delete mode 100755 scripts/spec/spec.sh delete mode 100644 src/analyses/fileUse.ml delete mode 100644 src/analyses/spec.ml delete mode 100644 src/mainspec.ml delete mode 100644 src/spec/dune delete mode 100644 src/spec/file.dot delete mode 100755 src/spec/render.sh delete mode 100644 src/spec/specCore.ml delete mode 100644 src/spec/specLexer.mll delete mode 100644 src/spec/specParser.mly delete mode 100644 src/spec/specUtil.ml delete mode 100644 tests/regression/18-file/01-ok.c delete mode 100644 tests/regression/18-file/02-function.c delete mode 100644 tests/regression/18-file/03-if-close.c delete mode 100644 tests/regression/18-file/04-no-open.c delete mode 100644 tests/regression/18-file/05-open-mode.c delete mode 100644 tests/regression/18-file/06-2open.c delete mode 100644 tests/regression/18-file/07-2close.c delete mode 100644 tests/regression/18-file/08-var-reuse.c delete mode 100644 tests/regression/18-file/09-inf-loop-no-close.c delete mode 100644 tests/regression/18-file/10-inf-loop-ok.c delete mode 100644 tests/regression/18-file/11-2if.c delete mode 100644 tests/regression/18-file/12-2close-if.c delete mode 100644 tests/regression/18-file/13-ptr-arith-ok.c delete mode 100644 tests/regression/18-file/14-ptr-arith-close.c delete mode 100644 tests/regression/18-file/15-var-switch.c delete mode 100644 tests/regression/18-file/16-var-reuse-close.c delete mode 100644 tests/regression/18-file/17-myfopen.c delete mode 100644 tests/regression/18-file/18-myfopen-arg.c delete mode 100644 tests/regression/18-file/19-if-close-else.c delete mode 100644 tests/regression/18-file/20-loop-close.c delete mode 100644 tests/regression/18-file/21-for-i.c delete mode 100644 tests/regression/18-file/22-f_int.c delete mode 100644 tests/regression/18-file/23-f_str.c delete mode 100644 tests/regression/18-file/24-f_wstr.c delete mode 100644 tests/regression/18-file/25-mem-ok.c delete mode 100644 tests/regression/18-file/26-open-error-ok.c delete mode 100644 tests/regression/18-file/27-open-error.c delete mode 100644 tests/regression/18-file/28-multiple-exits.c delete mode 100644 tests/regression/18-file/29-alias-global.c delete mode 100644 tests/regression/18-file/30-ptr-of-ptr.c delete mode 100644 tests/regression/18-file/31-var-reuse-fun.c delete mode 100644 tests/regression/18-file/32-multi-ptr-close.c delete mode 100644 tests/regression/18-file/33-multi-ptr-open.c delete mode 100644 tests/regression/18-file/34-multi-alias-close.c delete mode 100644 tests/regression/18-file/35-multi-alias-open.c delete mode 100644 tests/regression/18-file/36-fun-ptr.c delete mode 100644 tests/regression/18-file/37-var-switch-alias.c create mode 100644 tests/regression/18-file/README.md delete mode 100644 tests/regression/18-file/file.c delete mode 100644 tests/regression/18-file/file.optimistic.spec delete mode 100644 tests/regression/18-file/file.spec delete mode 100644 tests/regression/19-spec/01-malloc-free.c delete mode 100644 tests/regression/19-spec/02-mutex_rc.c create mode 100644 tests/regression/19-spec/README.md delete mode 100644 tests/regression/19-spec/malloc.optimistic.spec delete mode 100644 tests/regression/19-spec/malloc.spec delete mode 100644 tests/regression/19-spec/mutex-lock.spec diff --git a/docs/developer-guide/messaging.md b/docs/developer-guide/messaging.md index 28f24bc49c..0028d51f87 100644 --- a/docs/developer-guide/messaging.md +++ b/docs/developer-guide/messaging.md @@ -47,16 +47,3 @@ The `~loc` argument is optional and defaults to the current location, but allows The `_noloc` suffixed functions allow general messages without any location (not even current). By convention, may-warnings (the usual case) should use warning severity and must-warnings should use error severity. - -### Spec analysis - -Warnings inside `.spec` files are converted to warnings. -They parsed from string warnings: the first space-delimited substring determines the category and the rest determines the text. - -For example: -``` -w1 "behavior.undefined.use_after_free" -w2 "integer.overflow" -w3 "unknown my message" -w4 "integer.overflow some text describing the warning" -``` diff --git a/scripts/goblint-lib-modules.py b/scripts/goblint-lib-modules.py index 5f02271616..6369af53a1 100755 --- a/scripts/goblint-lib-modules.py +++ b/scripts/goblint-lib-modules.py @@ -42,8 +42,6 @@ "MessageCategory", # included in Messages "PreValueDomain", # included in ValueDomain - "SpecCore", # spec stuff - "SpecUtil", # spec stuff "ConfigVersion", "ConfigProfile", diff --git a/scripts/spec/check.sh b/scripts/spec/check.sh deleted file mode 100755 index 57b63edfd2..0000000000 --- a/scripts/spec/check.sh +++ /dev/null @@ -1,27 +0,0 @@ -export OCAMLRUNPARAM=b -# file to analyze -file=${1-"tests/file.c"} -# analysis to run or spec file -ana=${2-"tests/regression/18-file/file.optimistic.spec"} -debug=${debug-"true"} -if [ $ana == "file" ]; then - ana="file" - opt="--set ana.file.optimistic true" -else - spec=$ana - ana="spec" - opt="--set ana.spec.file $spec" -fi -cmd="./goblint --set ana.activated[0][+] $ana $opt --html --set warn.debug $debug $file" -echo -e "$(tput setaf 6)$cmd$(tput sgr 0)" -$cmd - - -# # focuses Firefox and reloads current tab -# if false && command -v xdotool >/dev/null 2>&1; then -# WID=`xdotool search --name "Mozilla Firefox" | head -1` -# xdotool windowactivate $WID -# #xdotool key F5 -# # reload is done by add-on Auto Reload (reload result/* on change of report.html) -# # https://addons.mozilla.org/en-US/firefox/addon/auto-reload/?src=api -# fi diff --git a/scripts/spec/regression.py b/scripts/spec/regression.py deleted file mode 100755 index dc9f9fa276..0000000000 --- a/scripts/spec/regression.py +++ /dev/null @@ -1,61 +0,0 @@ -# import fileinput -# for line in fileinput.input(): -# pass - -import sys, os -import re - -if len(sys.argv) != 2: - print("Stdin: output from goblint, 1. argument: C source-file") - sys.exit(1) -path = sys.argv[1] - -goblint = {} -for line in sys.stdin.readlines(): - line = re.sub(r"\033.*?m", "", line) - m = re.match(r"(.+) \("+re.escape(path)+":(.+)\)", line) - if m: goblint[int(m.group(2))] = m.group(1) - -source = {} -lines = open(path).readlines() -for i,line in zip(range(1, len(lines)+1), lines): - m = re.match(r".+ // WARN: (.+)", line) - if m: source[i] = m.group(1) - -diff = {}; -for k,v in sorted(set.union(set(goblint.items()), set(source.items()))): - if k in diff: continue - if k in goblint and k in source and goblint[k]!=source[k]: - diff[k] = ('D', [goblint[k], source[k]]) - elif (k,v) in goblint.items() and (k,v) not in source.items(): - diff[k] = ('G', [goblint[k]]) - elif (k,v) not in goblint.items() and (k,v) in source.items(): - diff[k] = ('S', [source[k]]) - -if not len(diff): - sys.exit(0) - -print("#"*50) -print(path) -print("file://"+os.getcwd()+"/result/"+os.path.basename(path)+".html") - -if len(goblint): - print("## Goblint warnings:") - for k,v in sorted(goblint.items()): - print("{} \t {}".format(k, v)) - print - -if len(source): - print("## Source warnings:") - for k,v in source.items(): - print("{} \t {}".format(k, v)) - print - -if len(diff): - print("## Diff (G..only goblint, S..only source, D..different):") - for k,(s,v) in sorted(diff.items()): - print("{} {} \t {}".format(s, k, v[0])) - for v in v[1:]: print("\t {}".format(v)) - -print -sys.exit(1) \ No newline at end of file diff --git a/scripts/spec/regression.sh b/scripts/spec/regression.sh deleted file mode 100755 index 6dc740ca75..0000000000 --- a/scripts/spec/regression.sh +++ /dev/null @@ -1,18 +0,0 @@ -debug_tmp=$debug -export debug=false # temporarily disable debug output -n=0 -c=0 -dir=${2-"tests/regression/18-file"} -for f in $dir/*.c; do - ./scripts/spec/check.sh $f ${1-"file"} 2>/dev/null | python scripts/spec/regression.py $f && ((c++)) - ((n++)) -done -debug=$debug_tmp -msg="passed $c/$n tests" -echo $msg -if [ $c -eq $n ]; then - exit 0 -else - notify-send -i stop "$msg" - exit 1 -fi diff --git a/scripts/spec/spec.sh b/scripts/spec/spec.sh deleted file mode 100755 index 03abe9a0c7..0000000000 --- a/scripts/spec/spec.sh +++ /dev/null @@ -1,10 +0,0 @@ -# print all states the parser goes through -#export OCAMLRUNPARAM='p' -bin=src/mainspec.native -spec=${1-"tests/regression/18-file/file.spec"} -ocamlbuild -yaccflag -v -X webapp -no-links -use-ocamlfind $bin \ - && (./_build/$bin $spec \ - || (echo "$spec failed, running interactive now..."; - rlwrap ./_build/$bin - ) - ) diff --git a/src/analyses/fileUse.ml b/src/analyses/fileUse.ml deleted file mode 100644 index 58257b7843..0000000000 --- a/src/analyses/fileUse.ml +++ /dev/null @@ -1,296 +0,0 @@ -(** Analysis of correct file handle usage ([file]). - - @see Vogler, R. Verifying Regular Safety Properties of C Programs Using the Static Analyzer Goblint. Section 3.*) - -open Batteries -open GoblintCil -open Analyses - -module Spec = -struct - include Analyses.DefaultSpec - - let name () = "file" - module D = FileDomain.Dom - module C = FileDomain.Dom - - (* special variables *) - let return_var = Cilfacade.create_var @@ Cil.makeVarinfo false "@return" Cil.voidType, `NoOffset - let unclosed_var = Cilfacade.create_var @@ Cil.makeVarinfo false "@unclosed" Cil.voidType, `NoOffset - - (* keys that were already warned about; needed for multiple returns (i.e. can't be kept in D) *) - let warned_unclosed = ref Set.empty - - (* queries *) - let query ctx (type a) (q: a Queries.t) = - match q with - | Queries.MayPointTo exp -> if M.tracing then M.tracel "file" "query MayPointTo: %a" d_plainexp exp; Queries.Result.top q - | _ -> Queries.Result.top q - - let query_ad (ask: Queries.ask) exp = - match ask.f (Queries.MayPointTo exp) with - | ad when not (Queries.AD.is_top ad) -> Queries.AD.elements ad - | _ -> [] - let print_query_lv ?msg:(msg="") ask exp = - let addrs = query_ad ask exp in (* MayPointTo -> LValSet *) - let pretty_key = function - | Queries.AD.Addr.Addr (v,o) -> Pretty.text (D.string_of_key (v, ValueDomain.Addr.Offs.to_exp o)) - | _ -> Pretty.text "" in - if M.tracing then M.tracel "file" "%s MayPointTo %a = [%a]" msg d_exp exp (Pretty.docList ~sep:(Pretty.text ", ") pretty_key) addrs - - let eval_fv ask exp: varinfo option = - match query_ad ask exp with - | [addr] -> Queries.AD.Addr.to_var_may addr - | _ -> None - - - (* transfer functions *) - let assign ctx (lval:lval) (rval:exp) : D.t = - let m = ctx.local in - (* ignore(printf "%a = %a\n" d_plainlval lval d_plainexp rval); *) - let saveOpened ?unknown:(unknown=false) k m = (* save maybe opened files in the domain to warn about maybe unclosed files at the end *) - if D.may k D.opened m && not (D.is_unknown k m) then (* if unknown we don't have any location for the warning and have handled it already anyway *) - let mustOpen, mayOpen = D.filter_records k D.opened m in - let mustOpen, mayOpen = if unknown then Set.empty, mayOpen else mustOpen, Set.diff mayOpen mustOpen in - D.extend_value unclosed_var (mustOpen, mayOpen) m - else m - in - let key_from_exp = function - | Lval x -> Some (D.key_from_lval x) - | _ -> None - in - match key_from_exp (Lval lval), key_from_exp (stripCasts rval) with (* we just care about Lval assignments *) - | Some k1, Some k2 when k1=k2 -> m (* do nothing on self-assignment *) - | Some k1, Some k2 when D.mem k1 m && D.mem k2 m -> (* both in D *) - if M.tracing then M.tracel "file" "assign (both in D): %s = %s" (D.string_of_key k1) (D.string_of_key k2); - saveOpened k1 m |> D.remove' k1 |> D.alias k1 k2 - | Some k1, Some k2 when D.mem k1 m -> (* only k1 in D *) - if M.tracing then M.tracel "file" "assign (only k1 in D): %s = %s" (D.string_of_key k1) (D.string_of_key k2); - saveOpened k1 m |> D.remove' k1 - | Some k1, Some k2 when D.mem k2 m -> (* only k2 in D *) - if M.tracing then M.tracel "file" "assign (only k2 in D): %s = %s" (D.string_of_key k1) (D.string_of_key k2); - D.alias k1 k2 m - | Some k1, _ when D.mem k1 m -> (* k1 in D and assign something unknown *) - if M.tracing then M.tracel "file" "assign (only k1 in D): %s = %a" (D.string_of_key k1) d_exp rval; - D.warn @@ "[Unsound]changed pointer "^D.string_of_key k1^" (no longer safe)"; - saveOpened ~unknown:true k1 m |> D.unknown k1 - | _ -> (* no change in D for other things *) - if M.tracing then M.tracel "file" "assign (none in D): %a = %a [%a]" d_lval lval d_exp rval d_plainexp rval; - m - - let branch ctx (exp:exp) (tv:bool) : D.t = - let m = ctx.local in - (* ignore(printf "if %a = %B (line %i)\n" d_plainexp exp tv (!Tracing.current_loc).line); *) - let check a b tv = - (* ignore(printf "check: %a = %a, %B\n" d_plainexp a d_plainexp b tv); *) - match a, b with - | Const (CInt(i, kind, str)), Lval lval - | Lval lval, Const (CInt(i, kind, str)) -> - (* ignore(printf "branch(%s==%i, %B)\n" v.vname (Int64.to_int i) tv); *) - let k = D.key_from_lval lval in - if Z.compare i Z.zero = 0 && tv then ( - (* ignore(printf "error-branch\n"); *) - D.error k m - )else - D.success k m - | _ -> M.debug ~category:Analyzer "nothing matched the given BinOp: %a = %a" d_plainexp a d_plainexp b; m - in - match stripCasts (constFold true exp) with - (* somehow there are a lot of casts inside the BinOp which stripCasts only removes when called on the subparts - -> matching as in flagMode didn't work *) - (* | BinOp (Eq, Const (CInt64(i, kind, str)), Lval (Var v, NoOffset), _) - | BinOp (Eq, Lval (Var v, NoOffset), Const (CInt64(i, kind, str)), _) -> - ignore(printf "%s %i\n" v.vname (Int64.to_int i)); m *) - | BinOp (Eq, a, b, _) -> check (stripCasts a) (stripCasts b) tv - | BinOp (Ne, a, b, _) -> check (stripCasts a) (stripCasts b) (not tv) - | e -> M.debug ~category:Analyzer "branch: nothing matched the given exp: %a" d_plainexp e; m - - let body ctx (f:fundec) : D.t = - ctx.local - - let return ctx (exp:exp option) (f:fundec) : D.t = - (* TODO check One Return transformation: oneret.ml *) - let m = ctx.local in - (* if f.svar.vname <> "main" && BatList.is_empty (callstack m) then M.write ("\n\t!!! call stack is empty for function "^f.svar.vname^" !!!"); *) - if f.svar.vname = "main" then ( - let mustOpen, mayOpen = D.union (D.filter_values D.opened m) (D.get_value unclosed_var m) in - if Set.cardinal mustOpen > 0 then ( - D.warn @@ "unclosed files: "^D.string_of_keys mustOpen; - Set.iter (fun v -> D.warn ~loc:(D.V.loc v) "file is never closed") mustOpen; - (* add warnings about currently open files (don't include overwritten or changed file handles!) *) - warned_unclosed := Set.union !warned_unclosed (fst (D.filter_values D.opened m)) (* can't save in domain b/c it wouldn't reach the other return *) - ); - (* go through files "never closed" and recheck for current return *) - Set.iter (fun v -> if D.must (D.V.key v) D.closed m then D.warn ~may:true ~loc:(D.V.loc v) "file is never closed") !warned_unclosed; - (* let mustOpenVars = List.map (fun x -> x.key) mustOpen in *) - (* let mayOpen = List.filter (fun x -> not (List.mem x.key mustOpenVars)) mayOpen in (* ignore values that are already in mustOpen *) *) - let mayOpen = Set.diff mayOpen mustOpen in - if Set.cardinal mayOpen > 0 then - D.warn ~may:true @@ "unclosed files: "^D.string_of_keys mayOpen; - Set.iter (fun v -> D.warn ~may:true ~loc:(D.V.loc v) "file is never closed") mayOpen - ); - (* take care of return value *) - let au = match exp with - | Some(Lval lval) when D.mem (D.key_from_lval lval) m -> (* we return a var in D *) - let k = D.key_from_lval lval in - let varinfo,offset = k in - if varinfo.vglob then - D.alias return_var k m (* if var is global, we alias it *) - else - D.add return_var (D.find' k m) m (* if var is local, we make a copy *) - | _ -> m - in - (* remove formals and locals *) - (* this is not a good approach, what if we added a key foo.fp? -> just keep the globals *) - List.fold_left (fun m var -> D.remove' (var, `NoOffset) m) au (f.sformals @ f.slocals) - (* D.only_globals au *) - - let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = - let m = if f.svar.vname <> "main" then - (* push current location onto stack *) - D.edit_callstack (BatList.cons (Option.get !Node.current_node)) ctx.local - else ctx.local in - (* we need to remove all variables that are neither globals nor special variables from the domain for f *) - (* problem: we need to be able to check aliases of globals in check_overwrite_open -> keep those in too :/ *) - (* TODO see Base.make_entry, reachable vars > globals? *) - (* [m, D.only_globals m] *) - [m, m] (* this is [caller, callee] *) - - let check_overwrite_open k m = (* used in combine and special *) - if List.is_empty (D.get_aliases k m) then ( - (* there are no other variables pointing to the file handle - and it is opened again without being closed before *) - D.report k D.opened ("overwriting still opened file handle "^D.string_of_key k) m; - let mustOpen, mayOpen = D.filter_records k D.opened m in - let mayOpen = Set.diff mayOpen mustOpen in - (* save opened files in the domain to warn about unclosed files at the end *) - D.extend_value unclosed_var (mustOpen, mayOpen) m - ) else m - - let combine_env ctx lval fexp f args fc au f_ask = - let m = ctx.local in - (* pop the last location off the stack *) - let m = D.edit_callstack List.tl m in (* TODO could it be problematic to keep this in the caller instead of callee domain? if we only add the stack for the callee in enter, then there would be no need to pop a location anymore... *) - (* TODO add all globals from au to m (since we remove formals and locals on return, we can just add everything except special vars?) *) - D.without_special_vars au |> D.add_all m - - let combine_assign ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) (f_ask: Queries.ask) : D.t = - let m = ctx.local in - let return_val = D.find_option return_var au in - match lval, return_val with - | Some lval, Some v -> - let k = D.key_from_lval lval in - (* handle potential overwrites *) - let m = check_overwrite_open k m in - (* if v.key is still in D, then it must be a global and we need to alias instead of rebind *) - (* TODO what if there is a local with the same name as the global? *) - if D.V.is_top v then (* returned a local that was top -> just add k as top *) - D.add' k v m - else (* v is now a local which is not top or a global which is aliased *) - let vvar = D.V.get_alias v in (* this is also ok if v is not an alias since it chooses an element from the May-Set which is never empty (global top gets aliased) *) - if D.mem vvar au then (* returned variable was a global TODO what if local had the same name? -> seems to work *) - D.alias k vvar m - else (* returned variable was a local *) - let v = D.V.set_key k v in (* adjust var-field to lval *) - D.add' k v m - | _ -> m - - let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = - (* is f a pointer to a function we look out for? *) - let f = eval_fv (Analyses.ask_of_ctx ctx) (Lval (Var f, NoOffset)) |? f in - let m = ctx.local in - let loc = (Option.get !Node.current_node)::(D.callstack m) in - let arglist = List.map (Cil.stripCasts) arglist in (* remove casts, TODO safe? *) - let split_err_branch lval dom = - (* type? NULL = 0 = 0-ptr? Cil.intType, Cil.intPtrType, Cil.voidPtrType -> no difference *) - if not (GobConfig.get_bool "ana.file.optimistic") then - ctx.split dom [Events.SplitBranch ((Cil.BinOp (Cil.Eq, Cil.Lval lval, Cil.integer 0, Cil.intType)), true)]; - dom - in - (* fold possible keys on domain *) - let ret_all f lval = - let xs = D.keys_from_lval lval (Analyses.ask_of_ctx ctx) in (* get all possible keys for a given lval *) - if xs = [] then (D.warn @@ GobPretty.sprintf "could not resolve %a" CilType.Lval.pretty lval; m) - else if List.compare_length_with xs 1 = 0 then f (List.hd xs) m true - (* else List.fold_left (fun m k -> D.join m (f k m)) m xs *) - else - (* if there is more than one key, join all values and do warnings on the result *) - let v = List.fold_left (fun v k -> match v, D.find_option k m with - | None, None -> None - | Some a, None - | None, Some a -> Some a - | Some a, Some b -> Some (D.V.join a b)) None xs in - (* set all of the keys to the computed joined value *) - (* let m' = Option.map_default (fun v -> List.fold_left (fun m k -> D.add' k v m) m xs) m v in *) - (* then check each key *) - (* List.iter (fun k -> ignore(f k m')) xs; *) - (* get Mval.Exp from lval *) - let k' = D.key_from_lval lval in - (* add joined value for that key *) - let m' = Option.map_default (fun v -> D.add' k' v m) m v in - (* check for warnings *) - ignore(f k' m' true); - (* and join the old domain without issuing warnings *) - List.fold_left (fun m k -> D.join m (f k m false)) m xs - in - match lval, f.vname, arglist with - | None, "fopen", _ -> - D.warn "file handle is not saved!"; m - | Some lval, "fopen", _ -> - let f k m w = - let m = check_overwrite_open k m in - (match arglist with - | Const(CStr(filename,_))::Const(CStr(mode,_))::[] -> - (* M.debug ~category:Analyzer @@ "fopen(\""^filename^"\", \""^mode^"\")"; *) - D.fopen k loc filename mode m |> split_err_branch lval (* TODO k instead of lval? *) - | e::Const(CStr(mode,_))::[] -> - (* ignore(printf "CIL: %a\n" d_plainexp e); *) - (match ctx.ask (Queries.EvalStr e) with - | `Lifted filename -> D.fopen k loc filename mode m - | _ -> D.warn "[Unsound]unknown filename"; D.fopen k loc "???" mode m - ) - | xs -> - let args = (String.concat ", " (List.map CilType.Exp.show xs)) in - M.debug ~category:Analyzer "fopen args: %s" args; - (* List.iter (fun exp -> ignore(printf "%a\n" d_plainexp exp)) xs; *) - D.warn @@ "[Program]fopen needs two strings as arguments, given: "^args; m - ) - in ret_all f lval - - | _, "fclose", [Lval fp] -> - let f k m w = - if w then D.reports k [ - false, D.closed, "closeing already closed file handle "^D.string_of_key k; - true, D.opened, "closeing unopened file handle "^D.string_of_key k - ] m; - D.fclose k loc m - in ret_all f fp - | _, "fclose", _ -> - D.warn "fclose needs exactly one argument"; m - - | _, "fprintf", (Lval fp)::_::_ -> - let f k m w = - if w then D.reports k [ - false, D.closed, "writing to closed file handle "^D.string_of_key k; - true, D.opened, "writing to unopened file handle "^D.string_of_key k; - true, D.writable, "writing to read-only file handle "^D.string_of_key k; - ] m; - m - in ret_all f fp - | _, "fprintf", fp::_::_ -> - (* List.iter (fun exp -> ignore(printf "%a\n" d_plainexp exp)) arglist; *) - print_query_lv ~msg:"fprintf(?, ...): " (Analyses.ask_of_ctx ctx) fp; - D.warn "[Program]first argument to printf must be a Lval"; m - | _, "fprintf", _ -> - D.warn "[Program]fprintf needs at least two arguments"; m - - | _ -> m - - let startstate v = D.bot () - let threadenter ctx ~multiple lval f args = [D.bot ()] - let threadspawn ctx ~multiple lval f args fctx = ctx.local - let exitstate v = D.bot () -end - -let _ = - MCP.register_analysis (module Spec : MCPSpec) diff --git a/src/analyses/spec.ml b/src/analyses/spec.ml deleted file mode 100644 index 2f754f6160..0000000000 --- a/src/analyses/spec.ml +++ /dev/null @@ -1,496 +0,0 @@ -(** Analysis using finite automaton specification file ([spec]). - - @author Ralf Vogler - - @see Vogler, R. Verifying Regular Safety Properties of C Programs Using the Static Analyzer Goblint. Section 4. *) - -open Batteries -open GoblintCil -open Analyses - -module SC = SpecCore - -module Spec = -struct - include Analyses.DefaultSpec - - let name() = "spec" - module D = SpecDomain.Dom - module C = SpecDomain.Dom - - (* special variables *) - let return_var = Cilfacade.create_var @@ Cil.makeVarinfo false "@return" Cil.voidType, `NoOffset - let global_var = Cilfacade.create_var @@ Cil.makeVarinfo false "@global" Cil.voidType, `NoOffset - - (* spec data *) - let nodes = ref [] - let edges = ref [] - - let load_specfile () = - let specfile = GobConfig.get_string "ana.spec.file" in - if String.length specfile < 1 then failwith "You need to specify a specification file using --set ana.spec.file path/to/file.spec when using the spec analysis!"; - if not (Sys.file_exists specfile) then failwith @@ "The given spec-file ("^specfile^") doesn't exist (CWD is "^Sys.getcwd ()^")."; - let _nodes, _edges = SpecUtil.parseFile specfile in - nodes := _nodes; edges := _edges (* don't change -> no need to save them in domain *) - - (* module for encapsulating general spec checking functions used in multiple transfer functions (assign, special) *) - (* - .spec-format: - - The file contains two types of definitions: nodes and edges. The labels of nodes are output. The labels of edges are the constraints. - - The given nodes are warnings, which have an implicit back edge to the previous node if used as a target. - - Alternatively warnings can be specified like this: "node1 -w1,w2,w3> node2 ...1" (w1, w2 and w3 will be output when the transition is taken). - - The start node of the first transition is the start node of the automaton. - - End nodes are specified by "node -> end _". - - "_end" is the local warning for nodes that are not in an end state, _END is the warning at return ($ is the list of keys). - - An edge with '_' matches everything. - - Edges with "->>" (or "-w1,w2>>" etc.) are forwarding edges, which will continue matching the same statement for the target node. - *) - module SpecCheck = - struct - (* custom goto (D.goto is just for modifying) that checks if the target state is a warning and acts accordingly *) - let goto ?may:(may=false) ?change_state:(change_state=true) key state m ws = - let loc = (Option.get !Node.current_node)::(D.callstack m) in - let warn key m msg = - Str.global_replace (Str.regexp_string "$") (D.string_of_key key) msg - |> D.warn ~may:(D.is_may key m || D.is_unknown key m) - in - (* do transition warnings *) - List.iter (fun state -> match SC.warning state !nodes with Some msg -> warn key m msg | _ -> ()) ws; - match SC.warning state !nodes with - | Some msg -> - warn key m msg; - m (* no goto == implicit back edge *) - | None -> - M.debug ~category:Analyzer "GOTO %s: %s -> %s" (D.string_of_key key) (D.string_of_state key m) state; - if not change_state then m - else if may then D.may_goto key loc state m else D.goto key loc state m - - let equal_exp ctx spec_exp cil_exp = match spec_exp, cil_exp with - (* TODO match constants right away to avoid queries? *) - | `String a, Const(CStr (b,_)) -> a=b - (* | `String a, Const(CWStr xs as c) -> failwith "not implemented" *) - (* CWStr is done in base.ml, query only returns `Str if it's safe *) - | `String a, e -> (match ctx.ask (Queries.EvalStr e) with - | `Lifted b -> a = b - | _ -> M.debug ~category:Analyzer "EQUAL String Query: no result!"; false - ) - | `Regex a, e -> (match ctx.ask (Queries.EvalStr e) with - | `Lifted b -> Str.string_match (Str.regexp a) b 0 - | _ -> M.debug ~category:Analyzer "EQUAL Regex String Query: no result!"; false - ) - | `Bool a, e -> (match ctx.ask (Queries.EvalInt e) with - | b -> (match Queries.ID.to_bool b with Some b -> a=b | None -> false) - ) - | `Int a, e -> (match ctx.ask (Queries.EvalInt e) with - | b -> (match Queries.ID.to_int b with Some b -> (Int64.of_int a)=(IntOps.BigIntOps.to_int64 b) | None -> false) - ) - | `Float a, Const(CReal (b, fkind, str_opt)) -> a=b - | `Float a, _ -> M.debug ~category:Analyzer "EQUAL Float: unsupported!"; false - (* arg is a key. currently there can only be one key per constraint, so we already used it for lookup. TODO multiple keys? *) - | `Var a, b -> true - (* arg is a identifier we use for matching constraints. TODO save in domain *) - | `Ident a, b -> true - | `Error s, b -> failwith @@ "Spec error: "^s - (* wildcard matches anything *) - | `Free, b -> true - | a,b -> M.info ~category:Unsound "EQUAL? Unmatched case - assume true..."; true - - let check_constraint ctx get_key matches m new_a old_key (a,ws,fwd,b,c as edge) = - (* If we have come to a wildcard, we match it instantly, but since there is no way of determining a key - this only makes sense if fwd is true (TODO wildcard for global. TODO use old_key). We pass a state replacement as 'new_a', - which will be applied in the following checks. - Multiple forwarding wildcards are not allowed, i.e. new_a must be None, otherwise we end up in a loop. *) - if SC.is_wildcard c && fwd && new_a=None then Some (m,fwd,Some (b,a),old_key) (* replace b with a in the following checks *) - else - (* save original start state of the constraint (needed to detect reflexive edges) *) - let old_a = a in - (* Assume new_a *) - let a = match new_a with - | Some (x,y) when a=x -> y - | _ -> a - in - (* if we forward, we have to replace the starting state for the following constraints *) - let new_a = if fwd then Some (b,a) else None in - (* TODO how to detect the key?? use "$foo" as key, "foo" as var in constraint and "_" for anything we're not interested in. - What to do for multiple keys (e.g. $foo, $bar)? -> Only allow one key & one map per spec-file (e.g. only $ as a key) or implement multiple maps? *) - (* look inside the constraint if there is a key and if yes, return what it corresponds to *) - (* if we can't find a matching key, we use the global key *) - let key = get_key c |? Cil.var (fst global_var) in - (* ignore(printf "KEY: %a\n" d_plainlval key); *) - (* get possible keys that &lval may point to *) - let keys = D.keys_from_lval key (Analyses.ask_of_ctx ctx) in (* does MayPointTo query *) - let check_key (m,n) var = - (* M.debug ~category:Analyzer @@ "check_key: "^f.vname^"(...): "^D.string_of_entry var m; *) - let wildcard = SC.is_wildcard c && fwd && b<>"end" in - (* skip transitions we can't take b/c we're not in the right state *) - (* i.e. if not in map, we must be at the start node or otherwise we must be in one of the possible saved states *) - if not (D.mem var m) && a<>SC.startnode !edges || D.mem var m && not (D.may_in_state var a m) then ( - (* ignore(printf "SKIP %s: state: %s, a: %s at %i\n" f.vname (D.string_of_state var m) a (!Tracing.current_loc.line)); *) - (m,n) (* not in map -> initial state. TODO save initial state? *) - ) - (* edge must match the current state or be a wildcard transition (except those for end) *) - else if not (matches edge) && not wildcard then (m,n) - (* everything matches the constraint -> go to new state and increase counter *) - else - (* TODO if #Queries.MayPointTo > 1: each result is May, but all combined are Must *) - let may = (List.compare_length_with keys 1 > 0) in - (* do not change state for reflexive edges where the key is not assigned to (e.g. *$p = _) *) - let change_state = not (old_a=b && SC.get_lval c <> Some `Var) in - M.debug ~category:Analyzer "GOTO ~may:%B ~change_state:%B. %s -> %s: %s" may change_state a b (SC.stmt_to_string c); - let new_m = goto ~may:may ~change_state:change_state var b m ws in - (new_m,n+1) - in - (* do check for each varinfo and return the resulting domain if there has been at least one matching constraint *) - let new_m,n = List.fold_left check_key (m,0) keys in (* start with original domain and #transitions=0 *) - if n==0 then None (* no constraint matched the current state *) - else Some (new_m,fwd,new_a,Some key) (* return new domain and forwarding info *) - - let check ctx get_key matches = - let m = ctx.local in - (* go through constraints and return resulting domain for the first match *) - (* if no constraint matches, the unchanged domain is returned *) - (* repeat for target node if it is a forwarding edge *) - (* TODO what should be done if multiple constraints would match? *) - (* TODO ^^ for May-Sets multiple constraints could match and should be taken! *) - try - let rec check_fwd_loop m new_a old_key = (* TODO cycle detection? *) - let new_m,fwd,new_a,key = List.find_map (check_constraint ctx get_key matches m new_a old_key) !edges in - (* List.iter (fun x -> M.debug ~category:Analyzer (x^"\n")) (D.string_of_map new_m); *) - if fwd then M.debug ~category:Analyzer "FWD: %B, new_a: %s, old_key: %s" fwd (dump new_a) (dump old_key); - if fwd then check_fwd_loop new_m new_a key else new_m,key - in - (* now we get the new domain and the latest key that was used *) - let new_m,key = check_fwd_loop m None None in - (* List.iter (fun x -> M.debug ~category:Analyzer (x^"\n")) (D.string_of_map new_m); *) - (* next we have to check if there is a branch() transition we could take *) - let branch_edges = List.filter (fun (a,ws,fwd,b,c) -> SC.is_branch c) !edges in - (* just for the compiler: key is initialized with None, but changes once some constaint matches. If none match, we wouldn't be here but at catch Not_found. *) - match key with - | Some key -> - (* we need to pass the key to the branch function. There is no scheme for getting the key from the constraint, but we should have been forwarded and can use the old key. *) - let check_branch branches var = - (* only keep those branch_edges for which our key might be in the right state *) - let branch_edges = List.filter (fun (a,ws,fwd,b,c) -> D.may_in_state var a new_m) branch_edges in - (* M.debug ~category:Analyzer @@ D.string_of_entry var new_m^" -> branch_edges: "^String.concat "\n " @@ List.map (fun x -> SC.def_to_string (SC.Edge x)) branch_edges; *) - (* count should be a multiple of 2 (true/false), otherwise the spec is malformed *) - if List.length branch_edges mod 2 <> 0 then failwith "Spec is malformed: branch-transitions always need a true and a false case!" else - (* if nothing matches, just return new_m without branching *) - (* if List.is_empty branch_edges then Set.of_list new_m else *) - if List.is_empty branch_edges then Set.of_list ([new_m, Cil.integer 1, true]) else (* XX *) - (* unique set of (dom,exp,tv) used in branch *) - let do_branch branches (a,ws,fwd,b,c) = - let c_str = match SC.branch_exp c with Some (exp,tv) -> SC.exp_to_string exp | _ -> "" in - let c_str = Str.global_replace (Str.regexp_string "$key") "%e:key" c_str in (* TODO what should be used to specify the key? *) - (* TODO this somehow also prints the expression!? why?? *) - let c_exp = Formatcil.cExp c_str [("key", Fe (D.K.to_cil_exp var))] in (* use Fl for Lval instead? *) - (* TODO encode key in exp somehow *) - (* ignore(printf "BRANCH %a\n" d_plainexp c_exp); *) - ctx.split new_m [Events.SplitBranch (c_exp, true)]; - Set.add (new_m,c_exp,true) (Set.add (new_m,c_exp,false) branches) - in - List.fold_left do_branch branches branch_edges - in - let keys = D.keys_from_lval key (Analyses.ask_of_ctx ctx) in - let new_set = List.fold_left check_branch Set.empty keys in ignore(new_set); (* TODO refactor *) - (* List.of_enum (Set.enum new_set) *) - new_m (* XX *) - | None -> new_m - with Not_found -> m (* nothing matched -> no change *) - end - - (* queries *) - let query ctx (type a) (q: a Queries.t) = - match q with - | _ -> Queries.Result.top q - - let query_addrs ask exp = - match ask (Queries.MayPointTo exp) with - | ad when not (Queries.AD.is_top ad) -> Queries.AD.elements ad - | _ -> [] - - let eval_fv ask exp: varinfo option = - match query_addrs ask exp with - | [addr] -> Queries.AD.Addr.to_var_may addr - | _ -> None - - - (* transfer functions *) - let assign ctx (lval:lval) (rval:exp) : D.t = - (* ignore(printf "%a = %a\n" d_plainlval lval d_plainexp rval); *) - let get_key c = match SC.get_key_variant c with - | `Lval s -> - M.debug ~category:Analyzer "Key variant assign `Lval %s; %s" s (SC.stmt_to_string c); - (match SC.get_lval c, lval with - | Some `Var, _ -> Some lval - | Some `Ptr, (Mem Lval x, o) -> Some x (* TODO offset? *) - | _ -> None) - | _ -> None - in - let matches (a,ws,fwd,b,c) = - SC.equal_form (Some lval) c && - (* check for constraints *p = _ where p is the key *) - match lval, SC.get_lval c with - | (Mem Lval x, o), Some `Ptr when SpecCheck.equal_exp ctx (SC.get_rval c) rval -> - let keys = D.keys_from_lval x (Analyses.ask_of_ctx ctx) in - if List.compare_length_with keys 1 <> 0 then failwith "not implemented" - else true - | _ -> false (* nothing to do *) - in - let m = SpecCheck.check ctx get_key matches in - let key_from_exp = function - | Lval (Var v,o) -> Some (v, Offset.Exp.of_cil o) - | _ -> None - in - match key_from_exp (Lval lval), key_from_exp (stripCasts rval) with (* TODO for now we just care about Lval assignments -> should use Queries.MayPointTo *) - | Some k1, Some k2 when k1=k2 -> m (* do nothing on self-assignment *) - | Some k1, Some k2 when D.mem k1 m && D.mem k2 m -> (* both in D *) - M.debug ~category:Analyzer "assign (both in D): %s = %s" (D.string_of_key k1) (D.string_of_key k2); - (* saveOpened k1 *) m |> D.remove' k1 |> D.alias k1 k2 - | Some k1, Some k2 when D.mem k1 m -> (* only k1 in D *) - M.debug ~category:Analyzer "assign (only k1 in D): %s = %s" (D.string_of_key k1) (D.string_of_key k2); - (* saveOpened k1 *) m |> D.remove' k1 - | Some k1, Some k2 when D.mem k2 m -> (* only k2 in D *) - M.debug ~category:Analyzer "assign (only k2 in D): %s = %s" (D.string_of_key k1) (D.string_of_key k2); - let m = D.alias k1 k2 m in (* point k1 to k2 *) - if Basetype.Variables.to_group (fst k2) = Temp (* check if k2 is a temporary Lval introduced by CIL *) - then D.remove' k2 m (* if yes we need to remove it from our map *) - else m (* otherwise no change *) - | Some k1, _ when D.mem k1 m -> (* k1 in D and assign something unknown *) - M.debug ~category:Analyzer "assign (only k1 in D): %s = %a" (D.string_of_key k1) d_exp rval; - D.warn @@ "changed pointer "^D.string_of_key k1^" (no longer safe)"; - (* saveOpened ~unknown:true k1 *) m |> D.unknown k1 - | _ -> (* no change in D for other things *) - M.debug ~category:Analyzer "assign (none in D): %a = %a [%a]" d_lval lval d_exp rval d_plainexp rval; - m - - (* - - branch-transitions in the spec-file come in pairs: e.g. true-branch goes to node a, false-branch to node b - - branch is called for both possibilities - - TODO query the exp and take/don't take the transition - - in case of `Top we take the transition - - both branches get joined after (e.g. for fopen: May [open; error]) - - if there is a branch in the code, branch is also called - -> get the key from exp and backtrack to the corresponding branch-transitions - -> reevaluate with current exp and meet domain with result - *) - (* - - get key from exp - - ask EvalInt - - if result is `Top and we are in a state that is the starting node of a branch edge, we have to: - - go to target node and modify the state in specDomain - - find out which value of key makes exp equal to tv - - save this value and answer queries for EvalInt with it - - if not, compare it with tv and take the corresponding branch - *) - let branch ctx (exp:exp) (tv:bool) : D.t = - let m = ctx.local in - (* ignore(printf "if %a = %B (line %i)\n" d_plainexp exp tv (!Tracing.current_loc).line); *) - let check a b tv = - (* ignore(printf "check: %a = %a\n" d_plainexp a d_plainexp b); *) - match a, b with - | Const (CInt(i, kind, str)), Lval lval - | Lval lval, Const (CInt(i, kind, str)) -> - (* let binop = BinOp (Eq, a, b, Cil.intType) in *) - (* standardize the format of the expression to 'lval==i'. -> spec needs to follow that format, the code is mapped to it. *) - let binop = BinOp (Eq, Lval lval, Const (CInt(i, kind, str)), Cil.intType) in - let key = D.key_from_lval lval in - let value = D.find key m in - if Z.equal i Z.zero && tv then ( - M.debug ~category:Analyzer "error-branch"; - (* D.remove key m *) - )else( - M.debug ~category:Analyzer "success-branch"; - (* m *) - ); - (* there should always be an entry in our domain for key *) - if not (D.mem key m) then m else - (* TODO for now we just assume that a Binop is used and Lval is the key *) - (* get the state(s) that key is/might be in *) - let states = D.get_states key m in - (* compare SC.exp with Cil.exp and tv *) - let branch_exp_eq c exp tv = - (* let c_str = match SC.branch_exp c with Some (exp,tv) -> SC.exp_to_string exp | _ -> "" in - let c_str = Str.global_replace (Str.regexp_string "$key") "%e:key" c_str in - let c_exp = Formatcil.cExp c_str [("key", Fe (D.K.to_exp key))] in *) - (* c_exp=exp *) (* leads to Out_of_memory *) - match SC.branch_exp c with - | Some (c_exp,c_tv) -> - (* let exp_str = CilType.Exp.show exp in *) (* contains too many casts, so that matching fails *) - let exp_str = CilType.Exp.show binop in - let c_str = SC.exp_to_string c_exp in - let c_str = Str.global_replace (Str.regexp_string "$key") (D.string_of_key key) c_str in - (* ignore(printf "branch_exp_eq: '%s' '%s' -> %B\n" c_str exp_str (c_str=exp_str)); *) - c_str=exp_str && c_tv=tv - | _ -> false - in - (* filter those edges that are branches, start with a state from states and have the same branch expression and the same tv *) - let branch_edges = List.filter (fun (a,ws,fwd,b,c) -> SC.is_branch c && List.mem a states && branch_exp_eq c exp tv) !edges in - (* there should be only one such edge or none *) - if List.compare_length_with branch_edges 1 <> 0 then ( (* call of branch for an actual branch *) - M.debug ~category:Analyzer "branch: branch_edges length is not 1! -> actual branch"; - M.debug ~category:Analyzer "%s -> branch_edges1: %a" (D.string_of_entry key m) (Pretty.d_list "\n " (fun () x -> Pretty.text (SC.def_to_string (SC.Edge x)))) branch_edges; - (* filter those edges that are branches, end with a state from states have the same branch expression and the same tv *) - (* TODO they should end with any predecessor of the current state, not only the direct predecessor *) - let branch_edges = List.filter (fun (a,ws,fwd,b,c) -> SC.is_branch c && List.mem b states && branch_exp_eq c exp tv) !edges in - M.debug ~category:Analyzer "%s -> branch_edges2: %a" (D.string_of_entry key m) (Pretty.d_list "\n " (fun () x -> Pretty.text (SC.def_to_string (SC.Edge x)))) branch_edges; - if List.compare_length_with branch_edges 1 <> 0 then m else - (* meet current value with the target state. this is tricky: we can not simply take the target state, since there might have been more than one element already before the branching. - -> find out what the alternative branch target was and remove it *) - let (a,ws,fwd,b,c) = List.hd branch_edges in - (* the alternative branch has the same start node, the same branch expression and the negated tv *) - let (a,ws,fwd,b,c) = List.find (fun (a2,ws,fwd,b,c) -> SC.is_branch c && a2=a && branch_exp_eq c exp (not tv)) !edges in - (* now b is the state the alternative branch goes to -> remove it *) - (* TODO may etc. *) - (* being explicit: check how many records there are. if the value is Must b, then we're sure that it is so and we don't remove anything. *) - if D.V.length value = (1,1) then m else (* XX *) - (* there are multiple possible states -> remove b *) - let v2 = D.V.remove_state b value in - (* M.debug ~category:Analyzer @@ "branch: changed state from "^D.V.string_of value^" to "^D.V.string_of v2; *) - D.add key v2 m - ) else (* call of branch directly after splitting *) - let (a,ws,fwd,b,c) = List.hd branch_edges in - (* TODO may etc. *) - let v2 = D.V.set_state b value in - (* M.debug ~category:Analyzer @@ "branch: changed state from "^D.V.string_of value^" to "^D.V.string_of v2; *) - D.add key v2 m - | _ -> M.debug ~category:Analyzer "nothing matched the given BinOp: %a = %a" d_plainexp a d_plainexp b; m - in - match stripCasts (constFold true exp) with - (* somehow there are a lot of casts inside the BinOp which stripCasts only removes when called on the subparts - -> matching as in flagMode didn't work *) - | BinOp (Eq, a, b, _) -> check (stripCasts a) (stripCasts b) tv - | BinOp (Ne, a, b, _) -> check (stripCasts a) (stripCasts b) (not tv) - | UnOp (LNot, a, _) -> check (stripCasts a) (integer 0) tv - (* TODO makes 2 tests fail. probably check changes something it shouldn't *) - (* | Lval _ as a -> check (stripCasts a) (integer 0) (not tv) *) - | e -> M.debug ~category:Analyzer "branch: nothing matched the given exp: %a" d_plainexp e; m - - let body ctx (f:fundec) : D.t = - ctx.local - - let return ctx (exp:exp option) (f:fundec) : D.t = - let m = ctx.local in - (* M.debug ~category:Analyzer @@ "return: ctx.local="^D.short 50 m^D.string_of_callstack m; *) - (* if f.svar.vname <> "main" && BatList.is_empty (D.callstack m) then M.debug ~category:Analyzer @@ "\n\t!!! call stack is empty for function "^f.svar.vname^" !!!"; *) - if f.svar.vname = "main" then ( - let warn_main msg_loc msg_end = (* there is an end warning for local, return or both *) - (* find edges that have 'end' as a target *) - (* we ignore the constraint, TODO maybe find a better syntax for declaring end states *) - let end_states = BatList.filter_map (fun (a,ws,fwd,b,c) -> if b="end" then Some a else None) !edges in - let must_not, may_not = D.filter_values (fun r -> not @@ List.exists (fun end_state -> D.V.in_state end_state r) end_states) m in - let may_not = Set.diff may_not must_not in - (match msg_loc with (* local warnings for entries that must/may not be in an end state *) - | Some msg -> - Set.iter (fun r -> D.warn ~loc:(D.V.loc r) msg) must_not; - Set.iter (fun r -> D.warn ~may:true ~loc:(D.V.loc r) msg) may_not - | None -> ()); - (match msg_end with - | Some msg -> (* warnings at return for entries that must/may not be in an end state *) - let f msg rs = Str.global_replace (Str.regexp_string "$") (D.string_of_keys rs) msg in - if Set.cardinal must_not > 0 then D.warn (f msg must_not); - if Set.cardinal may_not > 0 then D.warn ~may:true (f msg may_not) - | _ -> ()) - in - (* check if there is a warning for entries that are not in an end state *) - match SC.warning "_end" !nodes, SC.warning "_END" !nodes with - | None, None -> () (* nothing to do here *) - | msg_loc,msg_end -> warn_main msg_loc msg_end - ); - (* take care of return value *) - let au = match exp with - | Some(Lval lval) when D.mem (D.key_from_lval lval) m -> (* we return a var in D *) - let k = D.key_from_lval lval in - let varinfo,offset = k in - if varinfo.vglob then - D.alias return_var k m (* if var is global, we alias it *) - else - D.add return_var (D.find' k m) m (* if var is local, we make a copy *) - | _ -> m - in - (* remove formals and locals *) - (* TODO only keep globals like in fileUse *) - List.fold_left (fun m var -> D.remove' (var, `NoOffset) m) au (f.sformals @ f.slocals) - - let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = - (* M.debug ~category:Analyzer @@ "entering function "^f.vname^D.string_of_callstack ctx.local; *) - if f.svar.vname = "main" then load_specfile (); - let m = if f.svar.vname <> "main" then - D.edit_callstack (BatList.cons (Option.get !Node.current_node)) ctx.local - else ctx.local in [m, m] - - let combine_env ctx lval fexp f args fc au f_ask = - (* M.debug ~category:Analyzer @@ "leaving function "^f.vname^D.string_of_callstack au; *) - let au = D.edit_callstack List.tl au in - (* remove special return var *) - D.remove' return_var au - - let combine_assign ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) (f_ask: Queries.ask) : D.t = - let return_val = D.find_option return_var au in - match lval, return_val with - | Some lval, Some v -> - let k = D.key_from_lval lval in - (* handle potential overwrites *) - (* |> check_overwrite_open k *) - (* if v.key is still in D, then it must be a global and we need to alias instead of rebind *) - (* TODO what if there is a local with the same name as the global? *) - if D.V.is_top v then (* returned a local that was top -> just add k as top *) - D.add' k v ctx.local - else (* v is now a local which is not top or a global which is aliased *) - let vvar = D.V.get_alias v in (* this is also ok if v is not an alias since it chooses an element from the May-Set which is never empty (global top gets aliased) *) - if D.mem vvar au then (* returned variable was a global TODO what if local had the same name? -> seems to work *) - (* let _ = M.debug ~category:Analyzer @@ vvar.vname^" was a global -> alias" in *) - D.alias k vvar ctx.local - else (* returned variable was a local *) - let v = D.V.set_key k v in (* adjust var-field to lval *) - (* M.debug ~category:Analyzer @@ vvar.vname^" was a local -> rebind"; *) - D.add' k v ctx.local - | _ -> ctx.local - - let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = - let arglist = List.map (Cil.stripCasts) arglist in (* remove casts, TODO safe? *) - let get_key c = match SC.get_key_variant c with - | `Lval s -> - M.debug ~category:Analyzer "Key variant special `Lval %s; %s" s (SC.stmt_to_string c); - lval - | `Arg(s, i) -> - M.debug ~category:Analyzer "Key variant special `Arg(%s, %d). %s" s i (SC.stmt_to_string c); - (try - let arg = List.at arglist i in - match arg with - | Lval x -> Some x (* TODO enough to just assume the arg is already there as a Lval? *) - | AddrOf x -> Some x - | _ -> None - with Invalid_argument s -> - M.debug ~category:Analyzer "Key out of bounds! Msg: %s" s; (* TODO what to do if spec says that there should be more args... *) - None - ) - | _ -> None (* `Rval or `None *) - in - let matches (a,ws,fwd,b,c) = - let equal_args spec_args cil_args = - if List.compare_length_with spec_args 1 = 0 && List.hd spec_args = `Free then - true (* wildcard as an argument matches everything *) - else if List.compare_lengths arglist spec_args <> 0 then ( - M.debug ~category:Analyzer "SKIP the number of arguments doesn't match the specification!"; - false - )else - List.for_all2 (SpecCheck.equal_exp ctx) spec_args cil_args (* TODO Cil.constFold true arg. Test: Spec and c-file: 1+1 *) - in - (* function name must fit the constraint *) - SC.fname_is f.vname c && - (* right form (assignment or not) *) - SC.equal_form lval c && - (* function arguments match those of the constraint *) - equal_args (SC.get_fun_args c) arglist - in - SpecCheck.check ctx get_key matches - - - let startstate v = D.bot () - let threadenter ctx ~multiple lval f args = [D.bot ()] - let threadspawn ctx ~multiple lval f args fctx = ctx.local - let exitstate v = D.bot () -end - -let _ = - MCP.register_analysis (module Spec : MCPSpec) diff --git a/src/common/util/options.schema.json b/src/common/util/options.schema.json index 7c921c4f53..4d9546a9ca 100644 --- a/src/common/util/options.schema.json +++ b/src/common/util/options.schema.json @@ -467,32 +467,6 @@ }, "additionalProperties": false }, - "file": { - "title": "ana.file", - "type": "object", - "properties": { - "optimistic": { - "title": "ana.file.optimistic", - "description": "Assume fopen never fails.", - "type": "boolean", - "default": false - } - }, - "additionalProperties": false - }, - "spec": { - "title": "ana.spec", - "type": "object", - "properties": { - "file": { - "title": "ana.spec.file", - "description": "Path to the specification file.", - "type": "string", - "default": "" - } - }, - "additionalProperties": false - }, "pml": { "title": "ana.pml", "type": "object", diff --git a/src/goblint_lib.ml b/src/goblint_lib.ml index 70f331b5ac..d4f2982902 100644 --- a/src/goblint_lib.ml +++ b/src/goblint_lib.ml @@ -147,12 +147,10 @@ module UnitAnalysis = UnitAnalysis (** {2 Other} *) module Assert = Assert -module FileUse = FileUse module LoopTermination = LoopTermination module Uninit = Uninit module Expsplit = Expsplit module StackTrace = StackTrace -module Spec = Spec (** {2 Helper} diff --git a/src/main.camldoc b/src/main.camldoc index ec08a14a7b..0a0e52035f 100644 --- a/src/main.camldoc +++ b/src/main.camldoc @@ -85,7 +85,6 @@ FlagModeDomain LockDomain StackDomain FileDomain -SpecDomain LvalMapDomain } @@ -106,7 +105,6 @@ Glob {!modules: MCP Base -Spec CondVars Contain diff --git a/src/mainspec.ml b/src/mainspec.ml deleted file mode 100644 index 4509645f98..0000000000 --- a/src/mainspec.ml +++ /dev/null @@ -1,13 +0,0 @@ -open Goblint_lib -open Batteries (* otherwise open_in would return wrong type for SpecUtil *) -open SpecUtil - -let _ = - (* no arguments -> run interactively (= reading from stdin) *) - let args = Array.length Sys.argv > 1 in - if args && Sys.argv.(1) = "-" then - ignore(parse ~dot:true stdin) - else - let cin = if args then open_in Sys.argv.(1) else stdin in - ignore(parse ~repl:(not args) ~print:true cin) -(* exit 0 *) diff --git a/src/spec/dune b/src/spec/dune deleted file mode 100644 index 47c22a0d46..0000000000 --- a/src/spec/dune +++ /dev/null @@ -1,2 +0,0 @@ -(ocamllex specLexer) -(ocamlyacc specParser) diff --git a/src/spec/file.dot b/src/spec/file.dot deleted file mode 100644 index a78c64d3fc..0000000000 --- a/src/spec/file.dot +++ /dev/null @@ -1,37 +0,0 @@ -digraph file { - // changed file pointer {fp} (no longer safe) - - // file handle is not saved! - // overwriting still opened file handle - // file is never closed - // file may be never closed - // closeing unopened file handle - // closeing already closed file handle - // writing to closed file handle - // writing to unopened file handle - // writing to read-only file handle - - // unclosed files: ... - // maybe unclosed files: ... - - w1 [label="file handle is not saved!"]; - w2 [label="closeing unopened file handle"]; - w3 [label="writing to unopened file handle"]; - w4 [label="writing to read-only file handle"]; - w5 [label="closeing already closed file handle"]; - w6 [label="writing to closed file handle"]; - - 1 -> w1 [label="fopen(_)"]; - 1 -> w2 [label="fclose($fp)"]; - 1 -> w3 [label="fprintf($fp, _)"]; - 1 -> open_read [label="$fp = fopen($path, \"r\")"]; - 1 -> open_write [label="$fp = fopen($path, \"w\")"]; - 1 -> open_write [label="$fp = fopen($path, \"a\")"]; - open_read -> w4 [label="fprintf($fp, _)"]; - open_write -> open_write [label="fprintf($fp, _)"]; - open_read -> closed [label="fclose($fp)"]; - open_write -> closed [label="fclose($fp)"]; - closed -> w5 [label="fclose($fp)"]; - closed -> w6 [label="fprintf($fp, _)"]; - closed -> 1 [label="->"]; -} \ No newline at end of file diff --git a/src/spec/render.sh b/src/spec/render.sh deleted file mode 100755 index 91e486c247..0000000000 --- a/src/spec/render.sh +++ /dev/null @@ -1,31 +0,0 @@ -# command -v ls >&- || {echo >&2 bla; exit 1;} -function check(){ - set -e # needed to exit script from function - hash $1 2>&- || (echo >&2 "$1 is needed but not installed! $2"; exit 1;) - set +e # do not exit shell if some command fails (default) -} -check dot -mode=${1-"png"} -file=${2-"file"} -dst=graph -viewcmd=gpicview - -mkdir -p ${dst} -cp ${file}.dot ${dst} -file=${file##*/} # use basename in case the file was somewhere else -cd ${dst} -trap 'cd ..' EXIT # leave dst again on exit -case "$mode" in - png) dot -Tpng -o${file}.png ${file}.dot; - check ${viewcmd} "Please edit viewcmd accordingly." - pkill ${viewcmd}; - ${viewcmd} ${file}.png & - ;; - pdf) rm -f ${file}.tex; - check dot2tex - dot -Txdot ${file}.dot | dot2tex > ${file}.tex; - check pdflatex - pdflatex ${file}.tex - echo "generated $dst/$file.pdf" - ;; -esac diff --git a/src/spec/specCore.ml b/src/spec/specCore.ml deleted file mode 100644 index 9d0ce35624..0000000000 --- a/src/spec/specCore.ml +++ /dev/null @@ -1,152 +0,0 @@ -(* types used by specParser and functions for handling the constructed types *) - -open Batteries - -exception Endl -exception Eof - -(* type value = String of string | Bool of bool | Int of int | Float of float *) -type lval = Ptr of lval | Var of string | Ident of string -type fcall = {fname: string; args: exp list} -and exp = - Fun of fcall | - Exp_ | - Lval of lval | - Regex of string | - String of string | Bool of bool | Int of int | Float of float | - Binop of string * exp * exp | - Unop of string * exp -type stmt = {lval: lval option; exp: exp} -type def = Node of (string * string) (* node warning *) - | Edge of (string * string list * bool * string * stmt) (* start-node, warning-nodes, forwarding, target-node, constraint *) - -(* let stmts edges = List.map (fun (a,b,c) -> c) edges - let get_fun stmt = match stmt.exp with Fun x -> Some x | _ -> None - let fun_records edges = List.filter_map get_fun (stmts edges) - let fun_names edges = fun_records edges |> List.map (fun x -> x.fname) - let fun_by_fname fname edges = List.filter (fun x -> x.fname=fname) (fun_records edges) *) -let fname_is fname stmt = - match stmt.exp with - | Fun x -> x.fname=fname - | _ -> false - -let is_wildcard stmt = stmt.exp = Exp_ - -let branch_exp stmt = - match stmt.exp with - | Fun { fname="branch"; args=[exp; Bool tv] } -> Some (exp,tv) - | _ -> None - -let is_branch stmt = branch_exp stmt <> None - -let startnode edges = - (* The start node of the first transition is the start node of the automaton. *) - let a,ws,fwd,b,c = List.hd edges in a - -let warning state nodes = - try - Some (snd (List.find (fun x -> fst x = state) nodes)) (* find node for state and return its warning *) - with - | Not_found -> None (* no node for state *) - -let get_lval stmt = - let f = function - | Ptr x -> `Ptr (* TODO recursive *) - | Var s -> `Var - | Ident s -> `Ident - in - Option.map f stmt.lval - -let get_exp = function - | Regex x -> `Regex x - | String x -> `String x - | Bool x -> `Bool x - | Int x -> `Int x - | Float x -> `Float x - | Lval (Var x) -> `Var x - | Lval (Ident x) -> `Ident x - | Fun x -> `Error "Functions aren't allowed to have functions as an argument (put the function as a previous state instead)" - | Exp_ -> `Free - | Unop ("!", Bool x) -> `Bool (not x) - | _ -> `Error "Unsupported operation inside function argument, use a simpler expression instead." - -let get_rval stmt = get_exp stmt.exp - -let get_key_variant stmt = - let rec get_from_exp = function - | Fun f -> get_from_args f.args (* TODO for special we only consider constraints where the root of the exp is Fun (see fname_is) *) - | Lval (Var s) -> `Rval s - | _ -> `None - (* walks over arguments until it finds something or returns `None *) - and get_from_argsi i = function - | [] -> `None - | x::xs -> - match get_from_exp x with - | `Rval s -> `Arg(s, i) - | _ -> get_from_argsi (i+1) xs (* matches `None and `Arg -> `Arg of `Arg not supported *) - and get_from_args args = get_from_argsi 0 args (* maybe better use List.findi *) - in - let rec get_from_lval = function - | Ptr x -> get_from_lval x - | Var s -> Some s - | Ident s -> None - in - match stmt.lval with - | Some lval when Option.is_some (get_from_lval lval) -> `Lval (Option.get (get_from_lval lval)) - | _ -> get_from_exp stmt.exp - -let equal_form lval stmt = - match lval, stmt.lval with - | Some _, Some _ - | None, None -> true - | _ -> false - -(* get function arguments with tags corresponding to the type -> should only be called for functions, returns [] for everything else *) -let get_fun_args stmt = match stmt.exp with - | Fun f -> List.map get_exp f.args - | _ -> [] - -(* functions for output *) -let rec lval_to_string = function - | Ptr x -> "*"^(lval_to_string x) - | Var x -> "$"^x - | Ident x -> x -let rec exp_to_string = function - | Fun x -> x.fname^"("^String.concat ", " (List.map exp_to_string x.args)^")" - | Exp_ -> "_" - | Lval x -> lval_to_string x - | Regex x -> "r\""^x^"\"" - | String x -> "\""^x^"\"" - | Bool x -> string_of_bool x - | Int x -> string_of_int x - | Float x -> string_of_float x - | Binop (op, a, b) -> exp_to_string a ^ " " ^ op ^ " " ^ exp_to_string b - | Unop (op, a) -> op ^ " " ^ exp_to_string a -let stmt_to_string stmt = match stmt.lval, stmt.exp with - | Some lval, exp -> lval_to_string lval^" = "^exp_to_string exp - | None, exp -> exp_to_string exp -let arrow_to_string ws fwd = (String.concat "," ws)^if fwd then ">" else "" -let def_to_string = function - | Node(n, m) -> n^"\t\""^m^"\"" - | Edge(a, ws, fwd, b, s) -> a^" -"^arrow_to_string ws fwd^"> "^b^"\t"^stmt_to_string s - -let to_dot_graph defs = - let no_warnings = true in - let def_to_string = function - | Node(n, m) -> - if no_warnings then "" - else n^"\t[style=filled, fillcolor=orange, label=\""^n^": "^m^"\"];" - | Edge(a, ws, fwd, b, s) -> - let style = if fwd then "style=dotted, " else "" in - let ws = if List.is_empty ws then "" else (String.concat "," ws)^" | " in - a^" -> "^b^"\t["^style^"label=\""^ws^String.escaped (stmt_to_string s)^"\"];" - in - let ends,defs = List.partition (function Edge (a,ws,fwd,b,s) -> b="end" && s.exp=Exp_ | _ -> false) defs in - let endstates = List.filter_map (function Edge (a,ws,fwd,b,s) -> Some a | _ -> None) ends in - (* set the default style for nodes *) - let defaultstyle = "node [shape=box, style=rounded];" in - (* style end nodes and then reset *) - let endstyle = if List.is_empty endstates then "" else "node [peripheries=2]; "^(String.concat " " endstates)^"; node [peripheries=1];" in - let lines = "digraph file {"::defaultstyle::endstyle::(List.map def_to_string defs |> List.filter (fun s -> s<>"")) in - (* List.iter print_endline lines *) - String.concat "\n " lines ^ "\n}" diff --git a/src/spec/specLexer.mll b/src/spec/specLexer.mll deleted file mode 100644 index 64ac69359e..0000000000 --- a/src/spec/specLexer.mll +++ /dev/null @@ -1,67 +0,0 @@ -{ - open SpecParser (* The type token is defined in specParser.mli *) - exception Token of string - let line = ref 1 -} - -let digit = ['0'-'9'] -let alpha = ['a'-'z' 'A'-'Z'] -let nl = '\r'?'\n' (* new line *) -let s = [' ' '\t'] (* whitespace *) -let w = '_' | alpha | digit (* word *) -let endlinecomment = "//" [^'\n']* -let multlinecomment = "/*"([^'*']|('*'+[^'*''/'])|nl)*'*'+'/' -let comments = endlinecomment | multlinecomment -let str = ('\"'(([^'\"']|"\\\"")* as s)'\"') | ('\''(([^'\'']|"\\'")* as s)'\'') - -rule token = parse - | s { token lexbuf } (* skip blanks *) - | comments { token lexbuf } (* skip comments *) - | nl { incr line; EOL } - - (* operators *) - | '(' { LPAREN } - | ')' { RPAREN } - | '[' { LBRACK } - | ']' { RBRACK } - | '{' { LCURL } - | '}' { RCURL } - (*| '.' { DOT } *) - (*| "->" { ARROW } *) - | '+' { PLUS } - | '-' { MINUS } - | '*' { MUL } - | '/' { DIV } - | '%' { MOD } - | '<' { LT } - | '>' { GT } - | "==" { EQEQ } - | "!=" { NE } - | "<=" { LE } - | ">=" { GE } - | "&&" { AND } - | "||" { OR } - | '!' { NOT } - | '=' { EQ } - | ',' { COMMA } - | ';' { SEMICOLON } - - (* literals, identifiers *) - | "true" { BOOL(true) } - | "false" { BOOL(false) } - | "null" { NULL } - | digit+ as x { INT(int_of_string x) } - | str { STRING(s) } - | '_' { UNDERS } (* used for spec, but has to be before Ident! *) - | ('_'|alpha) w* as x { IDENT(x) } - - (* spec *) - | ':' { COLON } - | "$"(w+ as x) { VAR(x) } - | "r" str { REGEX(s) } - | (w+ as n) s+ str - { NODE(n, s) } - | (w+ as a) s* "-" ((w+ ("," w+)*)? as ws) (">"? as fwd) ">" s* (w+ as b) s+ - { EDGE(a, BatString.split_on_string ~by:"," ws, fwd=">", b) } - | eof { EOF } - | _ as x { raise(Token (Char.escaped x^": unknown token in line "^string_of_int !line)) } diff --git a/src/spec/specParser.mly b/src/spec/specParser.mly deleted file mode 100644 index fe8fe90ec8..0000000000 --- a/src/spec/specParser.mly +++ /dev/null @@ -1,116 +0,0 @@ -%{ - (* necessary to open a different compilation unit - because exceptions directly defined here aren't visible outside - (e.g. SpecParser.Eof is raised, but Error: Unbound constructor - if used to catch in a different module) *) - open SpecCore -%} - -%token EOL EOF -/* operators */ -%token LPAREN RPAREN LCURL RCURL LBRACK RBRACK -%token PLUS MINUS MUL DIV MOD -%token LT GT EQEQ NE LE GE AND OR NOT -%token EQ COMMA SEMICOLON -/* literals, identifiers */ -%token BOOL -%token NULL -%token INT -%token STRING -%token IDENT -/* spec */ -%token UNDERS COLON -%token VAR -%token REGEX -%token NODE -%token EDGE - -/* precedence groups from low to high */ -%right EQ -%left OR -%left AND -%left EQEQ NE -%left LT GT LE GE -%left PLUS MINUS -%left MUL DIV MOD -%right NOT UPLUS UMINUS DEREF - -%start file -%type file - -%% - -file: - | def EOL { $1 } - | def EOF { $1 } /* no need for an empty line at the end */ - | EOL { raise Endl } /* empty line */ - | EOF { raise Eof } /* end of file */ -; - -def: - | NODE { Node($1) } - | EDGE stmt { let a, ws, fwd, b = $1 in Edge(a, ws, fwd, b, $2) } -; - -stmt: - | lval EQ expr { {lval = Some $1; exp = $3} } /* TODO expression would be better */ - | expr { {lval = None; exp = $1} } -; - -lval: - | MUL lval %prec DEREF { Ptr $2 } - | IDENT { Ident $1 } /* C identifier, e.g. foo, _foo, _1, but not 1b */ - | VAR { Var $1 } /* spec variable, e.g. $foo, $123, $__ */ -; - -expr: - | LPAREN expr RPAREN { $2 } - | REGEX { Regex $1 } - | STRING { String $1 } - | BOOL { Bool $1 } - | lval { Lval $1 } - | IDENT args { Fun {fname=$1; args=$2} } /* function */ - | UNDERS { Exp_ } - | nexpr { Int $1 } - /* | nexpr LT nexpr { Bool ($1<$3) } - | nexpr GT nexpr { Bool ($1>$3) } - | nexpr EQEQ nexpr { Bool ($1=$3) } - | nexpr NE nexpr { Bool ($1<>$3) } - | nexpr LE nexpr { Bool ($1<=$3) } - | nexpr GE nexpr { Bool ($1>=$3) } */ - | expr OR expr { Binop ("||", $1, $3) } - | expr AND expr { Binop ("&&", $1, $3) } - | expr EQEQ expr { Binop ("==", $1, $3) } - | expr NE expr { Binop ("!=", $1, $3) } - | expr LT expr { Binop ("<", $1, $3) } - | expr GT expr { Binop (">", $1, $3) } - | expr LE expr { Binop ("<=", $1, $3) } - | expr GE expr { Binop (">=", $1, $3) } - | expr PLUS expr { Binop ("+", $1, $3) } - | expr MINUS expr { Binop ("-", $1, $3) } - | expr MUL expr { Binop ("*", $1, $3) } - | expr DIV expr { Binop ("/", $1, $3) } - | expr MOD expr { Binop ("%", $1, $3) } - | NOT expr { Unop ("!", $2) } -; - -nexpr: - | INT { $1 } - | MINUS nexpr %prec UMINUS { - $2 } - | PLUS nexpr %prec UPLUS { $2 } - /* | LPAREN nexpr RPAREN { $2 } - | nexpr PLUS nexpr { $1 + $3 } - | nexpr MINUS nexpr { $1 - $3 } - | nexpr MUL nexpr { $1 * $3 } - | nexpr DIV nexpr { $1 / $3 } */ -; - -args: - | LPAREN RPAREN { [] } - | LPAREN expr_list RPAREN { $2 } -; - -expr_list: - | expr { [$1] } - | expr COMMA expr_list { $1 :: $3 } -; diff --git a/src/spec/specUtil.ml b/src/spec/specUtil.ml deleted file mode 100644 index 55e0b51135..0000000000 --- a/src/spec/specUtil.ml +++ /dev/null @@ -1,52 +0,0 @@ -(* functions for driving specParser *) - -open Batteries - -(* config *) -let save_dot = true - -let line = ref 1 -exception Parse_error of string - -let parse ?repl:(repl=false) ?print:(print=false) ?dot:(dot=false) cin = - let lexbuf = Lexing.from_channel cin in - let defs = ref [] in - (* Printf.printf "\nrepl: %B, print: %B, dot: %B, save_dot: %B\n" repl print dot save_dot; *) - try - while true do (* loop over all lines *) - try - let result = SpecParser.file SpecLexer.token lexbuf in - defs := !defs@[result]; - incr line; - if print then (print_endline (SpecCore.def_to_string result); flush stdout) - with - (* just an empty line -> don't print *) - | SpecCore.Endl -> incr line - (* somehow gets raised in some cases instead of SpecCore.Eof *) - | BatInnerIO.Input_closed -> raise SpecCore.Eof - (* catch and print in repl-mode *) - | e when repl -> print_endline (Printexc.to_string e) - done; - ([], []) (* never happens, but ocaml needs it for type *) - with - (* done *) - | SpecCore.Eof -> - let nodes = List.filter_map (function SpecCore.Node x -> Some x | _ -> None) !defs in - let edges = List.filter_map (function SpecCore.Edge x -> Some x | _ -> None) !defs in - if print then Printf.printf "\n#Definitions: %i, #Nodes: %i, #Edges: %i\n" - (List.length !defs) (List.length nodes) (List.length edges); - if save_dot && not dot then ( - let dotgraph = SpecCore.to_dot_graph !defs in - output_file ~filename:"result/graph.dot" ~text:dotgraph; - print_endline ("saved graph as "^Sys.getcwd ()^"/result/graph.dot"); - ); - if dot then ( - print_endline (SpecCore.to_dot_graph !defs) - ); - (nodes, edges) - (* stop on parsing error if not in REPL and include line number *) - | e -> raise (Parse_error ("Line "^string_of_int !line^": "^Printexc.to_string e)) - -let parseFile filename = parse (open_in filename) - -(* print ~first:"[" ~sep:", " ~last:"]" print_any stdout @@ 5--10 *) diff --git a/tests/regression/18-file/01-ok.c b/tests/regression/18-file/01-ok.c deleted file mode 100644 index 5c1f21ff1c..0000000000 --- a/tests/regression/18-file/01-ok.c +++ /dev/null @@ -1,12 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -int main(){ - FILE *fp; - fp = fopen("test.txt", "a"); - fprintf(fp, "Testing...\n"); - fclose(fp); -} - -// All ok! diff --git a/tests/regression/18-file/02-function.c b/tests/regression/18-file/02-function.c deleted file mode 100644 index fc3157c264..0000000000 --- a/tests/regression/18-file/02-function.c +++ /dev/null @@ -1,17 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -FILE *fp; - -void f(){ - fp = fopen("test.txt", "a"); -} - -int main(){ - f(); - fprintf(fp, "Testing...\n"); - fclose(fp); -} - -// All ok! diff --git a/tests/regression/18-file/03-if-close.c b/tests/regression/18-file/03-if-close.c deleted file mode 100644 index b2bf1ebe97..0000000000 --- a/tests/regression/18-file/03-if-close.c +++ /dev/null @@ -1,15 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -FILE *fp; - -int main(){ - int b; - fp = fopen("test.txt", "a"); // WARN: MAYBE file is never closed - - fprintf(fp, "Testing...\n"); - - if (b) - fclose(fp); -} // WARN: MAYBE unclosed files: fp diff --git a/tests/regression/18-file/04-no-open.c b/tests/regression/18-file/04-no-open.c deleted file mode 100644 index 70683f3852..0000000000 --- a/tests/regression/18-file/04-no-open.c +++ /dev/null @@ -1,10 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -FILE *fp; - -int main(){ - fprintf(fp, "Testing...\n"); // WARN: writing to unopened file handle fp - fclose(fp); // WARN: closeing unopened file handle fp -} diff --git a/tests/regression/18-file/05-open-mode.c b/tests/regression/18-file/05-open-mode.c deleted file mode 100644 index 77326d7a70..0000000000 --- a/tests/regression/18-file/05-open-mode.c +++ /dev/null @@ -1,11 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -FILE *fp; - -int main(){ - fp = fopen("test.txt", "r"); - fprintf(fp, "Testing...\n"); // WARN: writing to read-only file handle fp - fclose(fp); -} diff --git a/tests/regression/18-file/06-2open.c b/tests/regression/18-file/06-2open.c deleted file mode 100644 index 2826c2f1dc..0000000000 --- a/tests/regression/18-file/06-2open.c +++ /dev/null @@ -1,12 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -FILE *fp; - -int main(){ - fp = fopen("test1.txt", "a"); // WARN: file is never closed - fp = fopen("test2.txt", "a"); // WARN: overwriting still opened file handle fp - fprintf(fp, "Testing...\n"); - fclose(fp); -} // WARN: unclosed files: fp diff --git a/tests/regression/18-file/07-2close.c b/tests/regression/18-file/07-2close.c deleted file mode 100644 index 0545bf9814..0000000000 --- a/tests/regression/18-file/07-2close.c +++ /dev/null @@ -1,11 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -int main(){ - FILE *fp; - fp = fopen("test.txt", "a"); - fprintf(fp, "Testing...\n"); - fclose(fp); - fclose(fp); // WARN: closeing already closed file handle fp -} diff --git a/tests/regression/18-file/08-var-reuse.c b/tests/regression/18-file/08-var-reuse.c deleted file mode 100644 index 1caa238517..0000000000 --- a/tests/regression/18-file/08-var-reuse.c +++ /dev/null @@ -1,15 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -int main(){ - FILE *fp; - fp = fopen("test.txt", "a"); - fprintf(fp, "Testing...\n"); - fclose(fp); - fp = fopen("test2.txt", "a"); - fprintf(fp, "Testing...\n"); - fclose(fp); -} - -// All ok! diff --git a/tests/regression/18-file/09-inf-loop-no-close.c b/tests/regression/18-file/09-inf-loop-no-close.c deleted file mode 100644 index e9563ef195..0000000000 --- a/tests/regression/18-file/09-inf-loop-no-close.c +++ /dev/null @@ -1,17 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -FILE *fp; - -int main(){ - int i; - fp = fopen("test.txt", "a"); // WARN: file is never closed - - while (i){ - fprintf(fp, "Testing...\n"); - i++; - } - - //fclose(fp); -} // WARN: unclosed files: fp diff --git a/tests/regression/18-file/10-inf-loop-ok.c b/tests/regression/18-file/10-inf-loop-ok.c deleted file mode 100644 index d88fde272e..0000000000 --- a/tests/regression/18-file/10-inf-loop-ok.c +++ /dev/null @@ -1,19 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -FILE *fp; - -int main(){ - int i; - fp = fopen("test.txt", "a"); - - while (i){ - fprintf(fp, "Testing...\n"); - i++; - } - - fclose(fp); -} - -// All ok. diff --git a/tests/regression/18-file/11-2if.c b/tests/regression/18-file/11-2if.c deleted file mode 100644 index e24fec6e46..0000000000 --- a/tests/regression/18-file/11-2if.c +++ /dev/null @@ -1,18 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -FILE *fp; - -int main(){ - int b; - fp = fopen("test.txt", "a"); // WARN: MAYBE file is never closed - - if (b) - fclose(fp); - - fprintf(fp, "Testing...\n"); // WARN: MAYBE writing to closed file handle fp - - if (!b) - fclose(fp); // WARN: MAYBE closeing already closed file handle fp -} // WARN: MAYBE unclosed files: fp diff --git a/tests/regression/18-file/12-2close-if.c b/tests/regression/18-file/12-2close-if.c deleted file mode 100644 index 4934b33114..0000000000 --- a/tests/regression/18-file/12-2close-if.c +++ /dev/null @@ -1,15 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -int main(){ - FILE *fp; - int b; - fp = fopen("test.txt", "a"); - fprintf(fp, "Testing...\n"); - - if (b) - fclose(fp); - - fclose(fp); // WARN: MAYBE closeing already closed file handle fp -} diff --git a/tests/regression/18-file/13-ptr-arith-ok.c b/tests/regression/18-file/13-ptr-arith-ok.c deleted file mode 100644 index f707110957..0000000000 --- a/tests/regression/18-file/13-ptr-arith-ok.c +++ /dev/null @@ -1,16 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -int main(){ - FILE *fp; - fp = fopen("test.txt", "a"); // WARN: MAYBE file is never closed - fprintf(fp, "Testing...\n"); - - fp++; // WARN: changed pointer fp (no longer safe) - fp--; // WARN: changed pointer fp (no longer safe) - - fclose(fp); // WARN: MAYBE closeing already closed file handle fp -} // WARN: MAYBE unclosed files: fp - -// OPT: All ok! diff --git a/tests/regression/18-file/14-ptr-arith-close.c b/tests/regression/18-file/14-ptr-arith-close.c deleted file mode 100644 index 3f9cd21ee2..0000000000 --- a/tests/regression/18-file/14-ptr-arith-close.c +++ /dev/null @@ -1,13 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -int main(){ - FILE *fp; - fp = fopen("test.txt", "a"); // WARN: MAYBE file is never closed - fprintf(fp, "Testing...\n"); - - fp++; // WARN: changed pointer fp (no longer safe) - - fclose(fp); // WARN: MAYBE closeing already closed file handle fp -} // WARN: MAYBE unclosed files: fp diff --git a/tests/regression/18-file/15-var-switch.c b/tests/regression/18-file/15-var-switch.c deleted file mode 100644 index d7f74b85db..0000000000 --- a/tests/regression/18-file/15-var-switch.c +++ /dev/null @@ -1,18 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -int main(){ - FILE *fp1; - fp1 = fopen("test.txt", "a"); - fprintf(fp1, "Testing...\n"); - - FILE *fp2; - fp2 = fopen("test.txt", "a"); // WARN: file is never closed - fprintf(fp2, "Testing...\n"); - - fp2 = fp1; - - fclose(fp1); - fclose(fp2); // WARN: closeing already closed file handle fp2 -} // WARN: unclosed files: fp2 diff --git a/tests/regression/18-file/16-var-reuse-close.c b/tests/regression/18-file/16-var-reuse-close.c deleted file mode 100644 index cb1fb5fd22..0000000000 --- a/tests/regression/18-file/16-var-reuse-close.c +++ /dev/null @@ -1,14 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -int main(){ - FILE *fp; - fp = fopen("test.txt", "a"); - fprintf(fp, "Testing...\n"); - fclose(fp); - - fp = fopen("test.txt", "a"); // WARN: file is never closed - fprintf(fp, "Testing...\n"); - // fclose(fp); -} // WARN: unclosed files: fp diff --git a/tests/regression/18-file/17-myfopen.c b/tests/regression/18-file/17-myfopen.c deleted file mode 100644 index 3e005c6e70..0000000000 --- a/tests/regression/18-file/17-myfopen.c +++ /dev/null @@ -1,21 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - - -FILE* myfopen(){ - // FILE *fp_tmp = fopen("test.txt", "a"); // local! - return fopen("test.txt", "a"); -} - -int main(){ - FILE *fp1; - FILE *fp2; - fp1 = myfopen(); - fp2 = myfopen(); // WARN: file is never closed - - fprintf(fp1, "Testing...\n"); - fclose(fp1); - fprintf(fp2, "Testing...\n"); - // fclose(fp2); -} // WARN: unclosed files: fp2 diff --git a/tests/regression/18-file/18-myfopen-arg.c b/tests/regression/18-file/18-myfopen-arg.c deleted file mode 100644 index 5d98db4c53..0000000000 --- a/tests/regression/18-file/18-myfopen-arg.c +++ /dev/null @@ -1,20 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - - -FILE* myfopen(char* f){ - return fopen(f, "a"); -} - -int main(){ - FILE *fp1; - FILE *fp2; - fp1 = myfopen("test1.txt"); - fp2 = myfopen("test2.txt"); // WARN: file is never closed - - fprintf(fp1, "Testing...\n"); - fclose(fp1); - fprintf(fp2, "Testing...\n"); - // fclose(fp2); -} // WARN: unclosed files: fp2 diff --git a/tests/regression/18-file/19-if-close-else.c b/tests/regression/18-file/19-if-close-else.c deleted file mode 100644 index 049e8454b4..0000000000 --- a/tests/regression/18-file/19-if-close-else.c +++ /dev/null @@ -1,17 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -FILE *fp; - -int main(){ - int b; - fp = fopen("test.txt", "a"); - - if (b) - fclose(fp); - else - fprintf(fp, "Testing...\n"); - - fclose(fp); // WARN: MAYBE closeing already closed file handle fp -} diff --git a/tests/regression/18-file/20-loop-close.c b/tests/regression/18-file/20-loop-close.c deleted file mode 100644 index 981248c152..0000000000 --- a/tests/regression/18-file/20-loop-close.c +++ /dev/null @@ -1,18 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -FILE *fp; - -int main(){ - int i; - fp = fopen("test.txt", "a"); // WARN: MAYBE file is never closed - - while (i){ // May closed (11, 3), open(test.txt, Write) (7, 3) - fprintf(fp, "Testing...\n"); // WARN: MAYBE writing to closed file handle fp - fclose(fp); // WARN: MAYBE closeing already closed file handle fp - i++; - } - // why: fp -> Must open(test.txt, Write) (7, 3) - // -> because loop wouldn't exit? -} // WARN: MAYBE unclosed files: fp diff --git a/tests/regression/18-file/21-for-i.c b/tests/regression/18-file/21-for-i.c deleted file mode 100644 index e41bb9b005..0000000000 --- a/tests/regression/18-file/21-for-i.c +++ /dev/null @@ -1,26 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -FILE *fp; - -int main(){ - int i; - fp = fopen("test.txt", "w"); // WARN: MAYBE file is never closed - - for(i=1; i<10; i++){ // join - // i -> Unknown int - if(i%2){ - // i -> Unknown int - // fprintf(fp, "Testing...%s\n", i); // Segmentation fault! - // actually shouldn't warn because open and close are always alternating... - fprintf(fp, "Testing...%i\n", i); // WARN: MAYBE writing to closed file handle fp - fclose(fp); // WARN: MAYBE closeing already closed file handle fp - }else{ - fp = fopen("test.txt", "a"); // WARN: MAYBE file is never closed - } - // why no join? - } - // fp opened or closed? (last i=9 -> open) - // widening -> Warn: might be unclosed -} // WARN: MAYBE unclosed files: fp diff --git a/tests/regression/18-file/22-f_int.c b/tests/regression/18-file/22-f_int.c deleted file mode 100644 index f0376fc5a9..0000000000 --- a/tests/regression/18-file/22-f_int.c +++ /dev/null @@ -1,13 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -int f(int x){ - return 2*x; -} - -int main(){ - int a = 1; - a = f(2); - return 0; -} diff --git a/tests/regression/18-file/23-f_str.c b/tests/regression/18-file/23-f_str.c deleted file mode 100644 index 81224d2e72..0000000000 --- a/tests/regression/18-file/23-f_str.c +++ /dev/null @@ -1,13 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -char* f(char* x){ - return x; -} - -int main(){ - char* a = "foo"; - a = f("bar"); - return 0; -} diff --git a/tests/regression/18-file/24-f_wstr.c b/tests/regression/18-file/24-f_wstr.c deleted file mode 100644 index 2379c1f718..0000000000 --- a/tests/regression/18-file/24-f_wstr.c +++ /dev/null @@ -1,14 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include -#include - -wchar_t* f(wchar_t* x){ - return x; -} - -int main(){ - wchar_t* a = L"foo"; - a = f(L"bar"); - return 0; -} diff --git a/tests/regression/18-file/25-mem-ok.c b/tests/regression/18-file/25-mem-ok.c deleted file mode 100644 index 00ba189b8d..0000000000 --- a/tests/regression/18-file/25-mem-ok.c +++ /dev/null @@ -1,29 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -int main(){ - FILE *fp[3]; - // Array -> varinfo with index-offset - fp[1] = fopen("test.txt", "a"); - fprintf(fp[1], "Testing...\n"); - fclose(fp[1]); - - - struct foo { - int i; - FILE *fp; - } bar; - // Struct -> varinfo with field-offset - bar.fp = fopen("test.txt", "a"); - fprintf(bar.fp, "Testing...\n"); - fclose(bar.fp); - - - // Pointer -> Mem exp - *(fp+2) = fopen("test.txt", "a"); - fprintf(*(fp+2), "Testing...\n"); - fclose(*(fp+2)); -} - -// All ok! diff --git a/tests/regression/18-file/26-open-error-ok.c b/tests/regression/18-file/26-open-error-ok.c deleted file mode 100644 index 5cf3aaf7bb..0000000000 --- a/tests/regression/18-file/26-open-error-ok.c +++ /dev/null @@ -1,15 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -int main (){ - FILE *fp; - fp = fopen("test.txt", "w"); - - if(fp!=NULL){ - fprintf(fp, "Testing..."); - fclose(fp); - } -} - -// All ok! diff --git a/tests/regression/18-file/27-open-error.c b/tests/regression/18-file/27-open-error.c deleted file mode 100644 index bd3048208f..0000000000 --- a/tests/regression/18-file/27-open-error.c +++ /dev/null @@ -1,13 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -int main (){ - FILE *fp; - fp = fopen("test.txt", "w"); // WARN: MAYBE file is never closed - - if(fp==NULL){ - fprintf(fp, "Testing..."); // WARN: writing to unopened file handle fp - fclose(fp); // WARN: closeing unopened file handle fp - } -} // WARN: MAYBE unclosed files: fp diff --git a/tests/regression/18-file/28-multiple-exits.c b/tests/regression/18-file/28-multiple-exits.c deleted file mode 100644 index 04fa5abab0..0000000000 --- a/tests/regression/18-file/28-multiple-exits.c +++ /dev/null @@ -1,14 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -int main(){ - FILE *fp; - fp = fopen("test.txt", "a"); // WARN: MAYBE file is never closed - fprintf(fp, "Testing...\n"); - int b; - if(b) - return 1; // WARN: unclosed files: fp - fclose(fp); - return 0; -} diff --git a/tests/regression/18-file/29-alias-global.c b/tests/regression/18-file/29-alias-global.c deleted file mode 100644 index 17b94748c0..0000000000 --- a/tests/regression/18-file/29-alias-global.c +++ /dev/null @@ -1,22 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -FILE* fp; -FILE* myfopen(char* f){ - fp = fopen(f, "a"); - return fp; -} - -int main(){ - FILE *fp1; - FILE *fp2; - fp1 = myfopen("test1.txt"); - fp2 = myfopen("test2.txt"); - fprintf(fp1, "Testing...\n"); - fclose(fp1); - fprintf(fp2, "Testing...\n"); - fclose(fp2); -} - -// All ok! diff --git a/tests/regression/18-file/30-ptr-of-ptr.c b/tests/regression/18-file/30-ptr-of-ptr.c deleted file mode 100644 index 5a8d1f97a9..0000000000 --- a/tests/regression/18-file/30-ptr-of-ptr.c +++ /dev/null @@ -1,14 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -int main(){ - FILE *fp1; - fp1 = fopen("test.txt", "a"); - FILE **fp2; - - fp2 = &fp1; - - fclose(fp1); - fclose(*fp2); // WARN: closeing already closed file handle fp1 -} diff --git a/tests/regression/18-file/31-var-reuse-fun.c b/tests/regression/18-file/31-var-reuse-fun.c deleted file mode 100644 index 9c0ccb16a2..0000000000 --- a/tests/regression/18-file/31-var-reuse-fun.c +++ /dev/null @@ -1,16 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -FILE* myfopen(char* f){ - FILE* fp; - fp = fopen(f, "a"); - return fp; -} - -int main(){ - FILE *fp; - fp = fopen("test1.txt", "a"); // WARN: file is never closed - fp = myfopen("test2.txt"); // WARN: overwriting still opened file handle fp - fclose(fp); -} // WARN: unclosed files: fp diff --git a/tests/regression/18-file/32-multi-ptr-close.c b/tests/regression/18-file/32-multi-ptr-close.c deleted file mode 100644 index e252d563a5..0000000000 --- a/tests/regression/18-file/32-multi-ptr-close.c +++ /dev/null @@ -1,25 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -int main(){ - FILE *fp1; - fp1 = fopen("test1.txt", "a"); - fprintf(fp1, "Testing...\n"); - - FILE *fp2; - fp2 = fopen("test2.txt", "a"); - fprintf(fp2, "Testing...\n"); - - FILE **fp; - int b; - if(b){ - fp = &fp1; - }else{ - fp = &fp2; - } - - fclose(*fp); - fclose(fp1); // WARN: MAYBE closeing already closed file handle fp1 - fclose(fp2); // WARN: MAYBE closeing already closed file handle fp2 -} diff --git a/tests/regression/18-file/33-multi-ptr-open.c b/tests/regression/18-file/33-multi-ptr-open.c deleted file mode 100644 index b3cfa0ade4..0000000000 --- a/tests/regression/18-file/33-multi-ptr-open.c +++ /dev/null @@ -1,23 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -int main(){ - FILE *fp1; - fp1 = fopen("test1.txt", "a"); // WARN: MAYBE file is never closed - - FILE *fp2; - fp2 = fopen("test2.txt", "r"); // WARN: MAYBE file is never closed - - FILE **fp; - int b; - if(b){ - fp = &fp1; - }else{ - fp = &fp2; - } - - fprintf(*fp, "Testing...\n"); // WARN: MAYBE writing to read-only file handle fp - - fclose(*fp); -} // WARN: MAYBE unclosed files: fp1, fp2 diff --git a/tests/regression/18-file/34-multi-alias-close.c b/tests/regression/18-file/34-multi-alias-close.c deleted file mode 100644 index 0ebb9ddd30..0000000000 --- a/tests/regression/18-file/34-multi-alias-close.c +++ /dev/null @@ -1,25 +0,0 @@ -// SKIP PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -int main(){ - FILE *fp1; - fp1 = fopen("test1.txt", "a"); - fprintf(fp1, "Testing...\n"); - - FILE *fp2; - fp2 = fopen("test2.txt", "a"); - fprintf(fp2, "Testing...\n"); - - FILE *fp; - int b; - if(b){ - fp = fp1; - }else{ - fp = fp2; - } - - fclose(fp); - fclose(fp1); // WARN: MAYBE closeing already closed file handle fp1 - fclose(fp2); // WARN: MAYBE closeing already closed file handle fp2 -} diff --git a/tests/regression/18-file/35-multi-alias-open.c b/tests/regression/18-file/35-multi-alias-open.c deleted file mode 100644 index 21a4a9cca6..0000000000 --- a/tests/regression/18-file/35-multi-alias-open.c +++ /dev/null @@ -1,23 +0,0 @@ -// SKIP PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -int main(){ - FILE *fp1; - fp1 = fopen("test1.txt", "a"); // WARN: MAYBE file is never closed - - FILE *fp2; - fp2 = fopen("test2.txt", "r"); // WARN: MAYBE file is never closed - - FILE *fp; - int b; - if(b){ - fp = fp1; - }else{ - fp = fp2; - } - - fprintf(fp, "Testing...\n"); // WARN: MAYBE writing to read-only file handle fp - - fclose(fp); -} // WARN: MAYBE unclosed files: fp1, fp2 diff --git a/tests/regression/18-file/36-fun-ptr.c b/tests/regression/18-file/36-fun-ptr.c deleted file mode 100644 index 4f70bf7382..0000000000 --- a/tests/regression/18-file/36-fun-ptr.c +++ /dev/null @@ -1,14 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -int main(){ - FILE *fp; - FILE* (*f)(const char *, const char*); - f = fopen; - fp = f("test.txt", "a"); - fprintf(fp, "Testing...\n"); - fclose(fp); -} - -// All ok! diff --git a/tests/regression/18-file/37-var-switch-alias.c b/tests/regression/18-file/37-var-switch-alias.c deleted file mode 100644 index 5dfde5a2d9..0000000000 --- a/tests/regression/18-file/37-var-switch-alias.c +++ /dev/null @@ -1,18 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -int main(){ - FILE *fp1; - fp1 = fopen("test.txt", "a"); - fprintf(fp1, "Testing...\n"); - - FILE *fp2; - fp2 = fopen("test.txt", "a"); // WARN: file is never closed - fprintf(fp2, "Testing...\n"); - - fp2 = fp1; - - fclose(fp2); - fclose(fp1); // WARN: closeing already closed file handle fp1 -} // WARN: unclosed files: fp2 diff --git a/tests/regression/18-file/README.md b/tests/regression/18-file/README.md new file mode 100644 index 0000000000..0e93e175c6 --- /dev/null +++ b/tests/regression/18-file/README.md @@ -0,0 +1,2 @@ +The file analysis has been removed from recent Goblint versions, please use Release 2.3.0 +Folder is left in place to avoid renumbering all tests diff --git a/tests/regression/18-file/file.c b/tests/regression/18-file/file.c deleted file mode 100644 index fc2ebe1699..0000000000 --- a/tests/regression/18-file/file.c +++ /dev/null @@ -1,44 +0,0 @@ -#include - -int main(){ - - // no errors - FILE *fp; - fp = fopen("test.txt", "a"); - if(fp!=0) { - fprintf(fp, "Testing...\n"); - fclose(fp); - } - - // missing fopen -> compiles, but leads to Segmentation fault - FILE *fp2; - // fp2 = fopen("test.txt", "a"); - fprintf(fp2, "Testing...\n"); // WARN - fclose(fp2); // WARN - - // writing to a read-only file -> doesn't do anything - FILE *fp3; - fp3 = fopen("test.txt", "r"); - fprintf(fp3, "Testing...\n"); // (WARN) - fclose(fp3); - - // accessing closed file -> write doesn't do anything - FILE *fp4; - fp4 = fopen("test.txt", "a"); - fclose(fp4); - fprintf(fp4, "Testing...\n"); // WARN - - // missing fclose - FILE *fp5; - fp5 = fopen("test.txt", "a"); // WARN - fprintf(fp5, "Testing...\n"); - - // missing assignment to file handle - fopen("test.txt", "a"); // WARN - - - // bad style: - // opening file but not doing anything - - return 0; // WARN about all unclosed files -} \ No newline at end of file diff --git a/tests/regression/18-file/file.optimistic.spec b/tests/regression/18-file/file.optimistic.spec deleted file mode 100644 index d42e2217b7..0000000000 --- a/tests/regression/18-file/file.optimistic.spec +++ /dev/null @@ -1,34 +0,0 @@ -w1 "file handle is not saved!" -w2 "closeing unopened file handle $" -w3 "writing to unopened file handle $" -w4 "writing to read-only file handle $" -w5 "closeing already closed file handle $" -w6 "writing to closed file handle $" -w7 "overwriting still opened file handle $" -w8 "unrecognized file open mode for file handle $" - -1 -> w1 fopen(_, _) -1 -> w2 fclose($fp) -1 -> w3 fprintf($fp, _) -1 -> open_read $fp = fopen(path, "r") -1 -> open_write $fp = fopen(path, r"[wa]") // see OCaml doc for details (e.g. \\| for alternatives) -1 -> w8 $fp = fopen(path, _) - -open_read -> w4 fprintf($fp, _) - -open_read -w7>> 1 $fp = fopen(path, _) -open_write -w7>> 1 $fp = fopen(path, _) - -open_read -> closed fclose($fp) -open_write -> closed fclose($fp) - -closed -> w5 fclose($fp) -closed -> w6 fprintf($fp, _) -closed ->> 1 _ // let state 1 handle the rest - -// setup which states are end states -1 -> end _ -closed -> end _ -// warning for all entries that are not in an end state -_end "file is never closed" -_END "unclosed files: $" \ No newline at end of file diff --git a/tests/regression/18-file/file.spec b/tests/regression/18-file/file.spec deleted file mode 100644 index aeb747abfd..0000000000 --- a/tests/regression/18-file/file.spec +++ /dev/null @@ -1,57 +0,0 @@ -w1 "file handle is not saved!" -w2 "closeing unopened file handle $" -w3 "writing to unopened file handle $" -w4 "writing to read-only file handle $" -w5 "closeing already closed file handle $" -w6 "writing to closed file handle $" -w7 "overwriting still opened file handle $" -w8 "unrecognized file open mode for file handle $" - -// TODO later add fputs and stuff -1 -> w1 fopen(_, _) -1 -> w2 fclose($fp) -1 -> w3 fprintf($fp, _) -//1 -> open_read $fp = fopen(path, "r") -//1 -> open_write $fp = fopen(path, r"[wa]") // see OCaml doc for details (e.g. \\| for alternatives) -//1 -> w8 $fp = fopen(path, _) - -// go to unchecked states first -1 -> u_open_read $fp = fopen(path, "r") -1 -> u_open_write $fp = fopen(path, r"[wa]") -1 -> w8 $fp = fopen(path, _) -// once branch(exp, tv) is matched, return dom with 1. arg (lval = exp) and true/false -// forwarding from branch is not possible (would need an extra map for storing states) -> ignore it -// warnings are also ignored -// then in branch take out lval, check exp and do the transition to a checked state -u_open_read -> 1 branch($key==0, true) -u_open_read -> open_read branch($key==0, false) -u_open_write -> 1 branch($key==0, true) -u_open_write -> open_write branch($key==0, false) - -// alternative: forward everything. Problem: saving arguments of call (special_fn -> branch -> special_fn) -// 1 ->> open_check $fp = fopen(path, _) -// open_check ->> 1 branch($fp==0, true) -// open_check ->> open branch($fp==0, false) -// open -> open_read $fp = fopen(path, "r") -// open -> open_write $fp = fopen(path, "[wa]") -// open -> w8 $fp = fopen(path, _) - -open_read -> w4 fprintf($fp, _) -// open_write -> open_write fprintf($fp, _) // not needed, but changes loc - -open_read -w7>> 1 $fp = fopen(path, _) -open_write -w7>> 1 $fp = fopen(path, _) - -open_read -> closed fclose($fp) -open_write -> closed fclose($fp) - -closed -> w5 fclose($fp) -closed -> w6 fprintf($fp, _) -closed ->> 1 _ // let state 1 handle the rest - -// setup which states are end states -1 -> end _ -closed -> end _ -// warning for all entries that are not in an end state -_end "file is never closed" -_END "unclosed files: $" \ No newline at end of file diff --git a/tests/regression/19-spec/01-malloc-free.c b/tests/regression/19-spec/01-malloc-free.c deleted file mode 100644 index 43ee527dba..0000000000 --- a/tests/regression/19-spec/01-malloc-free.c +++ /dev/null @@ -1,19 +0,0 @@ -#include -#include - -int main(){ - int *ip; - //*ip = 5; // segfault - //printf("%i", *ip); // segfault - ip = malloc(sizeof(int)); // assume malloc never fails - - // do stuff - //*ip = 5; - - free(ip); - //free(ip); // crash: double free or corruption - *ip = 5; // undefined but no crash - printf("%i", *ip); // undefined but printed 5 - ip = NULL; // make sure the pointer is not used anymore - *ip = 5; // segfault -} diff --git a/tests/regression/19-spec/02-mutex_rc.c b/tests/regression/19-spec/02-mutex_rc.c deleted file mode 100644 index 82c1642a93..0000000000 --- a/tests/regression/19-spec/02-mutex_rc.c +++ /dev/null @@ -1,23 +0,0 @@ -#include -#include - -int myglobal; -pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; -pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER; - -void *t_fun(void *arg) { - pthread_mutex_lock(&mutex1); - myglobal=myglobal+1; // RACE! - pthread_mutex_unlock(&mutex1); - return NULL; -} - -int main(void) { - pthread_t id; - pthread_create(&id, NULL, t_fun, NULL); - pthread_mutex_lock(&mutex2); - myglobal=myglobal+1; // RACE! - pthread_mutex_unlock(&mutex2); - pthread_join (id, NULL); - return 0; -} diff --git a/tests/regression/19-spec/README.md b/tests/regression/19-spec/README.md new file mode 100644 index 0000000000..d7e3ae3c8e --- /dev/null +++ b/tests/regression/19-spec/README.md @@ -0,0 +1,2 @@ +The spec analysis has been removed from recent Goblint versions, please use Release 2.3.0 +Folder is left in place to avoid renumbering all tests diff --git a/tests/regression/19-spec/malloc.optimistic.spec b/tests/regression/19-spec/malloc.optimistic.spec deleted file mode 100644 index 860c573814..0000000000 --- a/tests/regression/19-spec/malloc.optimistic.spec +++ /dev/null @@ -1,23 +0,0 @@ -w1 "pointer is not saved [leak]" -w2 "freeing unallocated pointer $ [segfault?]" -w3 "writing to unallocated pointer $ [segfault?]" -w4 "overwriting unfreed pointer $ [leak]" -w5 "freeing already freed pointer $ [double free!]" - -1 -w1> 1 malloc(_) -1 -w2> 1 free($p) -1 -w3> 1 *$p = _ -1 -> alloc $p = malloc(_) // TODO does compiler check size? - -alloc -w4> alloc $p = malloc(_) -alloc -> freed free($p) - -freed -w5> freed free($p) -freed ->> 1 _ // let state 1 handle the rest - -// setup which states are end states -1 -> end _ -freed -> end _ -// warning for all entries that are not in an end state -_end "pointer is never freed" -_END "unfreed pointers: $" \ No newline at end of file diff --git a/tests/regression/19-spec/malloc.spec b/tests/regression/19-spec/malloc.spec deleted file mode 100644 index 9f09430051..0000000000 --- a/tests/regression/19-spec/malloc.spec +++ /dev/null @@ -1,26 +0,0 @@ -w1 "pointer is not saved [leak]" -w2 "freeing unallocated pointer $ [segfault?]" -w3 "writing to unallocated pointer $ [segfault?]" -w4 "overwriting unfreed pointer $ [leak]" -w5 "freeing already freed pointer $ [double free!]" - -1 -w1> 1 malloc(_) -1 -w2> 1 free($p) -1 -w3> 1 *$p = _ -1 -> u_alloc $p = malloc(_) - -u_alloc -> 1 branch($key==0, true) -u_alloc -> alloc branch($key==0, false) - -alloc -w4> alloc $p = malloc(_) -alloc -> freed free($p) - -freed -w5> freed free($p) -freed ->> 1 _ // let state 1 handle the rest - -// setup which states are end states -1 -> end _ -freed -> end _ -// warning for all entries that are not in an end state -_end "pointer is never freed" -_END "unfreed pointers: $" \ No newline at end of file diff --git a/tests/regression/19-spec/mutex-lock.spec b/tests/regression/19-spec/mutex-lock.spec deleted file mode 100644 index 1ec8264078..0000000000 --- a/tests/regression/19-spec/mutex-lock.spec +++ /dev/null @@ -1,31 +0,0 @@ -w1 "unlocking not locked mutex" -w2 "locking already locked mutex" - -1 -w1> 1 pthread_mutex_unlock($p) -1 -> lock pthread_mutex_lock($p) - -lock -w2> lock pthread_mutex_lock($p) -lock -> 1 pthread_mutex_unlock($p) - -// setup which states are end states -1 -> end _ -// warning for all entries that are not in an end state -_end "mutex is never unlocked" -_END "locked mutexes: $" - - - -//w1 "joining not created thread" -//w2 "overwriting id of already created thread" -// -//1 -w1> 1 pthread_join ($p, _) -//1 -> created pthread_create($p, _, _, _) -// -//created -w2> created pthread_create($p, _, _, _) -//created -> 1 pthread_join ($p, _) -// -//// setup which states are end states -//1 -> end _ -//// warning for all entries that are not in an end state -//_end "thread is never joined" -//_END "unjoined threads: $" \ No newline at end of file From 9e0ef1cc2f5c0c553e92355907ea55b813f61d31 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 1 Dec 2023 21:06:25 +0100 Subject: [PATCH 1193/1312] Rm: Mainspec --- scripts/goblint-lib-modules.py | 1 - src/dune | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/scripts/goblint-lib-modules.py b/scripts/goblint-lib-modules.py index 6369af53a1..6c264a117b 100755 --- a/scripts/goblint-lib-modules.py +++ b/scripts/goblint-lib-modules.py @@ -30,7 +30,6 @@ "MessagesCompare", "PrivPrecCompare", "ApronPrecCompare", - "Mainspec", # libraries "Goblint_std", diff --git a/src/dune b/src/dune index acd5348acb..d3fe6bdd0d 100644 --- a/src/dune +++ b/src/dune @@ -6,7 +6,7 @@ (library (name goblint_lib) (public_name goblint.lib) - (modules :standard \ goblint mainspec privPrecCompare apronPrecCompare messagesCompare) + (modules :standard \ goblint privPrecCompare apronPrecCompare messagesCompare) (libraries goblint.sites goblint.build-info goblint-cil.all-features batteries.unthreaded qcheck-core.runner sha json-data-encoding jsonrpc cpu arg-complete fpath yaml yaml.unix uuidm goblint_timing catapult goblint_backtrace fileutils goblint_std goblint_common ; Conditionally compile based on whether apron optional dependency is installed or not. ; Alternative dependencies seem like the only way to optionally depend on optional dependencies. @@ -73,10 +73,10 @@ (copy_files# witness/z3/*.ml) (executables - (names goblint mainspec) - (public_names goblint -) + (names goblint) + (public_names goblint) (modes byte native) ; https://dune.readthedocs.io/en/stable/dune-files.html#linking-modes - (modules goblint mainspec) + (modules goblint) (libraries goblint.lib goblint.sites.dune goblint.build-info.dune goblint_std) (preprocess (pps ppx_deriving.std ppx_deriving_hash ppx_deriving_yojson)) (flags :standard -linkall -open Goblint_std) From 8104b3e08b1925efe289de73558d735f7804eae5 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 1 Dec 2023 21:33:12 +0100 Subject: [PATCH 1194/1312] Remove some workarounds not needed with batteries >=3.5.1 --- src/framework/cfgTools.ml | 2 +- src/solvers/postSolver.ml | 8 +------- src/util/std/gobHashtbl.ml | 4 ---- 3 files changed, 2 insertions(+), 12 deletions(-) diff --git a/src/framework/cfgTools.ml b/src/framework/cfgTools.ml index 8f98a48e84..7b673f99bc 100644 --- a/src/framework/cfgTools.ml +++ b/src/framework/cfgTools.ml @@ -475,7 +475,7 @@ let createCFG (file: file) = ); if Messages.tracing then Messages.trace "cfg" "CFG building finished.\n\n"; if get_bool "dbg.verbose" then - ignore (Pretty.eprintf "cfgF (%a), cfgB (%a)\n" GobHashtbl.pretty_statistics (GobHashtbl.magic_stats cfgF) GobHashtbl.pretty_statistics (GobHashtbl.magic_stats cfgB)); + ignore (Pretty.eprintf "cfgF (%a), cfgB (%a)\n" GobHashtbl.pretty_statistics (NH.stats cfgF) GobHashtbl.pretty_statistics (NH.stats cfgB)); cfgF, cfgB, skippedByEdge let createCFG = Timing.wrap "createCFG" createCFG diff --git a/src/solvers/postSolver.ml b/src/solvers/postSolver.ml index f96ca832a1..e01560c752 100644 --- a/src/solvers/postSolver.ml +++ b/src/solvers/postSolver.ml @@ -154,13 +154,7 @@ struct module VH = Hashtbl.Make (S.Var) (* starts as Hashtbl for quick lookup *) - let starth = - (* VH.of_list S.starts *) (* TODO: BatHashtbl.Make.of_list is broken, use after new Batteries release *) - let starth = VH.create (List.length S.starts) in - List.iter (fun (x, d) -> - VH.replace starth x d - ) S.starts; - starth + let starth = VH.of_list S.starts let system x = match S.system x, VH.find_option starth x with diff --git a/src/util/std/gobHashtbl.ml b/src/util/std/gobHashtbl.ml index c14bafc0cb..c93244eb47 100644 --- a/src/util/std/gobHashtbl.ml +++ b/src/util/std/gobHashtbl.ml @@ -1,9 +1,5 @@ module Pretty = GoblintCil.Pretty -let magic_stats h = - let h: _ Hashtbl.t = Obj.magic h in (* Batteries Hashtables don't expose stats yet...: https://github.com/ocaml-batteries-team/batteries-included/pull/1079 *) - Hashtbl.stats h - let pretty_statistics () (s: Hashtbl.statistics) = let load_factor = float_of_int s.num_bindings /. float_of_int s.num_buckets in Pretty.dprintf "bindings=%d buckets=%d max_length=%d histo=%a load=%f" s.num_bindings s.num_buckets s.max_bucket_length (Pretty.docList (Pretty.dprintf "%d")) (Array.to_list s.bucket_histogram) load_factor From aa7a8bb4dfcf0740a5b0d2b49e84032104eea591 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 1 Dec 2023 21:42:15 +0100 Subject: [PATCH 1195/1312] Require batteries >=3.5.1 --- dune-project | 2 +- goblint.opam | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dune-project b/dune-project index 81c8d2f091..37e81f4199 100644 --- a/dune-project +++ b/dune-project @@ -25,7 +25,7 @@ (depends (ocaml (>= 4.10)) (goblint-cil (>= 2.0.3)) ; TODO no way to define as pin-depends? Used goblint.opam.template to add it for now. https://github.com/ocaml/dune/issues/3231. Alternatively, removing this line and adding cil as a git submodule and `(vendored_dirs cil)` as ./dune also works. This way, no more need to reinstall the pinned cil opam package on changes. However, then cil is cleaned and has to be rebuild together with goblint. - (batteries (>= 3.5.0)) + (batteries (>= 3.5.1)) (zarith (>= 1.8)) (yojson (>= 2.0.0)) (qcheck-core (>= 0.19)) diff --git a/goblint.opam b/goblint.opam index 669b2d9c40..b5f1f360dc 100644 --- a/goblint.opam +++ b/goblint.opam @@ -22,7 +22,7 @@ depends: [ "dune" {>= "3.7"} "ocaml" {>= "4.10"} "goblint-cil" {>= "2.0.3"} - "batteries" {>= "3.5.0"} + "batteries" {>= "3.5.1"} "zarith" {>= "1.8"} "yojson" {>= "2.0.0"} "qcheck-core" {>= "0.19"} From 9891391807d9bf9a4ac6e5efe1f49b843ace9dbf Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 4 Dec 2023 11:17:42 +0100 Subject: [PATCH 1196/1312] Rm further spurious domains --- src/cdomains/fileDomain.ml | 81 --------- src/cdomains/mvalMapDomain.ml | 299 ---------------------------------- src/cdomains/specDomain.ml | 34 ---- src/goblint_lib.ml | 4 - 4 files changed, 418 deletions(-) delete mode 100644 src/cdomains/fileDomain.ml delete mode 100644 src/cdomains/mvalMapDomain.ml delete mode 100644 src/cdomains/specDomain.ml diff --git a/src/cdomains/fileDomain.ml b/src/cdomains/fileDomain.ml deleted file mode 100644 index ca585b8bce..0000000000 --- a/src/cdomains/fileDomain.ml +++ /dev/null @@ -1,81 +0,0 @@ -(** Domains for file handles. *) - -open Batteries - -module D = MvalMapDomain - - -module Val = -struct - type mode = Read | Write [@@deriving eq, ord, hash] - type s = Open of string*mode | Closed | Error [@@deriving eq, ord, hash] - let name = "File handles" - let var_state = Closed - let string_of_mode = function Read -> "Read" | Write -> "Write" - let string_of_state = function - | Open(filename, m) -> "open("^filename^", "^string_of_mode m^")" - | Closed -> "closed" - | Error -> "error" - - (* properties of records (e.g. used by Dom.warn_each) *) - let opened s = s <> Closed && s <> Error - let closed s = s = Closed - let writable s = match s with Open((_,Write)) -> true | _ -> false -end - - -module Dom = -struct - include D.Domain (D.Value (Val)) - - (* returns a tuple (thunk, result) *) - let report_ ?(neg=false) k p msg m = - let f ?(may=false) msg = - let f () = warn ~may msg in - f, if may then `May true else `Must true in - let mf = (fun () -> ()), `Must false in - if mem k m then - let p = if neg then not % p else p in - let v = find' k m in - if V.must p v then f msg (* must *) - else if V.may p v then f ~may:true msg (* may *) - else mf (* none *) - else if neg then f msg else mf - - let report ?(neg=false) k p msg m = (fst (report_ ~neg k p msg m)) () (* evaluate thunk *) - - let reports k xs m = - let uncurry (neg, p, msg) = report_ ~neg:neg k p msg m in - let f result x = if snd (uncurry x) = result then Some (fst (uncurry x)) else None in - let must_true = BatList.filter_map (f (`Must true)) xs in - let may_true = BatList.filter_map (f (`May true)) xs in - (* output first must and first may *) - if must_true <> [] then (List.hd must_true) (); - if may_true <> [] then (List.hd may_true) () - - (* handling state *) - let opened r = V.state r |> Val.opened - let closed r = V.state r |> Val.closed - let writable r = V.state r |> Val.writable - - let fopen k loc filename mode m = - if is_unknown k m then m else - let mode = match String.lowercase_ascii mode with "r" -> Val.Read | _ -> Val.Write in - let v = V.make k loc (Val.Open(filename, mode)) in - add' k v m - let fclose k loc m = - if is_unknown k m then m else - let v = V.make k loc Val.Closed in - change k v m - let error k m = - if is_unknown k m then m else - let loc = if mem k m then find' k m |> V.split |> snd |> Set.choose |> V.loc else [] in - let v = V.make k loc Val.Error in - change k v m - let success k m = - if is_unknown k m then m else - match find_option k m with - | Some v when V.may (Val.opened%V.state) v && V.may (V.in_state Val.Error) v -> - change k (V.filter (Val.opened%V.state) v) m (* TODO what about must-set? *) - | _ -> m -end diff --git a/src/cdomains/mvalMapDomain.ml b/src/cdomains/mvalMapDomain.ml deleted file mode 100644 index d0d2f8da85..0000000000 --- a/src/cdomains/mvalMapDomain.ml +++ /dev/null @@ -1,299 +0,0 @@ -(** Domains for {{!Mval} mvalue} maps. *) - -open Batteries -open GoblintCil - -module M = Messages - - -exception Unknown -exception Error - -(* signature for map entries *) -module type S = -sig - include Lattice.S - type k = Mval.Exp.t (* key *) - type s (* state is defined by Impl *) - type r (* record *) - - (* printing *) - val string_of: t -> string - val string_of_key: k -> string - val string_of_record: r -> string - - (* constructing *) - val make: k -> Node.t list -> s -> t - - (* manipulation *) - val map: (r -> r) -> t -> t - val filter: (r -> bool) -> t -> t - val union: t -> t -> t - val set_key: k -> t -> t - val set_state: s -> t -> t - val remove_state: s -> t -> t - - (* deconstructing *) - val split: t -> r Set.t * r Set.t - val map': (r -> 'a) -> t -> 'a Set.t * 'a Set.t - val filter': (r -> bool) -> t -> r Set.t * r Set.t - val length: t -> int * int - - (* predicates *) - val must: (r -> bool) -> t -> bool - val may: (r -> bool) -> t -> bool - (* properties of records *) - val key: r -> k - val loc: r -> Node.t list - val edit_loc: (Node.t list -> Node.t list) -> r -> r - val state: r -> s - val in_state: s -> r -> bool - - (* special variables *) - val get_record: t -> r option - (* val make_record: k -> location list -> s -> r *) - val make_var: k -> t - val from_tuple: r Set.t * r Set.t -> t - - (* aliasing *) - val is_alias: t -> bool - val get_alias: t -> k - val make_alias: k -> t -end - -module Value (Impl: sig - type s (* state *) [@@deriving eq, ord, hash] - val name: string - val var_state: s - val string_of_state: s -> string - end) : S with type s = Impl.s = -struct - type k = Mval.Exp.t [@@deriving eq, ord, hash] - type s = Impl.s [@@deriving eq, ord, hash] - module R = struct - include Printable.StdLeaf - type t = { key: k; loc: Node.t list; state: s } [@@deriving eq, ord, hash] - let name () = "MValMapDomainValue" - - let pretty () {key; loc; state} = - Pretty.dprintf "{key=%a; loc=%a; state=%s}" Mval.Exp.pretty key (Pretty.d_list ", " Node.pretty) loc (Impl.string_of_state state) - - include Printable.SimplePretty ( - struct - type nonrec t = t - let pretty = pretty - end - ) - end - type r = R.t - open R - (* TODO: use SetDomain.Reverse? *) - module Must' = SetDomain.ToppedSet (R) (struct let topname = "top" end) - module Must = Lattice.Reverse (Must') - module May = SetDomain.ToppedSet (R) (struct let topname = "top" end) - include Lattice.Prod (Must) (May) - let name () = Impl.name - - (* converts to polymorphic sets *) - let split (x,y) = try Must'.elements x |> Set.of_list, May.elements y |> Set.of_list with SetDomain.Unsupported _ -> Set.empty, Set.empty - - (* special variable used for indirection *) - let alias_var = Cilfacade.create_var @@ Cil.makeVarinfo false "@alias" Cil.voidType, `NoOffset - (* alias structure: x[0].key=alias_var, y[0].key=linked_var *) - let is_alias (x,y) = neg Must'.is_empty x && (Must'.choose x).key=alias_var - let get_alias (x,y) = (May.choose y).key - - (* Printing *) - let string_of_key k = Mval.Exp.show k - let string_of_loc xs = String.concat ", " (List.map (CilType.Location.show % Node.location) xs) - let string_of_record r = Impl.string_of_state r.state^" ("^string_of_loc r.loc^")" - let string_of (x,y) = - if is_alias (x,y) then - "alias for "^string_of_key @@ get_alias (x,y) - else - let x, y = split (x,y) in - let z = Set.diff y x in - "{ "^String.concat ", " (List.map string_of_record (Set.elements x))^" }, "^ - "{ "^String.concat ", " (List.map string_of_record (Set.elements z))^" }" - let show x = string_of x - include Printable.SimpleShow (struct - type nonrec t = t - let show = show - end) - (* constructing & manipulation *) - let make_record k l s = { key=k; loc=l; state=s } - let make k l s = let v = make_record k l s in Must'.singleton v, May.singleton v - let map f (x,y) = Must'.map f x, May.map f y - let filter p (x,y) = Must'.filter p x, May.filter p y (* retains top *) - let union (a,b) (c,d) = Must'.union a c, May.union b d - let set_key k v = map (fun x -> {x with key=k}) v (* changes key for all elements *) - let set_state s v = map (fun x -> {x with state=s}) v - let remove_state s v = filter (fun x -> x.state<>s) v - - (* deconstructing *) - let length = split %> Tuple2.mapn Set.cardinal - let map' f = split %> Tuple2.mapn (Set.map f) - let filter' f = split %> Tuple2.mapn (Set.filter f) - - (* predicates *) - let must p (x,y) = Must'.exists p x || May.for_all p y - let may p (x,y) = May.exists p y - - (* properties of records *) - let key r = r.key - let loc r = r.loc - let edit_loc f r = {r with loc=f r.loc} - let state r = r.state - let in_state s r = r.state = s - - (* special variables *) - let get_record (x,y) = if Must'.is_empty x then None else Some (Must'.choose x) - let make_var_record k = make_record k [] Impl.var_state - let make_var k = Must'.singleton (make_var_record k), May.singleton (make_var_record k) - let make_alias k = Must'.singleton (make_var_record alias_var), May.singleton (make_var_record k) - let from_tuple (x,y) = Set.to_list x |> Must'.of_list, Set.to_list y |> May.of_list -end - - -module Domain (V: S) = -struct - module K = Mval.Exp - module V = V - module MD = MapDomain.MapBot (Mval.Exp) (V) - include MD - - (* Map functions *) - (* find that resolves aliases *) - let find' k m = let v = find k m in if V.is_alias v then find (V.get_alias v) m else v - let find_option k m = if mem k m then Some(find' k m) else None - let get_alias k m = (* target: returns Some k' if k links to k' *) - if mem k m && V.is_alias (find k m) then Some (V.get_alias (find k m)) else None - let get_aliased k m = (* sources: get list of keys that link to k *) - (* iter (fun k' (x,y) -> if V.is_alias (x,y) then print_endline ("alias "^V.string_of_key k'^" -> "^V.string_of_key (Set.choose y).key)) m; *) - (* TODO V.get_alias v=k somehow leads to Out_of_memory... *) - filter (fun k' v -> V.is_alias v && V.string_of_key (V.get_alias v)=V.string_of_key k) m |> bindings |> List.map fst - let get_aliases k m = (* get list of all other keys that have the same pointee *) - match get_alias k m with - | Some k' -> [k] (* k links to k' *) - | None -> get_aliased k m (* k' that link to k *) - let alias a b m = (* link a to b *) - (* if b is already an alias, follow it... *) - let b' = get_alias b m |? b in - (* add an entry for key a, that points to b' *) - add a (V.make_alias b') m - let remove' k m = (* fixes keys that link to k before removing it *) - if mem k m && not (V.is_alias (find k m)) then (* k might be aliased *) - let v = find k m in - match get_aliased k m with - | [] -> remove k m (* nothing links to k *) - | k'::xs -> let m = add k' v m in (* set k' to v, link xs to k', finally remove k *) - (* List.map (fun x -> x.vname) (k'::xs) |> String.concat ", " |> print_endline; *) - List.fold_left (fun m x -> alias x k' m) m xs |> remove k - else remove k m (* k not in m or an alias *) - let add' k v m = - remove' k m (* fixes keys that might have linked to k *) - |> add k v (* set new value *) - let change k v m = (* if k is an alias, replace its pointee *) - add (get_alias k m |? k) v m - - (* special variables *) - let get_record k m = Option.bind (find_option k m) V.get_record - let edit_record k f m = - let v = find_option k m |? V.make_var k in - add k (V.map f v) m - let get_value k m = find_option k m |> Option.map_default V.split (Set.empty,Set.empty) - let extend_value k v' m = - let v = V.from_tuple v' in - if mem k m then - add k (V.union (find k m) v) m - else - add k v m - let union (a,b) (c,d) = Set.union a c, Set.union b d - let is_special_var k = String.get (V.string_of_key k) 0 = '@' - let without_special_vars m = filter (fun k v -> not @@ is_special_var k) m - - (* functions needed for enter & combine *) - (* only keep globals, aliases to them and special variables *) - let only_globals m = filter (fun k v -> (fst k).vglob || V.is_alias v && (fst (V.get_alias v)).vglob || is_special_var k) m - (* adds all the bindings from m2 to m1 (overwrites!) *) - let add_all m1 m2 = add_list (bindings m2) m1 - - (* callstack for locations *) - let callstack_var = Cilfacade.create_var @@ Cil.makeVarinfo false "@callstack" Cil.voidType, `NoOffset - let callstack m = get_record callstack_var m |> Option.map_default V.loc [] - let string_of_callstack m = " [call stack: "^String.concat ", " (List.map (CilType.Location.show % Node.location) (callstack m))^"]" - let edit_callstack f m = edit_record callstack_var (V.edit_loc f) m - - - (* predicates *) - let must k p m = mem k m && V.must p (find' k m) - let may k p m = mem k m && V.may p (find' k m) - let is_may k m = mem k m && let x,y = V.length (find' k m) in x=0 && y>0 - - let filter_values p m = (* filters all values in the map and flattens result *) - let flatten_sets = List.fold_left Set.union Set.empty in - without_special_vars m - |> filter (fun k v -> V.may p v && not (V.is_alias v)) - |> bindings |> List.map (fun (k,v) -> V.filter' p v) - |> List.split |> (fun (x,y) -> flatten_sets x, flatten_sets y) - let filter_records k p m = (* filters both sets of k *) - if mem k m then V.filter' p (find' k m) else Set.empty, Set.empty - - let unknown k m = add' k (V.top ()) m - let is_unknown k m = if mem k m then V.is_top (find' k m) else false - - (* printing *) - let string_of_state k m = if not (mem k m) then "?" else V.string_of (find' k m) - let string_of_key k = V.string_of_key k - let string_of_keys rs = Set.map (V.string_of_key % V.key) rs |> Set.elements |> String.concat ", " - let string_of_entry k m = string_of_key k ^ ": " ^ string_of_state k m - let string_of_map m = List.map (fun (k,v) -> string_of_entry k m) (bindings m) - - let warn ?may:(may=false) ?loc:(loc=[Option.get !Node.current_node]) msg = - let split_category s = - if Str.string_partial_match (Str.regexp {|\[\([^]]*\)\]|}) s 0 then - (Some (Str.matched_group 1 s), Str.string_after s (Str.match_end ())) - else - (None, s) - in - let rec split_categories s = - match split_category s with - | (Some category, s') -> - let (categories, s'') = split_categories s' in - (category :: categories, s'') - | (None, s') -> ([], s') - in - match split_categories msg with - | ([], msg) -> (if may then Messages.warn else Messages.error) ~loc:(Node (List.last loc)) "%s" msg - | (category :: categories, msg) -> - let category_of_string s = Messages.Category.from_string_list [String.lowercase_ascii s] in (* TODO: doesn't split subcategories, not used and no defined syntax even *) - let category = category_of_string category in - let tags = List.map (fun category -> Messages.Tag.Category (category_of_string category)) categories in - (if may then Messages.warn else Messages.error) ~loc:(Node (List.last loc)) ~category ~tags "%s" msg - - (* getting keys from Cil Lvals *) - - let key_from_lval lval = match lval with (* TODO try to get a Mval.Exp from Cil.Lval *) - | Var v1, o1 -> v1, Offset.Exp.of_cil o1 - | Mem Lval(Var v1, o1), o2 -> v1, Offset.Exp.of_cil (addOffset o1 o2) - (* | Mem exp, o1 -> failwith "not implemented yet" (* TODO use query_lv *) *) - | _ -> Cilfacade.create_var @@ Cil.makeVarinfo false ("?"^CilType.Lval.show lval) Cil.voidType, `NoOffset (* TODO *) - - let keys_from_lval lval (ask: Queries.ask) = (* use MayPointTo query to get all possible pointees of &lval *) - (* print_query_lv ctx.ask (AddrOf lval); *) - let query_addrs (ask: Queries.ask) exp = match ask.f (Queries.MayPointTo exp) with - | ad when not (Queries.AD.is_top ad) -> Queries.AD.elements ad - | _ -> [] - in - let exp = AddrOf lval in - let addrs = query_addrs ask exp in (* MayPointTo -> LValSet *) - let keys = List.fold (fun vs addr -> - match addr with - | Queries.AD.Addr.Addr (v,o) -> (v, ValueDomain.Offs.to_exp o) :: vs - | _ -> vs - ) [] addrs - in - let pretty_key k = Pretty.text (string_of_key k) in - Messages.debug ~category:Analyzer "MayPointTo %a = [%a]" d_exp exp (Pretty.docList ~sep:(Pretty.text ", ") pretty_key) keys; - keys -end diff --git a/src/cdomains/specDomain.ml b/src/cdomains/specDomain.ml deleted file mode 100644 index 75a9d8edc5..0000000000 --- a/src/cdomains/specDomain.ml +++ /dev/null @@ -1,34 +0,0 @@ -(** Domains for finite automaton specification file analysis. *) - -open Batteries - -module D = MvalMapDomain - - -module Val = -struct - type s = string [@@deriving eq, ord, hash] - let name = "Spec value" - let var_state = "" - let string_of_state s = s - - (* transforms May-Sets of length 1 to Must. NOTE: this should only be done if the original set had more than one element! *) - (* let maybe_must = function May xs when Set.cardinal xs = 1 -> Must (Set.choose xs) | x -> x *) - (* let may = function Must x -> May (Set.singleton x) | xs -> xs *) - (* let records = function Must x -> (Set.singleton x) | May xs -> xs *) - (* let list_of_records = function Must x -> [x] | May xs -> List.of_enum (Set.enum xs) *) - (* let vnames x = String.concat ", " (List.map (fun r -> string_of_key r.var) (list_of_records x)) *) -end - - -module Dom = -struct - include D.Domain (D.Value (Val)) - - (* handling state *) - let goto k loc state m = add' k (V.make k loc state) m - let may_goto k loc state m = let v = V.join (find' k m) (V.make k loc state) in add' k v m - let in_state k s m = must k (V.in_state s) m - let may_in_state k s m = may k (V.in_state s) m - let get_states k m = if not (mem k m) then [] else find' k m |> V.map' V.state |> snd |> Set.elements -end diff --git a/src/goblint_lib.ml b/src/goblint_lib.ml index d4f2982902..8d319dd4a1 100644 --- a/src/goblint_lib.ml +++ b/src/goblint_lib.ml @@ -261,12 +261,8 @@ module AccessDomain = AccessDomain module MusteqDomain = MusteqDomain module RegionDomain = RegionDomain -module FileDomain = FileDomain module StackDomain = StackDomain -module MvalMapDomain = MvalMapDomain -module SpecDomain = SpecDomain - (** {2 Testing} Modules related to (property-based) testing of domains. *) From d7d350325cc3f330a6f280c22a0d721d33bc615b Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 4 Dec 2023 17:17:00 +0100 Subject: [PATCH 1197/1312] Localize two helpers in `relationDomain.apron.ml` --- src/cdomains/apron/relationDomain.apron.ml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/cdomains/apron/relationDomain.apron.ml b/src/cdomains/apron/relationDomain.apron.ml index c5b6a0a89b..e613cad6c3 100644 --- a/src/cdomains/apron/relationDomain.apron.ml +++ b/src/cdomains/apron/relationDomain.apron.ml @@ -184,10 +184,9 @@ struct let name () = RD.name () ^ " * " ^ PrivD.name () - let of_tuple(rel, priv):t = {rel; priv} - let to_tuple r = (r.rel, r.priv) - let arbitrary () = + let to_tuple r = (r.rel, r.priv) in + let of_tuple (rel, priv) = {rel; priv} in let tr = QCheck.pair (RD.arbitrary ()) (PrivD.arbitrary ()) in QCheck.map ~rev:to_tuple of_tuple tr From 1473d6edb0437b716ebb4c5795d03e963da24439 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 4 Dec 2023 17:31:50 +0100 Subject: [PATCH 1198/1312] Add citation to TODO --- src/analyses/apron/relationAnalysis.apron.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index d2fe7eab9e..f5dc227ad2 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -283,6 +283,7 @@ struct let pass_to_callee fundec any_local_reachable var = (* TODO: currently, we pass all locals of the caller to the callee, provided one of them is reachbale to preserve relationality *) (* there should be smarter ways to do this, e.g. by keeping track of which values are written etc. ... *) + (* See, e.g, Beckschulze E, Kowalewski S, Brauer J (2012) Access-based localization for octagons. Electron Notes Theor Comput Sci 287:29–40 *) (* Also, a local *) let vname = RD.Var.to_string var in let locals = fundec.sformals @ fundec.slocals in From d9831131890923838186f9d0d4fd19fdda7e022c Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 4 Dec 2023 17:37:05 +0100 Subject: [PATCH 1199/1312] `make_callee_rel`: Introduce `filter_map` --- src/analyses/apron/relationAnalysis.apron.ml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index f5dc227ad2..b3c6dcb9b3 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -296,8 +296,7 @@ struct let st = ctx.local in let arg_assigns = GobList.combine_short f.sformals args (* TODO: is it right to ignore missing formals/args? *) - |> List.filter (fun (x, _) -> RD.Tracked.varinfo_tracked x) - |> List.map (Tuple2.map1 RV.arg) + |> List.filter_map (fun (x, e) -> if RD.Tracked.varinfo_tracked x then Some (RV.arg x, e) else None) in let arg_vars = List.map fst arg_assigns in let new_rel = RD.add_vars st.rel arg_vars in From 4940cebb9bf9f26c4c1d0044d0b5c59f039513d6 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 4 Dec 2023 18:49:32 +0100 Subject: [PATCH 1200/1312] Simplify --- .../apron/affineEqualityAnalysis.apron.ml | 1 - src/analyses/apron/apronAnalysis.apron.ml | 3 +- src/analyses/apron/relationAnalysis.apron.ml | 6 +-- src/analyses/apron/relationPriv.apron.ml | 3 +- .../apron/affineEqualityDomain.apron.ml | 5 +-- src/cdomains/apron/apronDomain.apron.ml | 11 +++--- src/cdomains/apron/gobApron.apron.ml | 37 +++++++++++++++++++ src/cdomains/apron/gobApron.no-apron.ml | 0 src/cdomains/apron/relationDomain.apron.ml | 28 +++++--------- src/cdomains/apron/sharedFunctions.apron.ml | 36 ------------------ src/dune | 4 ++ 11 files changed, 62 insertions(+), 72 deletions(-) create mode 100644 src/cdomains/apron/gobApron.apron.ml create mode 100644 src/cdomains/apron/gobApron.no-apron.ml diff --git a/src/analyses/apron/affineEqualityAnalysis.apron.ml b/src/analyses/apron/affineEqualityAnalysis.apron.ml index 03a9ecdb57..ce859d87b7 100644 --- a/src/analyses/apron/affineEqualityAnalysis.apron.ml +++ b/src/analyses/apron/affineEqualityAnalysis.apron.ml @@ -11,7 +11,6 @@ let spec_module: (module MCPSpec) Lazy.t = let module AD = AffineEqualityDomain.D2 (VectorMatrix.ArrayVector) (VectorMatrix.ArrayMatrix) in let module RD: RelationDomain.RD = struct - module Var = AffineEqualityDomain.Var module V = AffineEqualityDomain.V include AD end diff --git a/src/analyses/apron/apronAnalysis.apron.ml b/src/analyses/apron/apronAnalysis.apron.ml index 29e295a662..72dc81c121 100644 --- a/src/analyses/apron/apronAnalysis.apron.ml +++ b/src/analyses/apron/apronAnalysis.apron.ml @@ -12,10 +12,9 @@ let spec_module: (module MCPSpec) Lazy.t = let module AD = (val if diff_box then (module ApronDomain.BoxProd (AD): ApronDomain.S3) else (module AD)) in let module RD: RelationDomain.RD = struct - module Var = ApronDomain.Var module V = ApronDomain.V include AD - type var = ApronDomain.Var.t + type var = GobApron.Var.t end in let module Priv = (val RelationPriv.get_priv ()) in diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index b3c6dcb9b3..b401b58e93 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -285,7 +285,7 @@ struct (* there should be smarter ways to do this, e.g. by keeping track of which values are written etc. ... *) (* See, e.g, Beckschulze E, Kowalewski S, Brauer J (2012) Access-based localization for octagons. Electron Notes Theor Comput Sci 287:29–40 *) (* Also, a local *) - let vname = RD.Var.to_string var in + let vname = GobApron.Var.to_string var in let locals = fundec.sformals @ fundec.slocals in match List.find_opt (fun v -> VM.var_name (Local v) = vname) locals with (* TODO: optimize *) | None -> true @@ -318,7 +318,7 @@ struct RD.remove_filter_with new_rel (fun var -> match RV.find_metadata var with | Some (Local _) when not (pass_to_callee fundec any_local_reachable var) -> true (* remove caller locals provided they are unreachable *) - | Some (Arg _) when not (List.mem_cmp RD.Var.compare var arg_vars) -> true (* remove caller args, but keep just added args *) + | Some (Arg _) when not (List.mem_cmp GobApron.Var.compare var arg_vars) -> true (* remove caller args, but keep just added args *) | _ -> false (* keep everything else (just added args, globals, global privs) *) ); if M.tracing then M.tracel "combine" "relation enter newd: %a\n" RD.pretty new_rel; @@ -404,7 +404,7 @@ struct in let any_local_reachable = any_local_reachable fundec reachable_from_args in let arg_vars = f.sformals |> List.filter (RD.Tracked.varinfo_tracked) |> List.map RV.arg in - if M.tracing then M.tracel "combine" "relation remove vars: %a\n" (docList (fun v -> Pretty.text (RD.Var.to_string v))) arg_vars; + if M.tracing then M.tracel "combine" "relation remove vars: %a\n" (docList (fun v -> Pretty.text (GobApron.Var.to_string v))) arg_vars; RD.remove_vars_with new_fun_rel arg_vars; (* fine to remove arg vars that also exist in caller because unify from new_rel adds them back with proper constraints *) let tainted = f_ask.f Queries.MayBeTainted in let tainted_vars = TaintPartialContexts.conv_varset tainted in diff --git a/src/analyses/apron/relationPriv.apron.ml b/src/analyses/apron/relationPriv.apron.ml index b386af162b..a51fc3545f 100644 --- a/src/analyses/apron/relationPriv.apron.ml +++ b/src/analyses/apron/relationPriv.apron.ml @@ -195,8 +195,7 @@ struct end module AV = struct - include RelationDomain.VarMetadataTbl (VM) (RD.Var) - + include RelationDomain.VarMetadataTbl (VM) let local g = make_var (Local g) let unprot g = make_var (Unprot g) diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index a6f00fdba0..0054f685b1 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -10,7 +10,7 @@ open Batteries open GoblintCil open Pretty module M = Messages -open Apron +open GobApron open VectorMatrix module Mpqf = struct @@ -26,8 +26,7 @@ module Mpqf = struct let hash x = 31 * (Z.hash (get_den x)) + Z.hash (get_num x) end -module Var = SharedFunctions.Var -module V = RelationDomain.V(Var) +module V = RelationDomain.V (** It defines the type t of the affine equality domain (a struct that contains an optional matrix and an apron environment) and provides the functions needed for handling variables (which are defined by RelationDomain.D2) such as add_vars remove_vars. Furthermore, it provides the function get_coeff_vec that parses an apron expression into a vector of coefficients if the apron expression has an affine form. *) diff --git a/src/cdomains/apron/apronDomain.apron.ml b/src/cdomains/apron/apronDomain.apron.ml index 7dffafe967..ef9eac9bef 100644 --- a/src/cdomains/apron/apronDomain.apron.ml +++ b/src/cdomains/apron/apronDomain.apron.ml @@ -4,7 +4,7 @@ open Batteries open GoblintCil open Pretty (* A binding to a selection of Apron-Domains *) -open Apron +open GobApron open RelationDomain open SharedFunctions @@ -29,8 +29,7 @@ let widening_thresholds_apron = ResettableLazy.from_fun (fun () -> let reset_lazy () = ResettableLazy.reset widening_thresholds_apron -module Var = SharedFunctions.Var -module V = RelationDomain.V(Var) +module V = RelationDomain.V module type Manager = @@ -497,9 +496,9 @@ struct let to_yojson (x: t) = let constraints = A.to_lincons_array Man.mgr x - |> SharedFunctions.Lincons1Set.of_earray - |> SharedFunctions.Lincons1Set.elements - |> List.map (fun lincons1 -> `String (SharedFunctions.Lincons1.show lincons1)) + |> Lincons1Set.of_earray + |> Lincons1Set.elements + |> List.map (fun lincons1 -> `String (Lincons1.show lincons1)) in let env = `String (Format.asprintf "%a" (Environment.print: Format.formatter -> Environment.t -> unit) (A.env x)) in diff --git a/src/cdomains/apron/gobApron.apron.ml b/src/cdomains/apron/gobApron.apron.ml new file mode 100644 index 0000000000..df20f3c59d --- /dev/null +++ b/src/cdomains/apron/gobApron.apron.ml @@ -0,0 +1,37 @@ +open Batteries +include Apron + +module Var = +struct + include Var + let equal x y = Var.compare x y = 0 +end + +module Lincons1 = +struct + include Lincons1 + + let show = Format.asprintf "%a" print + let compare x y = String.compare (show x) (show y) (* HACK *) + + let num_vars x = + (* Apron.Linexpr0.get_size returns some internal nonsense, so we count ourselves. *) + let size = ref 0 in + Lincons1.iter (fun coeff var -> + if not (Apron.Coeff.is_zero coeff) then + incr size + ) x; + !size +end + +module Lincons1Set = +struct + include Set.Make (Lincons1) + + let of_earray ({lincons0_array; array_env}: Lincons1.earray): t = + Array.enum lincons0_array + |> Enum.map (fun (lincons0: Lincons0.t) -> + Lincons1.{lincons0; env = array_env} + ) + |> of_enum +end diff --git a/src/cdomains/apron/gobApron.no-apron.ml b/src/cdomains/apron/gobApron.no-apron.ml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/cdomains/apron/relationDomain.apron.ml b/src/cdomains/apron/relationDomain.apron.ml index e613cad6c3..e68540c41b 100644 --- a/src/cdomains/apron/relationDomain.apron.ml +++ b/src/cdomains/apron/relationDomain.apron.ml @@ -5,25 +5,15 @@ open Batteries open GoblintCil -(** Abstracts the extended apron Var. *) -module type Var = -sig - type t - val compare : t -> t -> int - val of_string : string -> t - val to_string : t -> string - val hash : t -> int - val equal : t -> t -> bool -end - module type VarMetadata = sig type t val var_name: t -> string end -module VarMetadataTbl (VM: VarMetadata) (Var: Var) = +module VarMetadataTbl (VM: VarMetadata) = struct + open GobApron module VH = Hashtbl.Make (Var) let vh = VH.create 113 @@ -57,7 +47,7 @@ end module type RV = sig - type t + type t = GobApron.Var.t type vartable val vh: vartable @@ -70,10 +60,11 @@ sig val to_cil_varinfo: t -> varinfo Option.t end -module V (Var: Var): (RV with type t = Var.t and type vartable = VM.t VarMetadataTbl (VM) (Var).VH.t) = +module V: (RV with type vartable = VM.t VarMetadataTbl (VM).VH.t) = struct + open GobApron type t = Var.t - module VMT = VarMetadataTbl (VM) (Var) + module VMT = VarMetadataTbl (VM) include VMT open VM @@ -105,7 +96,7 @@ end module type S2 = sig type t - type var + type var = GobApron.Var.t type marshal module Tracked: Tracked @@ -215,7 +206,6 @@ end module type RD = sig - module Var : Var - module V : module type of struct include V(Var) end - include S3 with type var = Var.t + module V : module type of struct include V end + include S3 end diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 059a7f8264..9c229e2d64 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -8,42 +8,6 @@ module M = Messages module BI = IntOps.BigIntOps -module Var = -struct - include Var - - let equal x y = Var.compare x y = 0 -end - -module Lincons1 = -struct - include Lincons1 - - let show = Format.asprintf "%a" print - let compare x y = String.compare (show x) (show y) (* HACK *) - - let num_vars x = - (* Apron.Linexpr0.get_size returns some internal nonsense, so we count ourselves. *) - let size = ref 0 in - Lincons1.iter (fun coeff var -> - if not (Apron.Coeff.is_zero coeff) then - incr size - ) x; - !size -end - -module Lincons1Set = -struct - include Set.Make (Lincons1) - - let of_earray ({lincons0_array; array_env}: Lincons1.earray): t = - Array.enum lincons0_array - |> Enum.map (fun (lincons0: Lincons0.t) -> - Lincons1.{lincons0; env = array_env} - ) - |> of_enum -end - let int_of_scalar ?round (scalar: Scalar.t) = if Scalar.is_infty scalar <> 0 then (* infinity means unbounded *) None diff --git a/src/dune b/src/dune index acd5348acb..40faae1f3f 100644 --- a/src/dune +++ b/src/dune @@ -11,6 +11,10 @@ ; Conditionally compile based on whether apron optional dependency is installed or not. ; Alternative dependencies seem like the only way to optionally depend on optional dependencies. ; See: https://dune.readthedocs.io/en/stable/concepts.html#alternative-dependencies. + (select gobApron.ml from + (apron -> gobApron.apron.ml) + (-> gobApron.no-apron.ml) + ) (select apronDomain.ml from (apron apron.octD apron.boxD apron.polkaMPQ zarith_mlgmpidl -> apronDomain.apron.ml) (-> apronDomain.no-apron.ml) From 49eb46df9bf722d0f528f52dbfcb2c8a524ede19 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 4 Dec 2023 19:08:36 +0100 Subject: [PATCH 1201/1312] Cleanup --- src/cdomains/apron/relationDomain.apron.ml | 59 ++++++++++------------ 1 file changed, 26 insertions(+), 33 deletions(-) diff --git a/src/cdomains/apron/relationDomain.apron.ml b/src/cdomains/apron/relationDomain.apron.ml index e68540c41b..aca2346820 100644 --- a/src/cdomains/apron/relationDomain.apron.ml +++ b/src/cdomains/apron/relationDomain.apron.ml @@ -1,7 +1,7 @@ (** Signatures for relational value domains. See {!ApronDomain} and {!AffineEqualityDomain}. *) - +open GobApron open Batteries open GoblintCil @@ -11,23 +11,6 @@ sig val var_name: t -> string end -module VarMetadataTbl (VM: VarMetadata) = -struct - open GobApron - module VH = Hashtbl.Make (Var) - - let vh = VH.create 113 - - let make_var ?name metadata = - let name = Option.default_delayed (fun () -> VM.var_name metadata) name in - let var = Var.of_string name in - VH.replace vh var metadata; - var - - let find_metadata (var: Var.t) = - VH.find_option vh var -end - module VM = struct type t = @@ -45,10 +28,26 @@ struct | Global g -> g.vname end +module VarMetadataTbl (VM: VarMetadata) = +struct + module VH = Hashtbl.Make (Var) + + let vh = VH.create 113 + + let make_var ?name metadata = + let name = Option.default_delayed (fun () -> VM.var_name metadata) name in + let var = Var.of_string name in + VH.replace vh var metadata; + var + + let find_metadata (var: Var.t) = + VH.find_option vh var +end + module type RV = sig - type t = GobApron.Var.t - type vartable + type t = Var.t + type vartable = VM.t VarMetadataTbl (VM).VH.t val vh: vartable val make_var: ?name:string -> VM.t -> t @@ -60,13 +59,13 @@ sig val to_cil_varinfo: t -> varinfo Option.t end -module V: (RV with type vartable = VM.t VarMetadataTbl (VM).VH.t) = +module V: RV = struct - open GobApron + open VM + type t = Var.t module VMT = VarMetadataTbl (VM) include VMT - open VM type vartable = VM.t VMT.VH.t @@ -81,12 +80,6 @@ struct | _ -> None end -module type LinCons = -sig - type t - val num_vars: t -> int -end - module type Tracked = sig val type_tracked: typ -> bool @@ -96,7 +89,7 @@ end module type S2 = sig type t - type var = GobApron.Var.t + type var = Var.t type marshal module Tracked: Tracked @@ -135,8 +128,8 @@ module type S3 = sig include S2 - val cil_exp_of_lincons1: Apron.Lincons1.t -> exp option - val invariant: t -> Apron.Lincons1.t list + val cil_exp_of_lincons1: Lincons1.t -> exp option + val invariant: t -> Lincons1.t list end type ('a, 'b) relcomponents_t = { @@ -206,6 +199,6 @@ end module type RD = sig - module V : module type of struct include V end + module V : RV include S3 end From d1b62287dc64b825b3640d5b95ce90394a85d725 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 4 Dec 2023 19:37:06 +0100 Subject: [PATCH 1202/1312] Move `Environment` things into `GobApron` --- .../apron/affineEqualityDomain.apron.ml | 14 ++--- src/cdomains/apron/apronDomain.apron.ml | 25 +++----- src/cdomains/apron/gobApron.apron.ml | 61 +++++++++++++++++++ src/cdomains/apron/sharedFunctions.apron.ml | 60 ------------------ 4 files changed, 76 insertions(+), 84 deletions(-) diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index 0054f685b1..ff2339cd6f 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -32,7 +32,6 @@ module V = RelationDomain.V Furthermore, it provides the function get_coeff_vec that parses an apron expression into a vector of coefficients if the apron expression has an affine form. *) module VarManagement (Vec: AbstractVector) (Mx: AbstractMatrix)= struct - include SharedFunctions.EnvOps module Vector = Vec (Mpqf) module Matrix = Mx(Mpqf) (Vec) @@ -77,16 +76,18 @@ struct let change_d t new_env add del = timing_wrap "dimension change" (change_d t new_env add) del + let vars x = Environment.ivars_only x.env + let add_vars t vars = let t = copy t in - let env' = add_vars t.env vars in + let env' = Environment.add_vars t.env vars in change_d t env' true false let add_vars t vars = timing_wrap "add_vars" (add_vars t) vars let drop_vars t vars del = let t = copy t in - let env' = remove_vars t.env vars in + let env' = Environment.remove_vars t.env vars in change_d t env' false del let drop_vars t vars = timing_wrap "drop_vars" (drop_vars t) vars @@ -101,7 +102,7 @@ struct t.env <- t'.env let remove_filter t f = - let env' = remove_filter t.env f in + let env' = Environment.remove_filter t.env f in change_d t env' false false let remove_filter t f = timing_wrap "remove_filter" (remove_filter t) f @@ -113,19 +114,18 @@ struct let keep_filter t f = let t = copy t in - let env' = keep_filter t.env f in + let env' = Environment.keep_filter t.env f in change_d t env' false false let keep_filter t f = timing_wrap "keep_filter" (keep_filter t) f let keep_vars t vs = let t = copy t in - let env' = keep_vars t.env vs in + let env' = Environment.keep_vars t.env vs in change_d t env' false false let keep_vars t vs = timing_wrap "keep_vars" (keep_vars t) vs - let vars t = vars t.env let mem_var t var = Environment.mem_var t.env var diff --git a/src/cdomains/apron/apronDomain.apron.ml b/src/cdomains/apron/apronDomain.apron.ml index ef9eac9bef..077aa971f2 100644 --- a/src/cdomains/apron/apronDomain.apron.ml +++ b/src/cdomains/apron/apronDomain.apron.ml @@ -208,7 +208,6 @@ module type AOpsExtra = sig type t val copy : t -> t - val vars_as_array : t -> Var.t array val vars : t -> Var.t list type marshal val unmarshal : marshal -> t @@ -247,15 +246,6 @@ struct let copy = A.copy Man.mgr - let vars_as_array d = - let ivs, fvs = Environment.vars (A.env d) in - assert (Array.length fvs = 0); (* shouldn't ever contain floats *) - ivs - - let vars d = - let ivs = vars_as_array d in - List.of_enum (Array.enum ivs) - (* marshal type: Abstract0.t and an array of var names *) type marshal = Man.mt Abstract0.t * string array @@ -265,30 +255,32 @@ struct let env = Environment.make vars [||] in {abstract0; env} + let vars x = Environment.ivars_only @@ A.env x + let marshal (x: t): marshal = - let vars = Array.map Var.to_string (vars_as_array x) in + let vars = Array.map Var.to_string (Array.of_list (Environment.ivars_only (A.env x))) in x.abstract0, vars let mem_var d v = Environment.mem_var (A.env d) v let add_vars_with nd vs = - let env' = EnvOps.add_vars (A.env nd) vs in + let env' = Environment.add_vars (A.env nd) vs in A.change_environment_with Man.mgr nd env' false let remove_vars_with nd vs = - let env' = EnvOps.remove_vars (A.env nd) vs in + let env' = Environment.remove_vars (A.env nd) vs in A.change_environment_with Man.mgr nd env' false let remove_filter_with nd f = - let env' = EnvOps.remove_filter (A.env nd) f in + let env' = Environment.remove_filter (A.env nd) f in A.change_environment_with Man.mgr nd env' false let keep_vars_with nd vs = - let env' = EnvOps.keep_vars (A.env nd) vs in + let env' = Environment.keep_vars (A.env nd) vs in A.change_environment_with Man.mgr nd env' false let keep_filter_with nd f = - let env' = EnvOps.keep_filter (A.env nd) f in + let env' = Environment.keep_filter (A.env nd) f in A.change_environment_with Man.mgr nd env' false let forget_vars_with nd vs = @@ -885,7 +877,6 @@ struct let unmarshal (b, d) = (BoxD.unmarshal b, D.unmarshal d) let mem_var (_, d) v = D.mem_var d v - let vars_as_array (_, d) = D.vars_as_array d let vars (_, d) = D.vars d let pretty_diff () ((_, d1), (_, d2)) = D.pretty_diff () (d1, d2) diff --git a/src/cdomains/apron/gobApron.apron.ml b/src/cdomains/apron/gobApron.apron.ml index df20f3c59d..c39a3e42db 100644 --- a/src/cdomains/apron/gobApron.apron.ml +++ b/src/cdomains/apron/gobApron.apron.ml @@ -35,3 +35,64 @@ struct ) |> of_enum end + +(** A few code elements for environment changes from functions as remove_vars etc. have been moved to sharedFunctions as they are needed in a similar way inside affineEqualityDomain. + A module that includes various methods used by variable handling operations such as add_vars, remove_vars etc. in apronDomain and affineEqualityDomain. *) +module Environment = +struct + include Environment + + let ivars_only env = + let ivs, fvs = Environment.vars env in + assert (Array.length fvs = 0); (* shouldn't ever contain floats *) + List.of_enum (Array.enum ivs) + + let add_vars env vs = + let vs' = + vs + |> List.enum + |> Enum.filter (fun v -> not (Environment.mem_var env v)) + |> Array.of_enum + in + Environment.add env vs' [||] + + let remove_vars env vs = + let vs' = + vs + |> List.enum + |> Enum.filter (fun v -> Environment.mem_var env v) + |> Array.of_enum + in + Environment.remove env vs' + + let remove_filter env f = + let vs' = + ivars_only env + |> List.enum + |> Enum.filter f + |> Array.of_enum + in + Environment.remove env vs' + + let keep_vars env vs = + (* Instead of iterating over all vars in env and doing a linear lookup in vs just to remove them, + make a new env with just the desired vs. *) + let vs' = + vs + |> List.enum + |> Enum.filter (fun v -> Environment.mem_var env v) + |> Array.of_enum + in + Environment.make vs' [||] + + let keep_filter env f = + (* Instead of removing undesired vars, + make a new env with just the desired vars. *) + let vs' = + ivars_only env + |> List.enum + |> Enum.filter f + |> Array.of_enum + in + Environment.make vs' [||] +end diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 9c229e2d64..e66be00ae4 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -255,66 +255,6 @@ struct include CilOfApron (V) end -(** A few code elements for environment changes from functions as remove_vars etc. have been moved to sharedFunctions as they are needed in a similar way inside affineEqualityDomain. - A module that includes various methods used by variable handling operations such as add_vars, remove_vars etc. in apronDomain and affineEqualityDomain. *) -module EnvOps = -struct - let vars env = - let ivs, fvs = Environment.vars env in - assert (Array.length fvs = 0); (* shouldn't ever contain floats *) - List.of_enum (Array.enum ivs) - - let add_vars env vs = - let vs' = - vs - |> List.enum - |> Enum.filter (fun v -> not (Environment.mem_var env v)) - |> Array.of_enum - in - Environment.add env vs' [||] - - let remove_vars env vs = - let vs' = - vs - |> List.enum - |> Enum.filter (fun v -> Environment.mem_var env v) - |> Array.of_enum - in - Environment.remove env vs' - - let remove_filter env f = - let vs' = - vars env - |> List.enum - |> Enum.filter f - |> Array.of_enum - in - Environment.remove env vs' - - let keep_vars env vs = - (* Instead of iterating over all vars in env and doing a linear lookup in vs just to remove them, - make a new env with just the desired vs. *) - let vs' = - vs - |> List.enum - |> Enum.filter (fun v -> Environment.mem_var env v) - |> Array.of_enum - in - Environment.make vs' [||] - - let keep_filter env f = - (* Instead of removing undesired vars, - make a new env with just the desired vars. *) - let vs' = - vars env - |> List.enum - |> Enum.filter f - |> Array.of_enum - in - Environment.make vs' [||] - -end - (** A more specific module type for RelationDomain.RelD2 with ConvBounds integrated and various apron elements. It is designed to be the interface for the D2 modules in affineEqualityDomain and apronDomain and serves as a functor argument for AssertionModule. *) module type AssertionRelS = From 4a848e4c809fd9e917ecc5dd5bdfaea234c06ea1 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 4 Dec 2023 19:45:47 +0100 Subject: [PATCH 1203/1312] Simplify marshal --- src/cdomains/apron/apronDomain.apron.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cdomains/apron/apronDomain.apron.ml b/src/cdomains/apron/apronDomain.apron.ml index 077aa971f2..ac9d7f0232 100644 --- a/src/cdomains/apron/apronDomain.apron.ml +++ b/src/cdomains/apron/apronDomain.apron.ml @@ -258,7 +258,7 @@ struct let vars x = Environment.ivars_only @@ A.env x let marshal (x: t): marshal = - let vars = Array.map Var.to_string (Array.of_list (Environment.ivars_only (A.env x))) in + let vars = Array.map Var.to_string (Array.of_list (vars x)) in x.abstract0, vars let mem_var d v = Environment.mem_var (A.env d) v From 13ac2001d8eed3060712ac05af74dfd3fc943b1d Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 4 Dec 2023 19:52:21 +0100 Subject: [PATCH 1204/1312] Some reuse --- src/cdomains/apron/apronDomain.apron.ml | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/src/cdomains/apron/apronDomain.apron.ml b/src/cdomains/apron/apronDomain.apron.ml index ac9d7f0232..03b9558621 100644 --- a/src/cdomains/apron/apronDomain.apron.ml +++ b/src/cdomains/apron/apronDomain.apron.ml @@ -263,25 +263,16 @@ struct let mem_var d v = Environment.mem_var (A.env d) v - let add_vars_with nd vs = - let env' = Environment.add_vars (A.env nd) vs in + let envop f nd a = + let env' = f (A.env nd) a in A.change_environment_with Man.mgr nd env' false - let remove_vars_with nd vs = - let env' = Environment.remove_vars (A.env nd) vs in - A.change_environment_with Man.mgr nd env' false - - let remove_filter_with nd f = - let env' = Environment.remove_filter (A.env nd) f in - A.change_environment_with Man.mgr nd env' false + let add_vars_with = envop Environment.add_vars + let remove_vars_with = envop Environment.remove_vars + let remove_filter_with = envop Environment.remove_filter + let keep_vars_with = envop Environment.keep_vars + let keep_filter_with = envop Environment.keep_filter - let keep_vars_with nd vs = - let env' = Environment.keep_vars (A.env nd) vs in - A.change_environment_with Man.mgr nd env' false - - let keep_filter_with nd f = - let env' = Environment.keep_filter (A.env nd) f in - A.change_environment_with Man.mgr nd env' false let forget_vars_with nd vs = (* Unlike keep_vars_with, this doesn't check mem_var, but assumes valid vars, like assigns *) From efa239491f9060aa7a89bd9197d82b8e757bd427 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 4 Dec 2023 20:16:51 +0100 Subject: [PATCH 1205/1312] Add TODO --- src/cdomains/apron/affineEqualityDomain.apron.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index ff2339cd6f..5aa1090dd4 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -461,6 +461,7 @@ struct let assign_exp (t: VarManagement(Vc)(Mx).t) var exp (no_ov: bool Lazy.t) = let t = if not @@ Environment.mem_var t.env var then add_vars t [var] else t in + (* TODO: Do we need to do a constant folding here? It happens for texpr1_of_cil_exp *) match Convert.texpr1_expr_of_cil_exp t t.env exp (Lazy.force no_ov) with | exp -> assign_texpr t var exp | exception Convert.Unsupported_CilExp _ -> From daf4c855f7524daded7d3b712b9684c17864ee8c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Dec 2023 20:23:06 +0000 Subject: [PATCH 1206/1312] Bump actions/configure-pages from 3 to 4 Bumps [actions/configure-pages](https://github.com/actions/configure-pages) from 3 to 4. - [Release notes](https://github.com/actions/configure-pages/releases) - [Commits](https://github.com/actions/configure-pages/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/configure-pages dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index e1648904c3..a34d3d1a87 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -46,7 +46,7 @@ jobs: - name: Setup Pages id: pages - uses: actions/configure-pages@v3 + uses: actions/configure-pages@v4 - name: Install dependencies run: opam install . --deps-only --locked --with-doc From ea28ee894565a9897594977ca561c6b4ae2e988a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Dec 2023 20:23:11 +0000 Subject: [PATCH 1207/1312] Bump actions/deploy-pages from 2 to 3 Bumps [actions/deploy-pages](https://github.com/actions/deploy-pages) from 2 to 3. - [Release notes](https://github.com/actions/deploy-pages/releases) - [Commits](https://github.com/actions/deploy-pages/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/deploy-pages dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index e1648904c3..60314d6f2e 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -68,4 +68,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v2 + uses: actions/deploy-pages@v3 From dff61c929f444a46dd40d327562115e433272554 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 5 Dec 2023 16:19:50 +0200 Subject: [PATCH 1208/1312] Remove two ignores of spec analysis --- .gitignore | 1 - scripts/regression2sv-benchmarks.py | 1 - 2 files changed, 2 deletions(-) diff --git a/.gitignore b/.gitignore index 75bd23d36b..faf1513653 100644 --- a/.gitignore +++ b/.gitignore @@ -29,7 +29,6 @@ linux-headers .goblint*/ goblint_temp_*/ -src/spec/graph .vagrant g2html.jar diff --git a/scripts/regression2sv-benchmarks.py b/scripts/regression2sv-benchmarks.py index 8f74a70f52..7bcc1c7ea3 100755 --- a/scripts/regression2sv-benchmarks.py +++ b/scripts/regression2sv-benchmarks.py @@ -31,7 +31,6 @@ "09-regions_34-escape_rc", # duplicate of 04/45 "09-regions_35-list2_rc-offsets-thread", # duplicate of 09/03 "10-synch_17-glob_fld_nr", # duplicate of 05/08 - "19-spec_02-mutex_rc", # duplicate of 04/01 "29-svcomp_01-race-2_3b-container_of", # duplicate sv-benchmarks "29-svcomp_01-race-2_4b-container_of", # duplicate sv-benchmarks From 02721c64ade754dc77e2032647994ba46fbb8050 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 6 Dec 2023 15:31:28 +0200 Subject: [PATCH 1209/1312] Remove unused MyCheck.Arbitrary.varinfo --- src/common/cdomains/basetype.ml | 2 -- src/common/domains/myCheck.ml | 3 --- 2 files changed, 5 deletions(-) diff --git a/src/common/cdomains/basetype.ml b/src/common/cdomains/basetype.ml index 55b5dbde07..da6c2bc100 100644 --- a/src/common/cdomains/basetype.ml +++ b/src/common/cdomains/basetype.ml @@ -20,8 +20,6 @@ struct | _ -> Local let name () = "variables" let printXml f x = BatPrintf.fprintf f "\n\n%s\n\n\n" (XmlUtil.escape (show x)) - - let arbitrary () = MyCheck.Arbitrary.varinfo end module RawStrings: Printable.S with type t = string = diff --git a/src/common/domains/myCheck.ml b/src/common/domains/myCheck.ml index 98583cd2c3..12809d5b46 100644 --- a/src/common/domains/myCheck.ml +++ b/src/common/domains/myCheck.ml @@ -56,7 +56,4 @@ struct let gens = List.map gen arbs in let shrinks = List.map shrink arbs in make ~shrink:(Shrink.sequence shrinks) (Gen.sequence gens) - - open GoblintCil - let varinfo: Cil.varinfo arbitrary = QCheck.always (Cil.makeGlobalVar "arbVar" Cil.voidPtrType) (* S TODO: how to generate this *) end From 152ed0df4f1fc525c5401311f86038a3bb618f04 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 6 Dec 2023 15:36:46 +0200 Subject: [PATCH 1210/1312] Move MyCheck to goblint.std as GobQCheck --- src/analyses/mCPRegistry.ml | 2 +- src/cdomains/intDomain.ml | 24 +++++++++---------- src/common/common.mld | 3 --- src/common/domains/printable.ml | 8 +++---- src/goblint_lib.ml | 6 ----- src/util/std/dune | 3 ++- .../myCheck.ml => util/std/gobQCheck.ml} | 0 src/util/std/goblint_std.ml | 1 + unittest/util/intOpsTest.ml | 4 ++-- 9 files changed, 22 insertions(+), 29 deletions(-) rename src/{common/domains/myCheck.ml => util/std/gobQCheck.ml} (100%) diff --git a/src/analyses/mCPRegistry.ml b/src/analyses/mCPRegistry.ml index 810da827ff..5d0174d44c 100644 --- a/src/analyses/mCPRegistry.ml +++ b/src/analyses/mCPRegistry.ml @@ -215,7 +215,7 @@ struct let arbitrary () = let arbs = map (fun (n, (module D: Printable.S)) -> QCheck.map ~rev:(fun (_, o) -> obj o) (fun x -> (n, repr x)) @@ D.arbitrary ()) @@ domain_list () in - MyCheck.Arbitrary.sequence arbs + GobQCheck.Arbitrary.sequence arbs let relift = unop_map (fun (module S: Printable.S) x -> Obj.repr (S.relift (Obj.obj x))) end diff --git a/src/cdomains/intDomain.ml b/src/cdomains/intDomain.ml index 054030017f..5d5174744f 100644 --- a/src/cdomains/intDomain.ml +++ b/src/cdomains/intDomain.ml @@ -996,12 +996,12 @@ struct let arbitrary ik = let open QCheck.Iter in - (* let int_arb = QCheck.map ~rev:Ints_t.to_bigint Ints_t.of_bigint MyCheck.Arbitrary.big_int in *) + (* let int_arb = QCheck.map ~rev:Ints_t.to_bigint Ints_t.of_bigint GobQCheck.Arbitrary.big_int in *) (* TODO: apparently bigints are really slow compared to int64 for domaintest *) - let int_arb = QCheck.map ~rev:Ints_t.to_int64 Ints_t.of_int64 MyCheck.Arbitrary.int64 in + let int_arb = QCheck.map ~rev:Ints_t.to_int64 Ints_t.of_int64 GobQCheck.Arbitrary.int64 in let pair_arb = QCheck.pair int_arb int_arb in let shrink = function - | Some (l, u) -> (return None) <+> (MyCheck.shrink pair_arb (l, u) >|= of_interval ik >|= fst) + | Some (l, u) -> (return None) <+> (GobQCheck.shrink pair_arb (l, u) >|= of_interval ik >|= fst) | None -> empty in QCheck.(set_shrink shrink @@ set_print show @@ map (*~rev:BatOption.get*) (fun x -> of_interval ik x |> fst ) pair_arb) @@ -1601,13 +1601,13 @@ struct let arbitrary ik = let open QCheck.Iter in - (* let int_arb = QCheck.map ~rev:Ints_t.to_bigint Ints_t.of_bigint MyCheck.Arbitrary.big_int in *) + (* let int_arb = QCheck.map ~rev:Ints_t.to_bigint Ints_t.of_bigint GobQCheck.Arbitrary.big_int in *) (* TODO: apparently bigints are really slow compared to int64 for domaintest *) - let int_arb = QCheck.map ~rev:Ints_t.to_int64 Ints_t.of_int64 MyCheck.Arbitrary.int64 in + let int_arb = QCheck.map ~rev:Ints_t.to_int64 Ints_t.of_int64 GobQCheck.Arbitrary.int64 in let pair_arb = QCheck.pair int_arb int_arb in let list_pair_arb = QCheck.small_list pair_arb in let canonize_randomly_generated_list = (fun x -> norm_intvs ik x |> fst) in - let shrink xs = MyCheck.shrink list_pair_arb xs >|= canonize_randomly_generated_list + let shrink xs = GobQCheck.shrink list_pair_arb xs >|= canonize_randomly_generated_list in QCheck.(set_shrink shrink @@ set_print show @@ map (*~rev:BatOption.get*) canonize_randomly_generated_list list_pair_arb) end @@ -1695,7 +1695,7 @@ struct let logand n1 n2 = of_bool ((to_bool' n1) && (to_bool' n2)) let logor n1 n2 = of_bool ((to_bool' n1) || (to_bool' n2)) let cast_to ?torg t x = failwith @@ "Cast_to not implemented for " ^ (name ()) ^ "." - let arbitrary ik = QCheck.map ~rev:Ints_t.to_int64 Ints_t.of_int64 MyCheck.Arbitrary.int64 (* TODO: use ikind *) + let arbitrary ik = QCheck.map ~rev:Ints_t.to_int64 Ints_t.of_int64 GobQCheck.Arbitrary.int64 (* TODO: use ikind *) let invariant _ _ = Invariant.none (* TODO *) end @@ -2402,8 +2402,8 @@ struct let excluded s = from_excl ik s in let definite x = of_int ik x in let shrink = function - | `Excluded (s, _) -> MyCheck.shrink (S.arbitrary ()) s >|= excluded (* S TODO: possibly shrink excluded to definite *) - | `Definite x -> (return `Bot) <+> (MyCheck.shrink (BigInt.arbitrary ()) x >|= definite) + | `Excluded (s, _) -> GobQCheck.shrink (S.arbitrary ()) s >|= excluded (* S TODO: possibly shrink excluded to definite *) + | `Definite x -> (return `Bot) <+> (GobQCheck.shrink (BigInt.arbitrary ()) x >|= definite) | `Bot -> empty in QCheck.frequency ~shrink ~print:show [ @@ -2816,8 +2816,8 @@ module Enums : S with type int_t = BigInt.t = struct let neg s = of_excl_list ik (BISet.elements s) in let pos s = norm ik (Inc s) in let shrink = function - | Exc (s, _) -> MyCheck.shrink (BISet.arbitrary ()) s >|= neg (* S TODO: possibly shrink neg to pos *) - | Inc s -> MyCheck.shrink (BISet.arbitrary ()) s >|= pos + | Exc (s, _) -> GobQCheck.shrink (BISet.arbitrary ()) s >|= neg (* S TODO: possibly shrink neg to pos *) + | Inc s -> GobQCheck.shrink (BISet.arbitrary ()) s >|= pos in QCheck.frequency ~shrink ~print:show [ 20, QCheck.map neg (BISet.arbitrary ()); @@ -3307,7 +3307,7 @@ struct let arbitrary ik = let open QCheck in - let int_arb = map ~rev:Ints_t.to_int64 Ints_t.of_int64 MyCheck.Arbitrary.int64 in + let int_arb = map ~rev:Ints_t.to_int64 Ints_t.of_int64 GobQCheck.Arbitrary.int64 in let cong_arb = pair int_arb int_arb in let of_pair ik p = normalize ik (Some p) in let to_pair = Option.get in diff --git a/src/common/common.mld b/src/common/common.mld index 662c789572..bf3f4d62e1 100644 --- a/src/common/common.mld +++ b/src/common/common.mld @@ -69,6 +69,3 @@ RichVarinfo {2 Standard library} {!modules:GobFormat} - -{2 Other libraries} -{!modules:MyCheck} diff --git a/src/common/domains/printable.ml b/src/common/domains/printable.ml index 3499cfdb04..cc01718ee8 100644 --- a/src/common/domains/printable.ml +++ b/src/common/domains/printable.ml @@ -233,9 +233,9 @@ struct let arbitrary () = let open QCheck.Iter in let shrink = function - | `Lifted x -> (return `Bot) <+> (MyCheck.shrink (Base.arbitrary ()) x >|= lift) + | `Lifted x -> (return `Bot) <+> (GobQCheck.shrink (Base.arbitrary ()) x >|= lift) | `Bot -> empty - | `Top -> MyCheck.Iter.of_arbitrary ~n:20 (Base.arbitrary ()) >|= lift + | `Top -> GobQCheck.Iter.of_arbitrary ~n:20 (Base.arbitrary ()) >|= lift in QCheck.frequency ~shrink ~print:show [ 20, QCheck.map lift (Base.arbitrary ()); @@ -626,8 +626,8 @@ struct let arbitrary () = let open QCheck.Iter in let shrink = function - | `Lifted x -> MyCheck.shrink (Base.arbitrary ()) x >|= lift - | `Top -> MyCheck.Iter.of_arbitrary ~n:20 (Base.arbitrary ()) >|= lift + | `Lifted x -> GobQCheck.shrink (Base.arbitrary ()) x >|= lift + | `Top -> GobQCheck.Iter.of_arbitrary ~n:20 (Base.arbitrary ()) >|= lift in QCheck.frequency ~shrink ~print:show [ 20, QCheck.map lift (Base.arbitrary ()); diff --git a/src/goblint_lib.ml b/src/goblint_lib.ml index cdb37b1256..e448d23775 100644 --- a/src/goblint_lib.ml +++ b/src/goblint_lib.ml @@ -461,9 +461,3 @@ module ApronPrecCompareUtil = ApronPrecCompareUtil OCaml standard library extensions which are not provided by {!Batteries}. *) module GobFormat = GobFormat - -(** {2 Other libraries} - - External library extensions. *) - -module MyCheck = MyCheck diff --git a/src/util/std/dune b/src/util/std/dune index c6961a1725..b074a29937 100644 --- a/src/util/std/dune +++ b/src/util/std/dune @@ -9,7 +9,8 @@ goblint-cil fpath yojson - yaml) + yaml + qcheck-core) (preprocess (pps ppx_deriving.std diff --git a/src/common/domains/myCheck.ml b/src/util/std/gobQCheck.ml similarity index 100% rename from src/common/domains/myCheck.ml rename to src/util/std/gobQCheck.ml diff --git a/src/util/std/goblint_std.ml b/src/util/std/goblint_std.ml index e716d1df5b..0d548cac08 100644 --- a/src/util/std/goblint_std.ml +++ b/src/util/std/goblint_std.ml @@ -19,6 +19,7 @@ module GobUnix = GobUnix module GobFpath = GobFpath module GobPretty = GobPretty +module GobQCheck = GobQCheck module GobYaml = GobYaml module GobYojson = GobYojson module GobZ = GobZ diff --git a/unittest/util/intOpsTest.ml b/unittest/util/intOpsTest.ml index 611f2f546f..006c66e13f 100644 --- a/unittest/util/intOpsTest.ml +++ b/unittest/util/intOpsTest.ml @@ -10,13 +10,13 @@ let old_div a b = if Z.lt a Z.zero then Z.neg (Z.ediv (Z.neg a) b) else Z.ediv a let old_rem a b = Z.sub a (Z.mul b (old_div a b)) let test_bigint_div = - QCheck.(Test.make ~name:"div" (pair MyCheck.Arbitrary.big_int MyCheck.Arbitrary.big_int) (fun (x, y) -> + QCheck.(Test.make ~name:"div" (pair GobQCheck.Arbitrary.big_int GobQCheck.Arbitrary.big_int) (fun (x, y) -> assume (Z.compare y Z.zero <> 0); Z.equal (Z.div x y) (old_div x y) )) let test_bigint_rem = - QCheck.(Test.make ~name:"rem" (pair MyCheck.Arbitrary.big_int MyCheck.Arbitrary.big_int) (fun (x, y) -> + QCheck.(Test.make ~name:"rem" (pair GobQCheck.Arbitrary.big_int GobQCheck.Arbitrary.big_int) (fun (x, y) -> assume (Z.compare y Z.zero <> 0); Z.equal (Z.rem x y) (old_rem x y) )) From c7f94ff3dc56c116f1835f027ad338dfaebfcb30 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 6 Dec 2023 15:54:14 +0200 Subject: [PATCH 1211/1312] Remove Basetype dependency on Lattice --- src/common/cdomains/basetype.ml | 12 ------------ src/domains/boolDomain.ml | 8 +++++++- src/domains/queries.ml | 6 +++++- src/framework/constraints.ml | 2 +- 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/common/cdomains/basetype.ml b/src/common/cdomains/basetype.ml index da6c2bc100..1b846309aa 100644 --- a/src/common/cdomains/basetype.ml +++ b/src/common/cdomains/basetype.ml @@ -33,12 +33,6 @@ struct let printXml f x = BatPrintf.fprintf f "\n\n%s\n\n\n" (XmlUtil.escape (show x)) end -module Strings: Lattice.S with type t = [`Bot | `Lifted of string | `Top] = - Lattice.Flat (RawStrings) (struct - let top_name = "?" - let bot_name = "-" - end) - module RawBools: Printable.S with type t = bool = struct include Printable.StdLeaf @@ -50,12 +44,6 @@ struct let printXml f x = BatPrintf.fprintf f "\n\n%s\n\n\n" (show x) end -module Bools: Lattice.S with type t = [`Bot | `Lifted of bool | `Top] = - Lattice.Flat (RawBools) (struct - let top_name = "?" - let bot_name = "-" - end) - module CilExp = struct include CilType.Exp diff --git a/src/domains/boolDomain.ml b/src/domains/boolDomain.ml index e088c3605c..43e15e1405 100644 --- a/src/domains/boolDomain.ml +++ b/src/domains/boolDomain.ml @@ -38,4 +38,10 @@ struct let widen = (&&) let meet = (||) let narrow = (||) -end \ No newline at end of file +end + +module FlatBool: Lattice.S with type t = [`Bot | `Lifted of bool | `Top] = + Lattice.Flat (Bool) (struct + let top_name = "?" + let bot_name = "-" + end) diff --git a/src/domains/queries.ml b/src/domains/queries.ml index b9fa28f5be..228320bef3 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -32,7 +32,11 @@ module FlatYojson = Lattice.Flat (Printable.Yojson) (struct let bot_name = "bot yojson" end) -module SD = Basetype.Strings +module SD: Lattice.S with type t = [`Bot | `Lifted of string | `Top] = + Lattice.Flat (Basetype.RawStrings) (struct + let top_name = "?" + let bot_name = "-" + end) module VD = ValueDomain.Compound module AD = ValueDomain.AD diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index b6046d023b..329b3b6415 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1344,7 +1344,7 @@ struct module EM = struct - include MapDomain.MapBot (Basetype.CilExp) (Basetype.Bools) + include MapDomain.MapBot (Basetype.CilExp) (BoolDomain.FlatBool) let name () = "branches" end From 983a226c7872c528897e0f70f4c631eef8aa7ff5 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 6 Dec 2023 16:03:38 +0200 Subject: [PATCH 1212/1312] Remove Lattice dependency on GobConfig --- src/common/domains/lattice.ml | 10 +++------- src/domains/mapDomain.ml | 2 +- src/framework/constraints.ml | 11 ++++++++--- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/common/domains/lattice.ml b/src/common/domains/lattice.ml index 51306d637f..9ea3f74635 100644 --- a/src/common/domains/lattice.ml +++ b/src/common/domains/lattice.ml @@ -148,18 +148,14 @@ struct end (* HAS SIDE-EFFECTS ---- PLEASE INSTANCIATE ONLY ONCE!!! *) -module HConsed (Base:S) = +module HConsed (Base:S) (Arg: sig val assume_idempotent: bool end) = struct include Printable.HConsed (Base) - (* We do refine int values on narrow and meet {!IntDomain.IntDomTupleImpl}, which can lead to fixpoint issues if we assume x op x = x *) - (* see https://github.com/goblint/analyzer/issues/1005 *) - let int_refine_active = GobConfig.get_string "ana.int.refinement" <> "never" - let lift_f2 f x y = f (unlift x) (unlift y) - let narrow x y = if (not int_refine_active) && x.BatHashcons.tag == y.BatHashcons.tag then x else lift (lift_f2 Base.narrow x y) + let narrow x y = if Arg.assume_idempotent && x.BatHashcons.tag == y.BatHashcons.tag then x else lift (lift_f2 Base.narrow x y) let widen x y = if x.BatHashcons.tag == y.BatHashcons.tag then x else lift (lift_f2 Base.widen x y) - let meet x y = if (not int_refine_active) && x.BatHashcons.tag == y.BatHashcons.tag then x else lift (lift_f2 Base.meet x y) + let meet x y = if Arg.assume_idempotent && x.BatHashcons.tag == y.BatHashcons.tag then x else lift (lift_f2 Base.meet x y) let join x y = if x.BatHashcons.tag == y.BatHashcons.tag then x else lift (lift_f2 Base.join x y) let leq x y = (x.BatHashcons.tag == y.BatHashcons.tag) || lift_f2 Base.leq x y let is_top = lift_f Base.is_top diff --git a/src/domains/mapDomain.ml b/src/domains/mapDomain.ml index 76dec6f0d2..4972da7d26 100644 --- a/src/domains/mapDomain.ml +++ b/src/domains/mapDomain.ml @@ -263,7 +263,7 @@ module HConsed (M: S) : S with type key = M.key and type value = M.value = struct - include Lattice.HConsed (M) + include Lattice.HConsed (M) (struct let assume_idempotent = false end) type key = M.key type value = M.value diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 329b3b6415..2763835e71 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -12,12 +12,17 @@ module M = Messages (** Lifts a [Spec] so that the domain is [Hashcons]d *) module HashconsLifter (S:Spec) - : Spec with module D = Lattice.HConsed (S.D) - and module G = S.G + : Spec with module G = S.G and module C = S.C = struct - module D = Lattice.HConsed (S.D) + module HConsedArg = + struct + (* We do refine int values on narrow and meet {!IntDomain.IntDomTupleImpl}, which can lead to fixpoint issues if we assume x op x = x *) + (* see https://github.com/goblint/analyzer/issues/1005 *) + let assume_idempotent = GobConfig.get_string "ana.int.refinement" = "never" + end + module D = Lattice.HConsed (S.D) (HConsedArg) module G = S.G module C = S.C module V = S.V From a4f9689b173d8a071e9575c24c8567e708143d31 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 6 Dec 2023 16:10:01 +0200 Subject: [PATCH 1213/1312] Fix unittest compilation --- unittest/dune | 2 +- unittest/util/intOpsTest.ml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/unittest/dune b/unittest/dune index 7313aa964b..a08a4b2323 100644 --- a/unittest/dune +++ b/unittest/dune @@ -2,7 +2,7 @@ (test (name mainTest) - (libraries ounit2 qcheck-ounit goblint.lib goblint.sites.dune goblint.build-info.dune) + (libraries ounit2 qcheck-ounit goblint.std goblint.lib goblint.sites.dune goblint.build-info.dune) (preprocess (pps ppx_deriving.std ppx_deriving_hash ppx_deriving_yojson)) (flags :standard -linkall)) diff --git a/unittest/util/intOpsTest.ml b/unittest/util/intOpsTest.ml index 006c66e13f..307d9e84b0 100644 --- a/unittest/util/intOpsTest.ml +++ b/unittest/util/intOpsTest.ml @@ -1,4 +1,5 @@ open OUnit2 +open Goblint_std open Goblint_lib (* If the first operand of a div is negative, Zarith rounds the result away from zero. From 19bcd3a753f21b679d4789f597eb26c0c79b4339 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 6 Dec 2023 16:14:57 +0200 Subject: [PATCH 1214/1312] Extract Lattice to goblint_domain dune library --- src/common/common.mld | 1 - src/domain/domain.mld | 9 +++++++++ src/domain/dune | 19 +++++++++++++++++++ src/{common/domains => domain}/lattice.ml | 0 src/dune | 2 +- src/index.mld | 3 +++ 6 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 src/domain/domain.mld create mode 100644 src/domain/dune rename src/{common/domains => domain}/lattice.ml (100%) diff --git a/src/common/common.mld b/src/common/common.mld index bf3f4d62e1..d8b8604b0b 100644 --- a/src/common/common.mld +++ b/src/common/common.mld @@ -30,7 +30,6 @@ Options {1 Domains} {!modules: Printable -Lattice } {2 Analysis-specific} diff --git a/src/domain/domain.mld b/src/domain/domain.mld new file mode 100644 index 0000000000..43d650abdd --- /dev/null +++ b/src/domain/domain.mld @@ -0,0 +1,9 @@ +{0 Library goblint.domain} +This library is unwrapped and provides the following top-level modules. +For better context, see {!Goblint_lib} which also documents these modules. + + +{1 Domains} +{!modules: +Lattice +} diff --git a/src/domain/dune b/src/domain/dune new file mode 100644 index 0000000000..45345b5946 --- /dev/null +++ b/src/domain/dune @@ -0,0 +1,19 @@ +(include_subdirs unqualified) + +(library + (name goblint_domain) + (public_name goblint.domain) + (wrapped false) ; TODO: wrap + (libraries + batteries.unthreaded + goblint_std + goblint_common + goblint-cil) + (flags :standard -open Goblint_std) + (preprocess + (pps + ppx_deriving.std + ppx_deriving_hash + ppx_deriving_yojson))) + +(documentation) diff --git a/src/common/domains/lattice.ml b/src/domain/lattice.ml similarity index 100% rename from src/common/domains/lattice.ml rename to src/domain/lattice.ml diff --git a/src/dune b/src/dune index d3fe6bdd0d..b57de472d3 100644 --- a/src/dune +++ b/src/dune @@ -7,7 +7,7 @@ (name goblint_lib) (public_name goblint.lib) (modules :standard \ goblint privPrecCompare apronPrecCompare messagesCompare) - (libraries goblint.sites goblint.build-info goblint-cil.all-features batteries.unthreaded qcheck-core.runner sha json-data-encoding jsonrpc cpu arg-complete fpath yaml yaml.unix uuidm goblint_timing catapult goblint_backtrace fileutils goblint_std goblint_common + (libraries goblint.sites goblint.build-info goblint-cil.all-features batteries.unthreaded qcheck-core.runner sha json-data-encoding jsonrpc cpu arg-complete fpath yaml yaml.unix uuidm goblint_timing catapult goblint_backtrace fileutils goblint_std goblint_common goblint_domain ; Conditionally compile based on whether apron optional dependency is installed or not. ; Alternative dependencies seem like the only way to optionally depend on optional dependencies. ; See: https://dune.readthedocs.io/en/stable/concepts.html#alternative-dependencies. diff --git a/src/index.mld b/src/index.mld index 2afbbc97ae..393323286b 100644 --- a/src/index.mld +++ b/src/index.mld @@ -10,6 +10,9 @@ This library currently contains the majority of Goblint and is in the process of {2 Library goblint.common} This {{!page-common}unwrapped library} contains various common modules extracted from {!Goblint_lib}. +{2 Library goblint.domain} +This {{!page-domain}unwrapped library} contains various domain modules extracted from {!Goblint_lib}. + {1 Library extensions} The following libraries provide extensions to other OCaml libraries. From 5937314efc456f84625e5b97c9312526a87f23b0 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 6 Dec 2023 16:20:52 +0200 Subject: [PATCH 1215/1312] Move general domains to goblint_domain library --- src/{domains => domain}/boolDomain.ml | 0 src/{domains => domain}/disjointDomain.ml | 0 src/domain/domain.mld | 12 ++++++++++++ src/{domains => domain}/flagHelper.ml | 0 src/{domains => domain}/hoareDomain.ml | 0 src/{domains => domain}/mapDomain.ml | 0 src/{domains => domain}/partitionDomain.ml | 0 src/{domains => domain}/setDomain.ml | 0 src/{domains => domain}/trieDomain.ml | 0 9 files changed, 12 insertions(+) rename src/{domains => domain}/boolDomain.ml (100%) rename src/{domains => domain}/disjointDomain.ml (100%) rename src/{domains => domain}/flagHelper.ml (100%) rename src/{domains => domain}/hoareDomain.ml (100%) rename src/{domains => domain}/mapDomain.ml (100%) rename src/{domains => domain}/partitionDomain.ml (100%) rename src/{domains => domain}/setDomain.ml (100%) rename src/{domains => domain}/trieDomain.ml (100%) diff --git a/src/domains/boolDomain.ml b/src/domain/boolDomain.ml similarity index 100% rename from src/domains/boolDomain.ml rename to src/domain/boolDomain.ml diff --git a/src/domains/disjointDomain.ml b/src/domain/disjointDomain.ml similarity index 100% rename from src/domains/disjointDomain.ml rename to src/domain/disjointDomain.ml diff --git a/src/domain/domain.mld b/src/domain/domain.mld index 43d650abdd..ce7e1a5859 100644 --- a/src/domain/domain.mld +++ b/src/domain/domain.mld @@ -7,3 +7,15 @@ For better context, see {!Goblint_lib} which also documents these modules. {!modules: Lattice } + +{2 General} +{!modules: +BoolDomain +SetDomain +MapDomain +TrieDomain +DisjointDomain +HoareDomain +PartitionDomain +FlagHelper +} diff --git a/src/domains/flagHelper.ml b/src/domain/flagHelper.ml similarity index 100% rename from src/domains/flagHelper.ml rename to src/domain/flagHelper.ml diff --git a/src/domains/hoareDomain.ml b/src/domain/hoareDomain.ml similarity index 100% rename from src/domains/hoareDomain.ml rename to src/domain/hoareDomain.ml diff --git a/src/domains/mapDomain.ml b/src/domain/mapDomain.ml similarity index 100% rename from src/domains/mapDomain.ml rename to src/domain/mapDomain.ml diff --git a/src/domains/partitionDomain.ml b/src/domain/partitionDomain.ml similarity index 100% rename from src/domains/partitionDomain.ml rename to src/domain/partitionDomain.ml diff --git a/src/domains/setDomain.ml b/src/domain/setDomain.ml similarity index 100% rename from src/domains/setDomain.ml rename to src/domain/setDomain.ml diff --git a/src/domains/trieDomain.ml b/src/domain/trieDomain.ml similarity index 100% rename from src/domains/trieDomain.ml rename to src/domain/trieDomain.ml From 1eb7af8e3e68abaeb60aca9056b15161f24f4679 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 6 Dec 2023 16:46:21 +0200 Subject: [PATCH 1216/1312] Remove Tracing dependency on CilType --- src/common/util/messages.ml | 18 ++++++++++++++++++ src/common/util/tracing.ml | 17 ----------------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/common/util/messages.ml b/src/common/util/messages.ml index 42a3118978..c9a08e8177 100644 --- a/src/common/util/messages.ml +++ b/src/common/util/messages.ml @@ -340,3 +340,21 @@ let msg_final severity ?(tags=[]) ?(category=Category.Unknown) fmt = GobPretty.igprintf () fmt include Tracing + +open Pretty + +let tracel sys ?var fmt = + let loc = !current_loc in + let docloc sys doc = + printtrace sys (dprintf "(%a)@?" CilType.Location.pretty loc ++ indent 2 doc); + in + gtrace true docloc sys var ~loc ignore fmt + +let traceli sys ?var ?(subsys=[]) fmt = + let loc = !current_loc in + let g () = activate sys subsys in + let docloc sys doc: unit = + printtrace sys (dprintf "(%a)" CilType.Location.pretty loc ++ indent 2 doc); + traceIndent () + in + gtrace true docloc sys var ~loc g fmt diff --git a/src/common/util/tracing.ml b/src/common/util/tracing.ml index ad8892c396..e4167d83a8 100644 --- a/src/common/util/tracing.ml +++ b/src/common/util/tracing.ml @@ -67,13 +67,6 @@ let trace sys ?var fmt = gtrace true printtrace sys var ignore fmt * c: continue/normal print w/o indent-change *) -let tracel sys ?var fmt = - let loc = !current_loc in - let docloc sys doc = - printtrace sys (dprintf "(%a)@?" CilType.Location.pretty loc ++ indent 2 doc); - in - gtrace true docloc sys var ~loc ignore fmt - let tracei (sys:string) ?var ?(subsys=[]) fmt = let f sys d = printtrace sys d; traceIndent () in let g () = activate sys subsys in @@ -85,13 +78,3 @@ let traceu sys fmt = let f sys d = printtrace sys d; traceOutdent () in let g () = deactivate sys in gtrace true f sys None g fmt - - -let traceli sys ?var ?(subsys=[]) fmt = - let loc = !current_loc in - let g () = activate sys subsys in - let docloc sys doc: unit = - printtrace sys (dprintf "(%a)" CilType.Location.pretty loc ++ indent 2 doc); - traceIndent () - in - gtrace true docloc sys var ~loc g fmt From 1ac6baf4b5a2239e7b0d6aaf48496a36502efce6 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 6 Dec 2023 16:56:01 +0200 Subject: [PATCH 1217/1312] Extract Tracing to goblint_tracing dune library --- src/analyses/basePriv.ml | 4 ++-- src/analyses/extractPthread.ml | 2 +- src/analyses/stackTrace.ml | 4 ++-- src/cdomains/valueDomain.ml | 2 +- src/common/common.mld | 1 - src/common/dune | 1 + src/common/util/gobConfig.ml | 9 ++++----- src/common/util/messages.ml | 3 ++- src/dune | 2 +- src/framework/constraints.ml | 12 ++++++------ src/framework/control.ml | 10 +++++----- src/goblint_lib.ml | 1 - src/index.mld | 3 +++ src/maingoblint.ml | 6 +++--- src/util/tracing/dune | 9 +++++++++ .../tracing.ml => util/tracing/goblint_tracing.ml} | 1 + 16 files changed, 41 insertions(+), 29 deletions(-) create mode 100644 src/util/tracing/dune rename src/{common/util/tracing.ml => util/tracing/goblint_tracing.ml} (99%) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index e42cd5a309..f9a4a22f44 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -230,7 +230,7 @@ struct CPA.find x st.cpa (* let read_global ask getg cpa x = let (cpa', v) as r = read_global ask getg cpa x in - ignore (Pretty.printf "READ GLOBAL %a (%a, %B) = %a\n" CilType.Varinfo.pretty x CilType.Location.pretty !Tracing.current_loc (is_unprotected ask x) VD.pretty v); + ignore (Pretty.printf "READ GLOBAL %a (%a, %B) = %a\n" CilType.Varinfo.pretty x CilType.Location.pretty !Goblint_tracing.current_loc (is_unprotected ask x) VD.pretty v); r *) let write_global ?(invariant=false) ask getg sideg (st: BaseComponents (D).t) x v = let cpa' = CPA.add x v st.cpa in @@ -1665,7 +1665,7 @@ struct let read_global ask getg st x = let v = Priv.read_global ask getg st x in if !AnalysisState.postsolving && !is_dumping then - LVH.modify_def (VD.bot ()) (!Tracing.current_loc, x) (VD.join v) lvh; + LVH.modify_def (VD.bot ()) (!Goblint_tracing.current_loc, x) (VD.join v) lvh; v let dump () = diff --git a/src/analyses/extractPthread.ml b/src/analyses/extractPthread.ml index f084a21edb..8412a65683 100644 --- a/src/analyses/extractPthread.ml +++ b/src/analyses/extractPthread.ml @@ -220,7 +220,7 @@ module Tbls = struct let make_new_val table k = (* TODO: all same key occurrences instead *) let line = -5 - all_keys_count table in - let loc = { !Tracing.current_loc with line } in + let loc = { !Goblint_tracing.current_loc with line } in MyCFG.Statement { (mkStmtOneInstr @@ Set (var dummyFunDec.svar, zero, loc, loc)) with sid = new_sid () diff --git a/src/analyses/stackTrace.ml b/src/analyses/stackTrace.ml index 3c3bd56640..dd2cedf871 100644 --- a/src/analyses/stackTrace.ml +++ b/src/analyses/stackTrace.ml @@ -36,7 +36,7 @@ struct (* transfer functions *) let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = - [ctx.local, D.push !Tracing.current_loc ctx.local] + [ctx.local, D.push !Goblint_tracing.current_loc ctx.local] let combine_env ctx lval fexp f args fc au f_ask = ctx.local (* keep local as opposed to IdentitySpec *) @@ -46,7 +46,7 @@ struct let exitstate v = D.top () let threadenter ctx ~multiple lval f args = - [D.push !Tracing.current_loc ctx.local] + [D.push !Goblint_tracing.current_loc ctx.local] end diff --git a/src/cdomains/valueDomain.ml b/src/cdomains/valueDomain.ml index cba4b04c18..e6f3122cb0 100644 --- a/src/cdomains/valueDomain.ml +++ b/src/cdomains/valueDomain.ml @@ -502,7 +502,7 @@ struct let warn_type op x y = if GobConfig.get_bool "dbg.verbose" then - ignore @@ printf "warn_type %s: incomparable abstr. values %s and %s at %a: %a and %a\n" op (tag_name (x:t)) (tag_name (y:t)) CilType.Location.pretty !Tracing.current_loc pretty x pretty y + ignore @@ printf "warn_type %s: incomparable abstr. values %s and %s at %a: %a and %a\n" op (tag_name (x:t)) (tag_name (y:t)) CilType.Location.pretty !Goblint_tracing.current_loc pretty x pretty y let rec leq x y = match (x,y) with diff --git a/src/common/common.mld b/src/common/common.mld index d8b8604b0b..3106933602 100644 --- a/src/common/common.mld +++ b/src/common/common.mld @@ -41,7 +41,6 @@ Printable {1 I/O} {!modules: Messages -Tracing } diff --git a/src/common/dune b/src/common/dune index c8f1564782..dc9fd61f77 100644 --- a/src/common/dune +++ b/src/common/dune @@ -8,6 +8,7 @@ batteries.unthreaded zarith goblint_std + goblint_tracing goblint-cil fpath yojson diff --git a/src/common/util/gobConfig.ml b/src/common/util/gobConfig.ml index c517ba150d..24a1701ce6 100644 --- a/src/common/util/gobConfig.ml +++ b/src/common/util/gobConfig.ml @@ -21,7 +21,6 @@ *) open Batteries -open Tracing open Printf exception ConfigError of string @@ -300,7 +299,7 @@ struct try let st = String.trim st in let x = get_value !json_conf (parse_path st) in - if tracing then trace "conf-reads" "Reading '%s', it is %a.\n" st GobYojson.pretty x; + if Goblint_tracing.tracing then Goblint_tracing.trace "conf-reads" "Reading '%s', it is %a.\n" st GobYojson.pretty x; try f x with Yojson.Safe.Util.Type_error (s, _) -> eprintf "The value for '%s' has the wrong type: %s\n" st s; @@ -332,7 +331,7 @@ struct let wrap_get f x = (* self-observe options, which Spec construction depends on *) - if !building_spec && Tracing.tracing then Tracing.trace "config" "get during building_spec: %s\n" x; + if !building_spec && Goblint_tracing.tracing then Goblint_tracing.trace "config" "get during building_spec: %s\n" x; (* TODO: blacklist such building_spec option from server mode modification since it will have no effect (spec is already built) *) f x @@ -352,7 +351,7 @@ struct (** Helper function for writing values. Handles the tracing. *) let set_path_string st v = - if tracing then trace "conf" "Setting '%s' to %a.\n" st GobYojson.pretty v; + if Goblint_tracing.tracing then Goblint_tracing.trace "conf" "Setting '%s' to %a.\n" st GobYojson.pretty v; set_value v json_conf (parse_path st) let set_json st j = @@ -402,7 +401,7 @@ struct | Some fn -> let v = Yojson.Safe.from_channel % BatIO.to_input_channel |> File.with_file_in (Fpath.to_string fn) in merge v; - if tracing then trace "conf" "Merging with '%a', resulting\n%a.\n" GobFpath.pretty fn GobYojson.pretty !json_conf + if Goblint_tracing.tracing then Goblint_tracing.trace "conf" "Merging with '%a', resulting\n%a.\n" GobFpath.pretty fn GobYojson.pretty !json_conf | None -> raise (Sys_error (Printf.sprintf "%s: No such file or diretory" (Fpath.to_string fn))) end diff --git a/src/common/util/messages.ml b/src/common/util/messages.ml index c9a08e8177..d7afec43c5 100644 --- a/src/common/util/messages.ml +++ b/src/common/util/messages.ml @@ -339,7 +339,8 @@ let msg_final severity ?(tags=[]) ?(category=Category.Unknown) fmt = else GobPretty.igprintf () fmt -include Tracing + +include Goblint_tracing open Pretty diff --git a/src/dune b/src/dune index b57de472d3..ffc387447e 100644 --- a/src/dune +++ b/src/dune @@ -7,7 +7,7 @@ (name goblint_lib) (public_name goblint.lib) (modules :standard \ goblint privPrecCompare apronPrecCompare messagesCompare) - (libraries goblint.sites goblint.build-info goblint-cil.all-features batteries.unthreaded qcheck-core.runner sha json-data-encoding jsonrpc cpu arg-complete fpath yaml yaml.unix uuidm goblint_timing catapult goblint_backtrace fileutils goblint_std goblint_common goblint_domain + (libraries goblint.sites goblint.build-info goblint-cil.all-features batteries.unthreaded qcheck-core.runner sha json-data-encoding jsonrpc cpu arg-complete fpath yaml yaml.unix uuidm goblint_timing catapult goblint_backtrace fileutils goblint_std goblint_common goblint_domain goblint_tracing ; Conditionally compile based on whether apron optional dependency is installed or not. ; Alternative dependencies seem like the only way to optionally depend on optional dependencies. ; See: https://dune.readthedocs.io/en/stable/concepts.html#alternative-dependencies. diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 2763835e71..bdb4370b39 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -825,13 +825,13 @@ struct ) let tf var getl sidel getg sideg prev_node (_,edge) d (f,t) = - let old_loc = !Tracing.current_loc in - let old_loc2 = !Tracing.next_loc in - Tracing.current_loc := f; - Tracing.next_loc := t; + let old_loc = !Goblint_tracing.current_loc in + let old_loc2 = !Goblint_tracing.next_loc in + Goblint_tracing.current_loc := f; + Goblint_tracing.next_loc := t; Goblint_backtrace.protect ~mark:(fun () -> TfLocation f) ~finally:(fun () -> - Tracing.current_loc := old_loc; - Tracing.next_loc := old_loc2 + Goblint_tracing.current_loc := old_loc; + Goblint_tracing.next_loc := old_loc2 ) (fun () -> let d = tf var getl sidel getg sideg prev_node edge d in d diff --git a/src/framework/control.ml b/src/framework/control.ml index 0c9b61739b..00a6034e27 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -142,12 +142,12 @@ struct if List.mem "termination" @@ get_string_list "ana.activated" then ( (* check if we have upjumping gotos *) let open Cilfacade in - let warn_for_upjumps fundec gotos = + let warn_for_upjumps fundec gotos = if FunSet.mem live_funs fundec then ( (* set nortermiantion flag *) AnalysisState.svcomp_may_not_terminate := true; (* iterate through locations to produce warnings *) - LocSet.iter (fun l _ -> + LocSet.iter (fun l _ -> M.warn ~loc:(M.Location.CilLocation l) ~category:Termination "The program might not terminate! (Upjumping Goto)" ) gotos ) @@ -313,7 +313,7 @@ struct if M.tracing then M.trace "con" "Initializer %a\n" CilType.Location.pretty loc; (*incr count; if (get_bool "dbg.verbose")&& (!count mod 1000 = 0) then Printf.printf "%d %!" !count; *) - Tracing.current_loc := loc; + Goblint_tracing.current_loc := loc; match edge with | MyCFG.Entry func -> if M.tracing then M.trace "global_inits" "Entry %a\n" d_lval (var func.svar); @@ -335,9 +335,9 @@ struct in let with_externs = do_extern_inits ctx file in (*if (get_bool "dbg.verbose") then Printf.printf "Number of init. edges : %d\nWorking:" (List.length edges); *) - let old_loc = !Tracing.current_loc in + let old_loc = !Goblint_tracing.current_loc in let result : Spec.D.t = List.fold_left transfer_func with_externs edges in - Tracing.current_loc := old_loc; + Goblint_tracing.current_loc := old_loc; if M.tracing then M.trace "global_inits" "startstate: %a\n" Spec.D.pretty result; result, !funs in diff --git a/src/goblint_lib.ml b/src/goblint_lib.ml index e448d23775..3c7dcf41a5 100644 --- a/src/goblint_lib.ml +++ b/src/goblint_lib.ml @@ -325,7 +325,6 @@ module SolverBox = SolverBox Various input/output interfaces and formats. *) module Messages = Messages -module Tracing = Tracing (** {2 Front-end} diff --git a/src/index.mld b/src/index.mld index 393323286b..bad756a8f1 100644 --- a/src/index.mld +++ b/src/index.mld @@ -46,6 +46,9 @@ The following libraries provide utilities which are completely independent of Go {2 Library goblint.timing} {!modules:Goblint_timing} +{2 Library goblint.tracing} +{!modules:Goblint_tracing} + {1 Vendored} The following libraries are vendored in Goblint. diff --git a/src/maingoblint.ml b/src/maingoblint.ml index dcee9abb13..2c7d353594 100644 --- a/src/maingoblint.ml +++ b/src/maingoblint.ml @@ -53,7 +53,7 @@ let rec option_spec_list: Arg_complete.speclist Lazy.t = lazy ( let add_string l = let f str = l := str :: !l in Arg_complete.String (f, Arg_complete.empty) in let add_int l = let f str = l := str :: !l in Arg_complete.Int (f, Arg_complete.empty) in let set_trace sys = - if Messages.tracing then Tracing.addsystem sys + if Messages.tracing then Goblint_tracing.addsystem sys else (prerr_endline "Goblint has been compiled without tracing, recompile in trace profile (./scripts/trace_on.sh)"; raise Stdlib.Exit) in let configure_html () = @@ -112,8 +112,8 @@ let rec option_spec_list: Arg_complete.speclist Lazy.t = lazy ( ; "--print_options" , Arg_complete.Unit (fun () -> Options.print_options (); exit 0), "" ; "--print_all_options" , Arg_complete.Unit (fun () -> Options.print_all_options (); exit 0), "" ; "--trace" , Arg_complete.String (set_trace, Arg_complete.empty), "" - ; "--tracevars" , add_string Tracing.tracevars, "" - ; "--tracelocs" , add_int Tracing.tracelocs, "" + ; "--tracevars" , add_string Goblint_tracing.tracevars, "" + ; "--tracelocs" , add_int Goblint_tracing.tracelocs, "" ; "--help" , Arg_complete.Unit (fun _ -> print_help stdout),"" ; "--html" , Arg_complete.Unit (fun _ -> configure_html ()),"" ; "--sarif" , Arg_complete.Unit (fun _ -> configure_sarif ()),"" diff --git a/src/util/tracing/dune b/src/util/tracing/dune new file mode 100644 index 0000000000..7e37139567 --- /dev/null +++ b/src/util/tracing/dune @@ -0,0 +1,9 @@ +(include_subdirs no) + +(library + (name goblint_tracing) + (public_name goblint.tracing) + (libraries + goblint_std + goblint-cil + goblint_build_info)) diff --git a/src/common/util/tracing.ml b/src/util/tracing/goblint_tracing.ml similarity index 99% rename from src/common/util/tracing.ml rename to src/util/tracing/goblint_tracing.ml index e4167d83a8..0e5580b036 100644 --- a/src/common/util/tracing.ml +++ b/src/util/tracing/goblint_tracing.ml @@ -4,6 +4,7 @@ * large domains we output. The original code generated the document object * even when the subsystem is not activated. *) +open Goblint_std open GoblintCil open Pretty From 54d7fdf5dd0f2494fa41a7d55764ec73b54330e3 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 6 Dec 2023 17:08:30 +0200 Subject: [PATCH 1218/1312] Extract configuration to goblint_config dune library --- .github/workflows/options.yml | 6 ++--- .readthedocs.yaml | 2 +- docs/user-guide/configuring.md | 2 +- src/common/common.mld | 8 ------- src/common/dune | 9 ++------ src/{common/util => config}/afterConfig.ml | 0 src/config/config.mld | 14 +++++++++++ src/config/dune | 23 +++++++++++++++++++ src/{common/util => config}/gobConfig.ml | 0 src/{common/util => config}/jsonSchema.ml | 0 src/{common/util => config}/options.ml | 2 +- .../util => config}/options.schema.json | 0 src/dune | 2 +- src/goblint_lib.ml | 2 +- src/index.mld | 3 +++ 15 files changed, 50 insertions(+), 23 deletions(-) rename src/{common/util => config}/afterConfig.ml (100%) create mode 100644 src/config/config.mld create mode 100644 src/config/dune rename src/{common/util => config}/gobConfig.ml (100%) rename src/{common/util => config}/jsonSchema.ml (100%) rename src/{common/util => config}/options.ml (98%) rename src/{common/util => config}/options.schema.json (100%) diff --git a/.github/workflows/options.yml b/.github/workflows/options.yml index 94c49e4bf6..7ef8b6929e 100644 --- a/.github/workflows/options.yml +++ b/.github/workflows/options.yml @@ -26,10 +26,10 @@ jobs: run: npm install -g ajv-cli - name: Migrate schema # https://github.com/ajv-validator/ajv-cli/issues/199 - run: ajv migrate -s src/common/util/options.schema.json + run: ajv migrate -s src/config/options.schema.json - name: Validate conf - run: ajv validate -s src/common/util/options.schema.json -d "conf/**/*.json" + run: ajv validate -s src/config/options.schema.json -d "conf/**/*.json" - name: Validate incremental tests - run: ajv validate -s src/common/util/options.schema.json -d "tests/incremental/*/*.json" + run: ajv validate -s src/config/options.schema.json -d "tests/incremental/*/*.json" diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 08044d195c..22f9c86121 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -20,4 +20,4 @@ build: - pip install json-schema-for-humans post_build: - mkdir _readthedocs/html/jsfh/ - - generate-schema-doc --config-file jsfh.yml src/common/util/options.schema.json _readthedocs/html/jsfh/ + - generate-schema-doc --config-file jsfh.yml src/config/options.schema.json _readthedocs/html/jsfh/ diff --git a/docs/user-guide/configuring.md b/docs/user-guide/configuring.md index 9a32a14a4c..cae57fc8cd 100644 --- a/docs/user-guide/configuring.md +++ b/docs/user-guide/configuring.md @@ -24,7 +24,7 @@ In `.vscode/settings.json` add the following: "/conf/*.json", "/tests/incremental/*/*.json" ], - "url": "/src/common/util/options.schema.json" + "url": "/src/config/options.schema.json" } ] } diff --git a/src/common/common.mld b/src/common/common.mld index 3106933602..a1cc9a261a 100644 --- a/src/common/common.mld +++ b/src/common/common.mld @@ -18,14 +18,6 @@ AnalysisState ControlSpecC } -{2 Configuration} -{!modules: -GobConfig -AfterConfig -JsonSchema -Options -} - {1 Domains} {!modules: diff --git a/src/common/dune b/src/common/dune index dc9fd61f77..7994798579 100644 --- a/src/common/dune +++ b/src/common/dune @@ -8,23 +8,18 @@ batteries.unthreaded zarith goblint_std + goblint_config goblint_tracing goblint-cil fpath yojson - json-data-encoding - cpu goblint_timing - goblint_build_info - goblint.sites qcheck-core.runner) (flags :standard -open Goblint_std) (preprocess (pps ppx_deriving.std ppx_deriving_hash - ppx_deriving_yojson - ppx_blob)) - (preprocessor_deps (file util/options.schema.json))) + ppx_deriving_yojson))) (documentation) diff --git a/src/common/util/afterConfig.ml b/src/config/afterConfig.ml similarity index 100% rename from src/common/util/afterConfig.ml rename to src/config/afterConfig.ml diff --git a/src/config/config.mld b/src/config/config.mld new file mode 100644 index 0000000000..160eaa9a11 --- /dev/null +++ b/src/config/config.mld @@ -0,0 +1,14 @@ +{0 Library goblint.config} +This library is unwrapped and provides the following top-level modules. +For better context, see {!Goblint_lib} which also documents these modules. + + +{1 Framework} + +{2 Configuration} +{!modules: +GobConfig +AfterConfig +JsonSchema +Options +} diff --git a/src/config/dune b/src/config/dune new file mode 100644 index 0000000000..b4dfea5c18 --- /dev/null +++ b/src/config/dune @@ -0,0 +1,23 @@ +(include_subdirs unqualified) + +(library + (name goblint_config) + (public_name goblint.config) + (wrapped false) ; TODO: wrap + (libraries + batteries.unthreaded + goblint_std + goblint_tracing + fpath + yojson + json-data-encoding + cpu + goblint.sites + qcheck-core.runner) + (flags :standard -open Goblint_std) + (preprocess + (pps + ppx_blob)) + (preprocessor_deps (file options.schema.json))) + +(documentation) diff --git a/src/common/util/gobConfig.ml b/src/config/gobConfig.ml similarity index 100% rename from src/common/util/gobConfig.ml rename to src/config/gobConfig.ml diff --git a/src/common/util/jsonSchema.ml b/src/config/jsonSchema.ml similarity index 100% rename from src/common/util/jsonSchema.ml rename to src/config/jsonSchema.ml diff --git a/src/common/util/options.ml b/src/config/options.ml similarity index 98% rename from src/common/util/options.ml rename to src/config/options.ml index 3046f70809..125da3330b 100644 --- a/src/common/util/options.ml +++ b/src/config/options.ml @@ -1,4 +1,4 @@ -(** [src/common/util/options.schema.json] low-level access. *) +(** [src/config/options.schema.json] low-level access. *) open Json_schema diff --git a/src/common/util/options.schema.json b/src/config/options.schema.json similarity index 100% rename from src/common/util/options.schema.json rename to src/config/options.schema.json diff --git a/src/dune b/src/dune index ffc387447e..6738398e59 100644 --- a/src/dune +++ b/src/dune @@ -7,7 +7,7 @@ (name goblint_lib) (public_name goblint.lib) (modules :standard \ goblint privPrecCompare apronPrecCompare messagesCompare) - (libraries goblint.sites goblint.build-info goblint-cil.all-features batteries.unthreaded qcheck-core.runner sha json-data-encoding jsonrpc cpu arg-complete fpath yaml yaml.unix uuidm goblint_timing catapult goblint_backtrace fileutils goblint_std goblint_common goblint_domain goblint_tracing + (libraries goblint.sites goblint.build-info goblint-cil.all-features batteries.unthreaded qcheck-core.runner sha json-data-encoding jsonrpc cpu arg-complete fpath yaml yaml.unix uuidm goblint_timing catapult goblint_backtrace fileutils goblint_std goblint_config goblint_common goblint_domain goblint_tracing ; Conditionally compile based on whether apron optional dependency is installed or not. ; Alternative dependencies seem like the only way to optionally depend on optional dependencies. ; See: https://dune.readthedocs.io/en/stable/concepts.html#alternative-dependencies. diff --git a/src/goblint_lib.ml b/src/goblint_lib.ml index 3c7dcf41a5..fee35c1ec9 100644 --- a/src/goblint_lib.ml +++ b/src/goblint_lib.ml @@ -51,7 +51,7 @@ module VarQuery = VarQuery (** {2 Configuration} Runtime configuration is represented as JSON. - Options are specified and documented by the JSON schema [src/common/util/options.schema.json]. *) + Options are specified and documented by the JSON schema [src/config/options.schema.json]. *) module GobConfig = GobConfig module AfterConfig = AfterConfig diff --git a/src/index.mld b/src/index.mld index bad756a8f1..eb7907f6fe 100644 --- a/src/index.mld +++ b/src/index.mld @@ -7,6 +7,9 @@ The following libraries make up Goblint's main codebase. {!modules:Goblint_lib} This library currently contains the majority of Goblint and is in the process of being split into smaller libraries. +{2 Library goblint.config} +This {{!page-config}unwrapped library} contains various configuration modules extracted from {!Goblint_lib}. + {2 Library goblint.common} This {{!page-common}unwrapped library} contains various common modules extracted from {!Goblint_lib}. From b5f6272dc15df99311ec2ad9d32c69ecf33b70ce Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 6 Dec 2023 17:18:15 +0200 Subject: [PATCH 1219/1312] Update Gobview dependencies on Goblint libraries --- gobview | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gobview b/gobview index d4eb66b9eb..3de13d7412 160000 --- a/gobview +++ b/gobview @@ -1 +1 @@ -Subproject commit d4eb66b9eb277349a75141cb01899dbab9d3ef5d +Subproject commit 3de13d74124ab7bc30d8be299f02570d8f498b84 From 036a016d3f1f219fc360fb5ba48e46bfc6f45364 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 6 Dec 2023 17:33:13 +0200 Subject: [PATCH 1220/1312] Remove CfgTools dependency on IntDomain via Offset --- src/cdomains/offset.ml | 2 +- src/common/util/cilfacade.ml | 8 +++++++- src/framework/cfgTools.ml | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/cdomains/offset.ml b/src/cdomains/offset.ml index eca85e08a4..52cfe9eb41 100644 --- a/src/cdomains/offset.ml +++ b/src/cdomains/offset.ml @@ -22,7 +22,7 @@ struct include CilType.Exp let name () = "exp index" - let any = CastE (TInt (Cilfacade.ptrdiff_ikind (), []), mkString "any_index") + let any = Cilfacade.any_index_exp let all = CastE (TInt (Cilfacade.ptrdiff_ikind (), []), mkString "all_index") (* Override output *) diff --git a/src/common/util/cilfacade.ml b/src/common/util/cilfacade.ml index 26a2f082a4..929dce6c25 100644 --- a/src/common/util/cilfacade.ml +++ b/src/common/util/cilfacade.ml @@ -706,4 +706,10 @@ let add_function_declarations (file: Cil.file): unit = in let fun_decls = List.filter_map declaration_from_GFun functions in let globals = upto_last_type @ fun_decls @ non_types @ functions in - file.globals <- globals \ No newline at end of file + file.globals <- globals + + +(** Special index expression for some unknown index. + Weakly updates array in assignment. + Used for [exp.fast_global_inits]. *) +let any_index_exp = CastE (TInt (ptrdiff_ikind (), []), mkString "any_index") diff --git a/src/framework/cfgTools.ml b/src/framework/cfgTools.ml index 8f98a48e84..af887da432 100644 --- a/src/framework/cfgTools.ml +++ b/src/framework/cfgTools.ml @@ -685,7 +685,7 @@ let getGlobalInits (file: file) : edges = lval in let rec any_index_offset = function - | Index (e,o) -> Index (Offset.Index.Exp.any, any_index_offset o) + | Index (e,o) -> Index (Cilfacade.any_index_exp, any_index_offset o) | Field (f,o) -> Field (f, any_index_offset o) | NoOffset -> NoOffset in From dbec9e8df27b1b12f8c2bfae3ebf032686a8c483 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 6 Dec 2023 17:38:03 +0200 Subject: [PATCH 1221/1312] Remove CompareCFG dependency on CfgTools --- src/common/util/cilfacade.ml | 6 ++++++ src/framework/cfgTools.ml | 6 +----- src/framework/constraints.ml | 2 +- src/incremental/compareCFG.ml | 2 +- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/common/util/cilfacade.ml b/src/common/util/cilfacade.ml index 929dce6c25..0fb9bd32b5 100644 --- a/src/common/util/cilfacade.ml +++ b/src/common/util/cilfacade.ml @@ -531,6 +531,12 @@ let stmt_fundecs: fundec StmtH.t ResettableLazy.t = h ) + +let get_pseudo_return_id fd = + let start_id = 10_000_000_000 in (* TODO get max_sid? *) + let sid = Hashtbl.hash fd.svar.vid in (* Need pure sid instead of Cil.new_sid for incremental, similar to vid in Cilfacade.create_var. We only add one return stmt per loop, so the hash from the functions vid should be unique. *) + if sid < start_id then sid + start_id else sid + let pseudo_return_to_fun = StmtH.create 113 (** Find [fundec] which the [stmt] is in. *) diff --git a/src/framework/cfgTools.ml b/src/framework/cfgTools.ml index af887da432..1afdb69514 100644 --- a/src/framework/cfgTools.ml +++ b/src/framework/cfgTools.ml @@ -122,10 +122,6 @@ let rec pretty_edges () = function | [_,x] -> Edge.pretty_plain () x | (_,x)::xs -> Pretty.dprintf "%a; %a" Edge.pretty_plain x pretty_edges xs -let get_pseudo_return_id fd = - let start_id = 10_000_000_000 in (* TODO get max_sid? *) - let sid = Hashtbl.hash fd.svar.vid in (* Need pure sid instead of Cil.new_sid for incremental, similar to vid in Cilfacade.create_var. We only add one return stmt per loop, so the hash from the functions vid should be unique. *) - if sid < start_id then sid + start_id else sid let node_scc_global = NH.create 113 @@ -260,7 +256,7 @@ let createCFG (file: file) = if Messages.tracing then Messages.trace "cfg" "adding pseudo-return to the function %s.\n" fd.svar.vname; let fd_end_loc = {fd_loc with line = fd_loc.endLine; byte = fd_loc.endByte; column = fd_loc.endColumn} in let newst = mkStmt (Return (None, fd_end_loc)) in - newst.sid <- get_pseudo_return_id fd; + newst.sid <- Cilfacade.get_pseudo_return_id fd; Cilfacade.StmtH.add Cilfacade.pseudo_return_to_fun newst fd; Cilfacade.IntH.replace Cilfacade.pseudo_return_stmt_sids newst.sid newst; let newst_node = Statement newst in diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index bdb4370b39..77d3a38186 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1004,7 +1004,7 @@ struct let dummy_pseudo_return_node f = (* not the same as in CFG, but compares equal because of sid *) - Node.Statement ({Cil.dummyStmt with sid = CfgTools.get_pseudo_return_id f}) + Node.Statement ({Cil.dummyStmt with sid = Cilfacade.get_pseudo_return_id f}) in let add_nodes_of_fun (functions: fundec list) (withEntry: fundec -> bool) = let add_stmts (f: fundec) = diff --git a/src/incremental/compareCFG.ml b/src/incremental/compareCFG.ml index 225cbb1c76..55b3fa8fc5 100644 --- a/src/incremental/compareCFG.ml +++ b/src/incremental/compareCFG.ml @@ -17,7 +17,7 @@ let (&&<>) (prev_result: bool * rename_mapping) f : bool * rename_mapping = let eq_node (x, fun1) (y, fun2) ~rename_mapping = let isPseudoReturn f sid = - let pid = CfgTools.get_pseudo_return_id f in + let pid = Cilfacade.get_pseudo_return_id f in sid == pid in match x,y with | Statement s1, Statement s2 -> From 3dd355154a57853a477e0726daf393fee2d21e55 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 6 Dec 2023 17:43:16 +0200 Subject: [PATCH 1222/1312] Move CfgTools to goblint_common --- src/common/common.mld | 1 + src/{ => common}/framework/cfgTools.ml | 0 2 files changed, 1 insertion(+) rename src/{ => common}/framework/cfgTools.ml (100%) diff --git a/src/common/common.mld b/src/common/common.mld index a1cc9a261a..2ad88c3758 100644 --- a/src/common/common.mld +++ b/src/common/common.mld @@ -10,6 +10,7 @@ For better context, see {!Goblint_lib} which also documents these modules. Node Edge MyCFG +CfgTools } {2 Specification} diff --git a/src/framework/cfgTools.ml b/src/common/framework/cfgTools.ml similarity index 100% rename from src/framework/cfgTools.ml rename to src/common/framework/cfgTools.ml From deb727b36cc59ecb946208d1d0fac439085d05a1 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 6 Dec 2023 17:49:03 +0200 Subject: [PATCH 1223/1312] Extract incremental to goblint_incremental dune library --- src/dune | 2 +- src/{util => incremental}/cilMaps.ml | 0 src/incremental/dune | 22 ++++++++++++++++++++++ src/incremental/incremental.mld | 16 ++++++++++++++++ src/index.mld | 3 +++ 5 files changed, 42 insertions(+), 1 deletion(-) rename src/{util => incremental}/cilMaps.ml (100%) create mode 100644 src/incremental/dune create mode 100644 src/incremental/incremental.mld diff --git a/src/dune b/src/dune index 6738398e59..e40a58fcbd 100644 --- a/src/dune +++ b/src/dune @@ -7,7 +7,7 @@ (name goblint_lib) (public_name goblint.lib) (modules :standard \ goblint privPrecCompare apronPrecCompare messagesCompare) - (libraries goblint.sites goblint.build-info goblint-cil.all-features batteries.unthreaded qcheck-core.runner sha json-data-encoding jsonrpc cpu arg-complete fpath yaml yaml.unix uuidm goblint_timing catapult goblint_backtrace fileutils goblint_std goblint_config goblint_common goblint_domain goblint_tracing + (libraries goblint.sites goblint.build-info goblint-cil.all-features batteries.unthreaded qcheck-core.runner sha json-data-encoding jsonrpc cpu arg-complete fpath yaml yaml.unix uuidm goblint_timing catapult goblint_backtrace fileutils goblint_std goblint_config goblint_common goblint_domain goblint_incremental goblint_tracing ; Conditionally compile based on whether apron optional dependency is installed or not. ; Alternative dependencies seem like the only way to optionally depend on optional dependencies. ; See: https://dune.readthedocs.io/en/stable/concepts.html#alternative-dependencies. diff --git a/src/util/cilMaps.ml b/src/incremental/cilMaps.ml similarity index 100% rename from src/util/cilMaps.ml rename to src/incremental/cilMaps.ml diff --git a/src/incremental/dune b/src/incremental/dune new file mode 100644 index 0000000000..a664c78ea7 --- /dev/null +++ b/src/incremental/dune @@ -0,0 +1,22 @@ +(include_subdirs unqualified) + +(library + (name goblint_incremental) + (public_name goblint.incremental) + (wrapped false) ; TODO: wrap + (libraries + batteries.unthreaded + zarith + goblint_std + goblint_config + goblint_common + goblint-cil + fpath) + (flags :standard -open Goblint_std) + (preprocess + (pps + ppx_deriving.std + ppx_deriving_hash + ppx_deriving_yojson))) + +(documentation) diff --git a/src/incremental/incremental.mld b/src/incremental/incremental.mld new file mode 100644 index 0000000000..bf9b6e6a58 --- /dev/null +++ b/src/incremental/incremental.mld @@ -0,0 +1,16 @@ +{0 Library goblint.incremental} +This library is unwrapped and provides the following top-level modules. +For better context, see {!Goblint_lib} which also documents these modules. + + +{1 Incremental} + +{!modules: +CompareCIL +CompareAST +CompareCFG +UpdateCil +MaxIdUtil +Serialize +CilMaps +} diff --git a/src/index.mld b/src/index.mld index eb7907f6fe..755a736e6c 100644 --- a/src/index.mld +++ b/src/index.mld @@ -16,6 +16,9 @@ This {{!page-common}unwrapped library} contains various common modules extracted {2 Library goblint.domain} This {{!page-domain}unwrapped library} contains various domain modules extracted from {!Goblint_lib}. +{2 Library goblint.incremental} +This {{!page-incremental}unwrapped library} contains various incremental modules extracted from {!Goblint_lib}. + {1 Library extensions} The following libraries provide extensions to other OCaml libraries. From 9261b71573b38f8b9e56d9121a9b1025325a13ec Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 7 Dec 2023 10:53:43 +0200 Subject: [PATCH 1224/1312] Extract library specificaton to goblint_library dune library --- src/dune | 2 +- src/index.mld | 3 +++ src/{domains => util/library}/accessKind.ml | 0 src/util/library/dune | 18 ++++++++++++++++++ src/util/library/library.mld | 14 ++++++++++++++ src/{analyses => util/library}/libraryDesc.ml | 0 src/{analyses => util/library}/libraryDsl.ml | 0 src/{analyses => util/library}/libraryDsl.mli | 0 .../library}/libraryFunctions.ml | 0 .../library}/libraryFunctions.mli | 0 10 files changed, 36 insertions(+), 1 deletion(-) rename src/{domains => util/library}/accessKind.ml (100%) create mode 100644 src/util/library/dune create mode 100644 src/util/library/library.mld rename src/{analyses => util/library}/libraryDesc.ml (100%) rename src/{analyses => util/library}/libraryDsl.ml (100%) rename src/{analyses => util/library}/libraryDsl.mli (100%) rename src/{analyses => util/library}/libraryFunctions.ml (100%) rename src/{analyses => util/library}/libraryFunctions.mli (100%) diff --git a/src/dune b/src/dune index e40a58fcbd..8ad1b3aa4c 100644 --- a/src/dune +++ b/src/dune @@ -7,7 +7,7 @@ (name goblint_lib) (public_name goblint.lib) (modules :standard \ goblint privPrecCompare apronPrecCompare messagesCompare) - (libraries goblint.sites goblint.build-info goblint-cil.all-features batteries.unthreaded qcheck-core.runner sha json-data-encoding jsonrpc cpu arg-complete fpath yaml yaml.unix uuidm goblint_timing catapult goblint_backtrace fileutils goblint_std goblint_config goblint_common goblint_domain goblint_incremental goblint_tracing + (libraries goblint.sites goblint.build-info goblint-cil.all-features batteries.unthreaded qcheck-core.runner sha json-data-encoding jsonrpc cpu arg-complete fpath yaml yaml.unix uuidm goblint_timing catapult goblint_backtrace fileutils goblint_std goblint_config goblint_common goblint_domain goblint_library goblint_incremental goblint_tracing ; Conditionally compile based on whether apron optional dependency is installed or not. ; Alternative dependencies seem like the only way to optionally depend on optional dependencies. ; See: https://dune.readthedocs.io/en/stable/concepts.html#alternative-dependencies. diff --git a/src/index.mld b/src/index.mld index 755a736e6c..76b9d230dd 100644 --- a/src/index.mld +++ b/src/index.mld @@ -16,6 +16,9 @@ This {{!page-common}unwrapped library} contains various common modules extracted {2 Library goblint.domain} This {{!page-domain}unwrapped library} contains various domain modules extracted from {!Goblint_lib}. +{2 Library goblint.library} +This {{!page-library}unwrapped library} contains various library specification modules extracted from {!Goblint_lib}. + {2 Library goblint.incremental} This {{!page-incremental}unwrapped library} contains various incremental modules extracted from {!Goblint_lib}. diff --git a/src/domains/accessKind.ml b/src/util/library/accessKind.ml similarity index 100% rename from src/domains/accessKind.ml rename to src/util/library/accessKind.ml diff --git a/src/util/library/dune b/src/util/library/dune new file mode 100644 index 0000000000..075c01c35d --- /dev/null +++ b/src/util/library/dune @@ -0,0 +1,18 @@ +(include_subdirs no) + +(library + (name goblint_library) + (public_name goblint.library) + (wrapped false) ; TODO: wrap + (libraries + batteries.unthreaded + goblint_common + goblint_domain + goblint_config + goblint-cil) + (preprocess + (pps + ppx_deriving.std + ppx_deriving_hash))) + +(documentation) diff --git a/src/util/library/library.mld b/src/util/library/library.mld new file mode 100644 index 0000000000..f55db3f2ff --- /dev/null +++ b/src/util/library/library.mld @@ -0,0 +1,14 @@ +{0 Library goblint.library} +This library is unwrapped and provides the following top-level modules. +For better context, see {!Goblint_lib} which also documents these modules. + + +{1 Utilities} + +{2 Library specification} +{!modules: +AccessKind +LibraryDesc +LibraryDsl +LibraryFunctions +} diff --git a/src/analyses/libraryDesc.ml b/src/util/library/libraryDesc.ml similarity index 100% rename from src/analyses/libraryDesc.ml rename to src/util/library/libraryDesc.ml diff --git a/src/analyses/libraryDsl.ml b/src/util/library/libraryDsl.ml similarity index 100% rename from src/analyses/libraryDsl.ml rename to src/util/library/libraryDsl.ml diff --git a/src/analyses/libraryDsl.mli b/src/util/library/libraryDsl.mli similarity index 100% rename from src/analyses/libraryDsl.mli rename to src/util/library/libraryDsl.mli diff --git a/src/analyses/libraryFunctions.ml b/src/util/library/libraryFunctions.ml similarity index 100% rename from src/analyses/libraryFunctions.ml rename to src/util/library/libraryFunctions.ml diff --git a/src/analyses/libraryFunctions.mli b/src/util/library/libraryFunctions.mli similarity index 100% rename from src/analyses/libraryFunctions.mli rename to src/util/library/libraryFunctions.mli From 5662024232f32fe74dd25c9317dee4436ecb212d Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 7 Dec 2023 11:07:00 +0200 Subject: [PATCH 1225/1312] Fix LibraryFunctions.invalidate_actions indentation --- src/util/library/libraryFunctions.ml | 164 +++++++++++++-------------- 1 file changed, 82 insertions(+), 82 deletions(-) diff --git a/src/util/library/libraryFunctions.ml b/src/util/library/libraryFunctions.ml index 8152e5b886..2c65f7ae61 100644 --- a/src/util/library/libraryFunctions.ml +++ b/src/util/library/libraryFunctions.ml @@ -1233,88 +1233,88 @@ open Invalidate * We assume that no known functions that are reachable are executed/spawned. For that we use ThreadCreate above. *) (* WTF: why are argument numbers 1-indexed (in partition)? *) let invalidate_actions = [ - "__printf_chk", readsAll;(*safe*) - "printk", readsAll;(*safe*) - "__mutex_init", readsAll;(*safe*) - "__builtin___snprintf_chk", writes [1];(*keep [1]*) - "__vfprintf_chk", writes [1];(*keep [1]*) - "__builtin_va_arg", readsAll;(*safe*) - "__builtin_va_end", readsAll;(*safe*) - "__builtin_va_start", readsAll;(*safe*) - "__ctype_b_loc", readsAll;(*safe*) - "__errno", readsAll;(*safe*) - "__errno_location", readsAll;(*safe*) - "__strdup", readsAll;(*safe*) - "strtoul__extinline", readsAll;(*safe*) - "readdir_r", writesAll;(*unsafe*) - "atoi__extinline", readsAll;(*safe*) - "_IO_getc", writesAll;(*unsafe*) - "pipe", writesAll;(*unsafe*) - "strerror_r", writesAll;(*unsafe*) - "raise", writesAll;(*unsafe*) - "_strlen", readsAll;(*safe*) - "stat__extinline", writesAllButFirst 1 readsAll;(*drop 1*) - "lstat__extinline", writesAllButFirst 1 readsAll;(*drop 1*) - "waitpid", readsAll;(*safe*) - "__open_alias", readsAll;(*safe*) - "__open_2", readsAll;(*safe*) - "ioctl", writesAll;(*unsafe*) - "fstat__extinline", writesAll;(*unsafe*) - "scandir", writes [1;3;4];(*keep [1;3;4]*) - "bindtextdomain", readsAll;(*safe*) - "textdomain", readsAll;(*safe*) - "dcgettext", readsAll;(*safe*) - "putw", readsAll;(*safe*) - "__getdelim", writes [3];(*keep [3]*) - "__h_errno_location", readsAll;(*safe*) - "__fxstat", readsAll;(*safe*) - "openlog", readsAll;(*safe*) - "umask", readsAll;(*safe*) - "clntudp_create", writesAllButFirst 3 readsAll;(*drop 3*) - "svctcp_create", readsAll;(*safe*) - "clntudp_bufcreate", writesAll;(*unsafe*) - "authunix_create_default", readsAll;(*safe*) - "clnt_broadcast", writesAll;(*unsafe*) - "clnt_sperrno", readsAll;(*safe*) - "pmap_unset", writesAll;(*unsafe*) - "svcudp_create", readsAll;(*safe*) - "svc_register", writesAll;(*unsafe*) - "svc_run", writesAll;(*unsafe*) - "dup", readsAll; (*safe*) - "__builtin___vsnprintf", writesAllButFirst 3 readsAll; (*drop 3*) - "__builtin___vsnprintf_chk", writesAllButFirst 3 readsAll; (*drop 3*) - "__error", readsAll; (*safe*) - "__maskrune", writesAll; (*unsafe*) - "times", writesAll; (*unsafe*) - "timespec_get", writes [1]; - "__tolower", readsAll; (*safe*) - "signal", writesAll; (*unsafe*) - "BF_cfb64_encrypt", writes [1;3;4;5]; (*keep [1;3;4,5]*) - "BZ2_bzBuffToBuffDecompress", writes [3;4]; (*keep [3;4]*) - "uncompress", writes [3;4]; (*keep [3;4]*) - "__xstat", writes [3]; (*keep [1]*) - "__lxstat", writes [3]; (*keep [1]*) - "remove", readsAll; - "BZ2_bzBuffToBuffCompress", writes [3;4]; (*keep [3;4]*) - "compress2", writes [3]; (*keep [3]*) - "__toupper", readsAll; (*safe*) - "BF_set_key", writes [3]; (*keep [3]*) - "PL_NewHashTable", readsAll; (*safe*) - "assert_failed", readsAll; (*safe*) - "munmap", readsAll;(*safe*) - "mmap", readsAll;(*safe*) - "__builtin_va_arg_pack_len", readsAll; - "__open_too_many_args", readsAll; - "usb_submit_urb", readsAll; (* first argument is written to but according to specification must not be read from anymore *) - "dev_driver_string", readsAll; - "__spin_lock_init", writes [1]; - "kmem_cache_create", readsAll; - "idr_pre_get", readsAll; - "zil_replay", writes [1;2;3;5]; - (* ddverify *) - "sema_init", readsAll; - "__goblint_assume_join", readsAll; - ] + "__printf_chk", readsAll;(*safe*) + "printk", readsAll;(*safe*) + "__mutex_init", readsAll;(*safe*) + "__builtin___snprintf_chk", writes [1];(*keep [1]*) + "__vfprintf_chk", writes [1];(*keep [1]*) + "__builtin_va_arg", readsAll;(*safe*) + "__builtin_va_end", readsAll;(*safe*) + "__builtin_va_start", readsAll;(*safe*) + "__ctype_b_loc", readsAll;(*safe*) + "__errno", readsAll;(*safe*) + "__errno_location", readsAll;(*safe*) + "__strdup", readsAll;(*safe*) + "strtoul__extinline", readsAll;(*safe*) + "readdir_r", writesAll;(*unsafe*) + "atoi__extinline", readsAll;(*safe*) + "_IO_getc", writesAll;(*unsafe*) + "pipe", writesAll;(*unsafe*) + "strerror_r", writesAll;(*unsafe*) + "raise", writesAll;(*unsafe*) + "_strlen", readsAll;(*safe*) + "stat__extinline", writesAllButFirst 1 readsAll;(*drop 1*) + "lstat__extinline", writesAllButFirst 1 readsAll;(*drop 1*) + "waitpid", readsAll;(*safe*) + "__open_alias", readsAll;(*safe*) + "__open_2", readsAll;(*safe*) + "ioctl", writesAll;(*unsafe*) + "fstat__extinline", writesAll;(*unsafe*) + "scandir", writes [1;3;4];(*keep [1;3;4]*) + "bindtextdomain", readsAll;(*safe*) + "textdomain", readsAll;(*safe*) + "dcgettext", readsAll;(*safe*) + "putw", readsAll;(*safe*) + "__getdelim", writes [3];(*keep [3]*) + "__h_errno_location", readsAll;(*safe*) + "__fxstat", readsAll;(*safe*) + "openlog", readsAll;(*safe*) + "umask", readsAll;(*safe*) + "clntudp_create", writesAllButFirst 3 readsAll;(*drop 3*) + "svctcp_create", readsAll;(*safe*) + "clntudp_bufcreate", writesAll;(*unsafe*) + "authunix_create_default", readsAll;(*safe*) + "clnt_broadcast", writesAll;(*unsafe*) + "clnt_sperrno", readsAll;(*safe*) + "pmap_unset", writesAll;(*unsafe*) + "svcudp_create", readsAll;(*safe*) + "svc_register", writesAll;(*unsafe*) + "svc_run", writesAll;(*unsafe*) + "dup", readsAll; (*safe*) + "__builtin___vsnprintf", writesAllButFirst 3 readsAll; (*drop 3*) + "__builtin___vsnprintf_chk", writesAllButFirst 3 readsAll; (*drop 3*) + "__error", readsAll; (*safe*) + "__maskrune", writesAll; (*unsafe*) + "times", writesAll; (*unsafe*) + "timespec_get", writes [1]; + "__tolower", readsAll; (*safe*) + "signal", writesAll; (*unsafe*) + "BF_cfb64_encrypt", writes [1;3;4;5]; (*keep [1;3;4,5]*) + "BZ2_bzBuffToBuffDecompress", writes [3;4]; (*keep [3;4]*) + "uncompress", writes [3;4]; (*keep [3;4]*) + "__xstat", writes [3]; (*keep [1]*) + "__lxstat", writes [3]; (*keep [1]*) + "remove", readsAll; + "BZ2_bzBuffToBuffCompress", writes [3;4]; (*keep [3;4]*) + "compress2", writes [3]; (*keep [3]*) + "__toupper", readsAll; (*safe*) + "BF_set_key", writes [3]; (*keep [3]*) + "PL_NewHashTable", readsAll; (*safe*) + "assert_failed", readsAll; (*safe*) + "munmap", readsAll;(*safe*) + "mmap", readsAll;(*safe*) + "__builtin_va_arg_pack_len", readsAll; + "__open_too_many_args", readsAll; + "usb_submit_urb", readsAll; (* first argument is written to but according to specification must not be read from anymore *) + "dev_driver_string", readsAll; + "__spin_lock_init", writes [1]; + "kmem_cache_create", readsAll; + "idr_pre_get", readsAll; + "zil_replay", writes [1;2;3;5]; + (* ddverify *) + "sema_init", readsAll; + "__goblint_assume_join", readsAll; +] let invalidate_actions = let tbl = Hashtbl.create 113 in From a6095e7d3990dc518d3f7f14dbae6dc9ed8ddb8d Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 7 Dec 2023 12:09:00 +0200 Subject: [PATCH 1226/1312] Use (include_subdirs no) for new dune libraries --- src/config/dune | 2 +- src/domain/dune | 2 +- src/incremental/dune | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/config/dune b/src/config/dune index b4dfea5c18..1508e2553e 100644 --- a/src/config/dune +++ b/src/config/dune @@ -1,4 +1,4 @@ -(include_subdirs unqualified) +(include_subdirs no) (library (name goblint_config) diff --git a/src/domain/dune b/src/domain/dune index 45345b5946..169f4a1d5c 100644 --- a/src/domain/dune +++ b/src/domain/dune @@ -1,4 +1,4 @@ -(include_subdirs unqualified) +(include_subdirs no) (library (name goblint_domain) diff --git a/src/incremental/dune b/src/incremental/dune index a664c78ea7..595dba22f7 100644 --- a/src/incremental/dune +++ b/src/incremental/dune @@ -1,4 +1,4 @@ -(include_subdirs unqualified) +(include_subdirs no) (library (name goblint_incremental) From 029c1e93daa47624cce2dd94d4ed2c600ed1cc07 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 7 Dec 2023 11:22:05 +0100 Subject: [PATCH 1227/1312] Add newline back for ocamldoc Co-authored-by: Simmo Saan --- src/cdomains/apron/relationDomain.apron.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cdomains/apron/relationDomain.apron.ml b/src/cdomains/apron/relationDomain.apron.ml index aca2346820..48720b0382 100644 --- a/src/cdomains/apron/relationDomain.apron.ml +++ b/src/cdomains/apron/relationDomain.apron.ml @@ -1,6 +1,7 @@ (** Signatures for relational value domains. See {!ApronDomain} and {!AffineEqualityDomain}. *) + open GobApron open Batteries open GoblintCil From 4fae8c62af777b3199cd63525cb88ae212206d8e Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 7 Dec 2023 11:22:34 +0100 Subject: [PATCH 1228/1312] Directly use `Apron.Var.t` Co-authored-by: Simmo Saan --- src/analyses/apron/apronAnalysis.apron.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/apron/apronAnalysis.apron.ml b/src/analyses/apron/apronAnalysis.apron.ml index 72dc81c121..0ba17cdb35 100644 --- a/src/analyses/apron/apronAnalysis.apron.ml +++ b/src/analyses/apron/apronAnalysis.apron.ml @@ -14,7 +14,7 @@ let spec_module: (module MCPSpec) Lazy.t = struct module V = ApronDomain.V include AD - type var = GobApron.Var.t + type var = Apron.Var.t end in let module Priv = (val RelationPriv.get_priv ()) in From 5f5c1c8cd90b4b3811e37ce46286698dfa103a65 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 7 Dec 2023 11:22:44 +0100 Subject: [PATCH 1229/1312] Directly use `Apron.Var.t` Co-authored-by: Simmo Saan --- src/analyses/apron/relationAnalysis.apron.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index b401b58e93..b794c4d70b 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -285,7 +285,7 @@ struct (* there should be smarter ways to do this, e.g. by keeping track of which values are written etc. ... *) (* See, e.g, Beckschulze E, Kowalewski S, Brauer J (2012) Access-based localization for octagons. Electron Notes Theor Comput Sci 287:29–40 *) (* Also, a local *) - let vname = GobApron.Var.to_string var in + let vname = Apron.Var.to_string var in let locals = fundec.sformals @ fundec.slocals in match List.find_opt (fun v -> VM.var_name (Local v) = vname) locals with (* TODO: optimize *) | None -> true From 129b9c3538c84d72cf70099d367766ececb89cd8 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 7 Dec 2023 17:24:31 +0100 Subject: [PATCH 1230/1312] Switch `GobApron.Var` to `Apron.Var` --- src/analyses/apron/relationAnalysis.apron.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index b794c4d70b..5e128ffc30 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -318,7 +318,7 @@ struct RD.remove_filter_with new_rel (fun var -> match RV.find_metadata var with | Some (Local _) when not (pass_to_callee fundec any_local_reachable var) -> true (* remove caller locals provided they are unreachable *) - | Some (Arg _) when not (List.mem_cmp GobApron.Var.compare var arg_vars) -> true (* remove caller args, but keep just added args *) + | Some (Arg _) when not (List.mem_cmp Apron.Var.compare var arg_vars) -> true (* remove caller args, but keep just added args *) | _ -> false (* keep everything else (just added args, globals, global privs) *) ); if M.tracing then M.tracel "combine" "relation enter newd: %a\n" RD.pretty new_rel; @@ -404,7 +404,7 @@ struct in let any_local_reachable = any_local_reachable fundec reachable_from_args in let arg_vars = f.sformals |> List.filter (RD.Tracked.varinfo_tracked) |> List.map RV.arg in - if M.tracing then M.tracel "combine" "relation remove vars: %a\n" (docList (fun v -> Pretty.text (GobApron.Var.to_string v))) arg_vars; + if M.tracing then M.tracel "combine" "relation remove vars: %a\n" (docList (fun v -> Pretty.text (Apron.Var.to_string v))) arg_vars; RD.remove_vars_with new_fun_rel arg_vars; (* fine to remove arg vars that also exist in caller because unify from new_rel adds them back with proper constraints *) let tainted = f_ask.f Queries.MayBeTainted in let tainted_vars = TaintPartialContexts.conv_varset tainted in From c2e0e169bbc132a401795f781691e257ec2df62a Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 7 Dec 2023 20:09:23 +0100 Subject: [PATCH 1231/1312] Add `GobApron` to goblint_lib.ml #1283 --- src/goblint_lib.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/goblint_lib.ml b/src/goblint_lib.ml index cdb37b1256..08691fa273 100644 --- a/src/goblint_lib.ml +++ b/src/goblint_lib.ml @@ -441,6 +441,7 @@ module WideningThresholds = WideningThresholds module VectorMatrix = VectorMatrix module SharedFunctions = SharedFunctions +module GobApron = GobApron (** {2 Precision comparison} *) From fd0d9ff2e904b7ea1bcddd673c953d1153125e84 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 8 Dec 2023 10:25:59 +0200 Subject: [PATCH 1232/1312] Add TODOs (PR #1288) --- src/common/util/cilfacade.ml | 2 +- src/domain/mapDomain.ml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/common/util/cilfacade.ml b/src/common/util/cilfacade.ml index 0fb9bd32b5..eff97da404 100644 --- a/src/common/util/cilfacade.ml +++ b/src/common/util/cilfacade.ml @@ -718,4 +718,4 @@ let add_function_declarations (file: Cil.file): unit = (** Special index expression for some unknown index. Weakly updates array in assignment. Used for [exp.fast_global_inits]. *) -let any_index_exp = CastE (TInt (ptrdiff_ikind (), []), mkString "any_index") +let any_index_exp = CastE (TInt (ptrdiff_ikind (), []), mkString "any_index") (* TODO: move back to Offset *) diff --git a/src/domain/mapDomain.ml b/src/domain/mapDomain.ml index 4972da7d26..9013b036e5 100644 --- a/src/domain/mapDomain.ml +++ b/src/domain/mapDomain.ml @@ -259,6 +259,7 @@ struct end (* TODO: this is very slow because every add/remove in a fold-loop relifts *) +(* TODO: currently hardcoded to assume_idempotent *) module HConsed (M: S) : S with type key = M.key and type value = M.value = From 54bcf607850d2e8c4dc21310abbba0a32c8959d5 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 8 Dec 2023 10:52:39 +0200 Subject: [PATCH 1233/1312] Add TODO about shallow ThreadJoin invalidate --- src/analyses/base.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 2e0002dd55..078799bea6 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2397,6 +2397,7 @@ struct (* handling thread joins... sort of *) | ThreadJoin { thread = id; ret_var }, _ -> let st' = + (* TODO: should invalidate shallowly? https://github.com/goblint/analyzer/pull/1224#discussion_r1405826773 *) match (eval_rv (Analyses.ask_of_ctx ctx) gs st ret_var) with | Int n when GobOption.exists (BI.equal BI.zero) (ID.to_int n) -> st | Address ret_a -> From 3b82569d92be73112cfbe4677ec5e35f2ad7ed2b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 8 Dec 2023 10:54:47 +0200 Subject: [PATCH 1234/1312] Ignore Goblint_tracing in Goblint_lib modules check --- scripts/goblint-lib-modules.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/goblint-lib-modules.py b/scripts/goblint-lib-modules.py index 6c264a117b..ec0e78e440 100755 --- a/scripts/goblint-lib-modules.py +++ b/scripts/goblint-lib-modules.py @@ -35,6 +35,7 @@ "Goblint_std", "Goblint_timing", "Goblint_backtrace", + "Goblint_tracing", "Goblint_sites", "Goblint_build_info", "Dune_build_info", From cb908119d50ad31b1d846bac1de3b759fc7f5427 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 8 Dec 2023 10:57:30 +0200 Subject: [PATCH 1235/1312] Fix indentation in goblint_domain --- src/domain/boolDomain.ml | 8 ++++---- src/domain/hoareDomain.ml | 22 ++++++++++++---------- src/domain/mapDomain.ml | 4 ++-- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/domain/boolDomain.ml b/src/domain/boolDomain.ml index 43e15e1405..08be66a602 100644 --- a/src/domain/boolDomain.ml +++ b/src/domain/boolDomain.ml @@ -4,10 +4,10 @@ module Bool = struct include Basetype.RawBools (* type t = bool - let equal = Bool.equal - let compare = Bool.compare - let relift x = x - let arbitrary () = QCheck.bool *) + let equal = Bool.equal + let compare = Bool.compare + let relift x = x + let arbitrary () = QCheck.bool *) let pretty_diff () (x,y) = GoblintCil.Pretty.dprintf "%s: %a not leq %a" (name ()) pretty x pretty y end diff --git a/src/domain/hoareDomain.ml b/src/domain/hoareDomain.ml index 23b1a92240..37b8231b92 100644 --- a/src/domain/hoareDomain.ml +++ b/src/domain/hoareDomain.ml @@ -134,13 +134,15 @@ struct let equal x y = leq x y && leq y x let hash xs = fold (fun v a -> a + E.hash v) xs 0 let compare x y = - if equal x y - then 0 + if equal x y then + 0 + else ( + let caridnality_comp = compare (cardinal x) (cardinal y) in + if caridnality_comp <> 0 then + caridnality_comp else - let caridnality_comp = compare (cardinal x) (cardinal y) in - if caridnality_comp <> 0 - then caridnality_comp - else Map.compare (List.compare E.compare) x y + Map.compare (List.compare E.compare) x y + ) let show x : string = let all_elems : string list = List.map E.show (elements x) in Printable.get_short_list "{" "}" all_elems @@ -234,8 +236,8 @@ struct ) s2 nil with Not_found -> dprintf "choose failed b/c of empty set s1: %d s2: %d" - (cardinal s1) - (cardinal s2) + (cardinal s1) + (cardinal s2) end end @@ -339,8 +341,8 @@ struct ) s2 nil with Not_found -> dprintf "choose failed b/c of empty set s1: %d s2: %d" - (cardinal s1) - (cardinal s2) + (cardinal s1) + (cardinal s2) end end [@@deprecated] diff --git a/src/domain/mapDomain.ml b/src/domain/mapDomain.ml index 9013b036e5..740da9969e 100644 --- a/src/domain/mapDomain.ml +++ b/src/domain/mapDomain.ml @@ -718,8 +718,8 @@ struct let singleton k v = `Lifted (M.singleton k v) let empty () = `Lifted (M.empty ()) let is_empty = function - | `Bot -> false - | `Lifted x -> M.is_empty x + | `Bot -> false + | `Lifted x -> M.is_empty x let exists f = function | `Bot -> raise (Fn_over_All "exists") | `Lifted x -> M.exists f x From a432e47951b2c54660e23e6356917ef259d965e9 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 8 Dec 2023 19:46:59 +0100 Subject: [PATCH 1236/1312] Port 6 specs --- src/util/library/libraryFunctions.ml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/util/library/libraryFunctions.ml b/src/util/library/libraryFunctions.ml index 2c65f7ae61..d91ee61d12 100644 --- a/src/util/library/libraryFunctions.ml +++ b/src/util/library/libraryFunctions.ml @@ -159,6 +159,8 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("wscanf", unknown (drop "fmt" [r] :: VarArgs (drop' [w]))); ("fwscanf", unknown (drop "stream" [r_deep; w_deep] :: drop "fmt" [r] :: VarArgs (drop' [w]))); ("swscanf", unknown (drop "buffer" [r] :: drop "fmt" [r] :: VarArgs (drop' [w]))); + ("remove", unknown [drop "pathname" [r]]); + ("raise", unknown [drop "sig" []]); (* safe-ish, we don't handle signal handlers for now *) ] (** C POSIX library functions. @@ -418,6 +420,10 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("random", special [] Rand); ("posix_memalign", unknown [drop "memptr" [w]; drop "alignment" []; drop "size" []]); (* TODO: Malloc *) ("stpcpy", unknown [drop "dest" [w]; drop "src" [r]]); + ("dup", unknown [drop "oldfd" []]); + ("readdir_r", unknown [drop "dirp" [r_deep]; drop "entry" [r_deep]; drop "result" [w]]); + ("pipe", unknown [drop "pipefd" [w_deep]]); + ("waitpid", unknown [drop "pid" []; drop "wstatus" [w]; drop "options" []]); ] (** Pthread functions. *) @@ -1246,16 +1252,12 @@ let invalidate_actions = [ "__errno_location", readsAll;(*safe*) "__strdup", readsAll;(*safe*) "strtoul__extinline", readsAll;(*safe*) - "readdir_r", writesAll;(*unsafe*) "atoi__extinline", readsAll;(*safe*) "_IO_getc", writesAll;(*unsafe*) - "pipe", writesAll;(*unsafe*) "strerror_r", writesAll;(*unsafe*) - "raise", writesAll;(*unsafe*) "_strlen", readsAll;(*safe*) "stat__extinline", writesAllButFirst 1 readsAll;(*drop 1*) "lstat__extinline", writesAllButFirst 1 readsAll;(*drop 1*) - "waitpid", readsAll;(*safe*) "__open_alias", readsAll;(*safe*) "__open_2", readsAll;(*safe*) "ioctl", writesAll;(*unsafe*) @@ -1280,7 +1282,6 @@ let invalidate_actions = [ "svcudp_create", readsAll;(*safe*) "svc_register", writesAll;(*unsafe*) "svc_run", writesAll;(*unsafe*) - "dup", readsAll; (*safe*) "__builtin___vsnprintf", writesAllButFirst 3 readsAll; (*drop 3*) "__builtin___vsnprintf_chk", writesAllButFirst 3 readsAll; (*drop 3*) "__error", readsAll; (*safe*) @@ -1294,7 +1295,6 @@ let invalidate_actions = [ "uncompress", writes [3;4]; (*keep [3;4]*) "__xstat", writes [3]; (*keep [1]*) "__lxstat", writes [3]; (*keep [1]*) - "remove", readsAll; "BZ2_bzBuffToBuffCompress", writes [3;4]; (*keep [3;4]*) "compress2", writes [3]; (*keep [3]*) "__toupper", readsAll; (*safe*) From 1823684fa70be0993a62363f7285840e6396c552 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 8 Dec 2023 20:12:48 +0100 Subject: [PATCH 1237/1312] Port 5 specs --- src/util/library/libraryFunctions.ml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/util/library/libraryFunctions.ml b/src/util/library/libraryFunctions.ml index d91ee61d12..4866f2aa17 100644 --- a/src/util/library/libraryFunctions.ml +++ b/src/util/library/libraryFunctions.ml @@ -424,6 +424,9 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("readdir_r", unknown [drop "dirp" [r_deep]; drop "entry" [r_deep]; drop "result" [w]]); ("pipe", unknown [drop "pipefd" [w_deep]]); ("waitpid", unknown [drop "pid" []; drop "wstatus" [w]; drop "options" []]); + ("strerror_r", unknown [drop "errnum" []; drop "buff" [w]; drop "buflen" []]); + ("umask", unknown [drop "mask" []]); + ("openlog", unknown [drop "ident" [r]; drop "option" []; drop "facility" []]); ] (** Pthread functions. *) @@ -644,6 +647,7 @@ let glibc_desc_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("strchrnul", unknown [drop "s" [r]; drop "c" []]); ("getdtablesize", unknown []); ("daemon", unknown [drop "nochdir" []; drop "noclose" []]); + ("putw", unknown [drop "w" []; drop "stream" [r_deep; w_deep]]); ] let linux_userspace_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ @@ -741,6 +745,7 @@ let linux_kernel_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__kmalloc", special [__ "size" []; drop "flags" []] @@ fun size -> Malloc size); ("kzalloc", special [__ "size" []; drop "flags" []] @@ fun size -> Calloc {count = Cil.one; size}); ("usb_alloc_urb", special [__ "iso_packets" []; drop "mem_flags" []] @@ fun iso_packets -> Malloc MyCFG.unknown_exp); + ("ioctl", unknown (drop "fd" [] :: drop "request" [] :: VarArgs (drop' [r]))); ] (** Goblint functions. *) @@ -1254,24 +1259,19 @@ let invalidate_actions = [ "strtoul__extinline", readsAll;(*safe*) "atoi__extinline", readsAll;(*safe*) "_IO_getc", writesAll;(*unsafe*) - "strerror_r", writesAll;(*unsafe*) "_strlen", readsAll;(*safe*) "stat__extinline", writesAllButFirst 1 readsAll;(*drop 1*) "lstat__extinline", writesAllButFirst 1 readsAll;(*drop 1*) "__open_alias", readsAll;(*safe*) "__open_2", readsAll;(*safe*) - "ioctl", writesAll;(*unsafe*) "fstat__extinline", writesAll;(*unsafe*) "scandir", writes [1;3;4];(*keep [1;3;4]*) "bindtextdomain", readsAll;(*safe*) "textdomain", readsAll;(*safe*) "dcgettext", readsAll;(*safe*) - "putw", readsAll;(*safe*) "__getdelim", writes [3];(*keep [3]*) "__h_errno_location", readsAll;(*safe*) "__fxstat", readsAll;(*safe*) - "openlog", readsAll;(*safe*) - "umask", readsAll;(*safe*) "clntudp_create", writesAllButFirst 3 readsAll;(*drop 3*) "svctcp_create", readsAll;(*safe*) "clntudp_bufcreate", writesAll;(*unsafe*) From 7d50626caa4883cbcb625a41016cbca7cf166941 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 8 Dec 2023 20:20:07 +0100 Subject: [PATCH 1238/1312] Port 2 more specs --- src/util/library/libraryFunctions.ml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/util/library/libraryFunctions.ml b/src/util/library/libraryFunctions.ml index 4866f2aa17..bb2b89c364 100644 --- a/src/util/library/libraryFunctions.ml +++ b/src/util/library/libraryFunctions.ml @@ -161,6 +161,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("swscanf", unknown (drop "buffer" [r] :: drop "fmt" [r] :: VarArgs (drop' [w]))); ("remove", unknown [drop "pathname" [r]]); ("raise", unknown [drop "sig" []]); (* safe-ish, we don't handle signal handlers for now *) + ("timespec_get", unknown [drop "ts" [w]; drop "base" []]); ] (** C POSIX library functions. @@ -427,6 +428,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("strerror_r", unknown [drop "errnum" []; drop "buff" [w]; drop "buflen" []]); ("umask", unknown [drop "mask" []]); ("openlog", unknown [drop "ident" [r]; drop "option" []; drop "facility" []]); + ("times", unknown [drop "buf" [w]]) ] (** Pthread functions. *) @@ -1272,6 +1274,7 @@ let invalidate_actions = [ "__getdelim", writes [3];(*keep [3]*) "__h_errno_location", readsAll;(*safe*) "__fxstat", readsAll;(*safe*) + (* RPC library start *) "clntudp_create", writesAllButFirst 3 readsAll;(*drop 3*) "svctcp_create", readsAll;(*safe*) "clntudp_bufcreate", writesAll;(*unsafe*) @@ -1282,12 +1285,11 @@ let invalidate_actions = [ "svcudp_create", readsAll;(*safe*) "svc_register", writesAll;(*unsafe*) "svc_run", writesAll;(*unsafe*) + (* RPC library end *) "__builtin___vsnprintf", writesAllButFirst 3 readsAll; (*drop 3*) "__builtin___vsnprintf_chk", writesAllButFirst 3 readsAll; (*drop 3*) "__error", readsAll; (*safe*) "__maskrune", writesAll; (*unsafe*) - "times", writesAll; (*unsafe*) - "timespec_get", writes [1]; "__tolower", readsAll; (*safe*) "signal", writesAll; (*unsafe*) "BF_cfb64_encrypt", writes [1;3;4;5]; (*keep [1;3;4,5]*) From 80b4f825d00cca340ebe8fb44784a76ca67c276e Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 8 Dec 2023 20:27:07 +0100 Subject: [PATCH 1239/1312] Port 3 more specs --- src/util/library/libraryFunctions.ml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/util/library/libraryFunctions.ml b/src/util/library/libraryFunctions.ml index bb2b89c364..ee8d58d886 100644 --- a/src/util/library/libraryFunctions.ml +++ b/src/util/library/libraryFunctions.ml @@ -162,6 +162,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("remove", unknown [drop "pathname" [r]]); ("raise", unknown [drop "sig" []]); (* safe-ish, we don't handle signal handlers for now *) ("timespec_get", unknown [drop "ts" [w]; drop "base" []]); + ("signal", unknown [drop "signum" []; drop "handler" [s]]); ] (** C POSIX library functions. @@ -428,7 +429,9 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("strerror_r", unknown [drop "errnum" []; drop "buff" [w]; drop "buflen" []]); ("umask", unknown [drop "mask" []]); ("openlog", unknown [drop "ident" [r]; drop "option" []; drop "facility" []]); - ("times", unknown [drop "buf" [w]]) + ("times", unknown [drop "buf" [w]]); + ("mmap", unknown [drop "addr" []; drop "length" []; drop "prot" []; drop "flags" []; drop "fd" []; drop "offset" []]); + ("munmap", unknown [drop "addr" []; drop "length" []]); ] (** Pthread functions. *) @@ -1291,7 +1294,6 @@ let invalidate_actions = [ "__error", readsAll; (*safe*) "__maskrune", writesAll; (*unsafe*) "__tolower", readsAll; (*safe*) - "signal", writesAll; (*unsafe*) "BF_cfb64_encrypt", writes [1;3;4;5]; (*keep [1;3;4,5]*) "BZ2_bzBuffToBuffDecompress", writes [3;4]; (*keep [3;4]*) "uncompress", writes [3;4]; (*keep [3;4]*) @@ -1303,8 +1305,6 @@ let invalidate_actions = [ "BF_set_key", writes [3]; (*keep [3]*) "PL_NewHashTable", readsAll; (*safe*) "assert_failed", readsAll; (*safe*) - "munmap", readsAll;(*safe*) - "mmap", readsAll;(*safe*) "__builtin_va_arg_pack_len", readsAll; "__open_too_many_args", readsAll; "usb_submit_urb", readsAll; (* first argument is written to but according to specification must not be read from anymore *) From 77b4f67b71e878d6e67a20b5181f6b1972c8908c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 11 Dec 2023 10:50:15 +0200 Subject: [PATCH 1240/1312] Fix invalid free in 73-strings/03-string_basics --- tests/regression/73-strings/03-string_basics.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression/73-strings/03-string_basics.c b/tests/regression/73-strings/03-string_basics.c index 7b913ea767..e4d6c5c5e4 100644 --- a/tests/regression/73-strings/03-string_basics.c +++ b/tests/regression/73-strings/03-string_basics.c @@ -84,7 +84,7 @@ int main() { cmp = strstr(s1, "0"); __goblint_check(cmp == NULL); // UNKNOWN - free(s1); + free(s5); return 0; } From f2fdb622997b9508908415639838500a7eadfa9c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 11 Dec 2023 10:54:24 +0200 Subject: [PATCH 1241/1312] Add TODOs related to null byte array domain --- src/analyses/base.ml | 5 ++++- src/cdomains/valueDomain.ml | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 01b27847ac..9e79eeec2b 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2191,6 +2191,7 @@ struct in let address_from_value (v:value) = match v with | Address a -> + (* TODO: is it fine to just drop the last index unconditionally? https://github.com/goblint/analyzer/pull/1076#discussion_r1408975611 *) let rec lo = function | `Index (i, `NoOffset) -> `NoOffset | `NoOffset -> `NoOffset @@ -2210,6 +2211,7 @@ struct let s2_a = address_from_value s2_v in let s2_typ = AD.type_of s2_a in (* compute value in string literals domain if s1 and s2 are both string literals *) + (* TODO: is this reliable? there could be a char* which isn't StrPtr *) if CilType.Typ.equal s1_typ charPtrType && CilType.Typ.equal s2_typ charPtrType then begin match lv, op_addr with | Some lv_val, Some f -> @@ -2304,7 +2306,8 @@ struct let a = address_from_value v in let value:value = (* if s string literal, compute strlen in string literals domain *) - if AD.type_of a = charPtrType then + (* TODO: is this reliable? there could be a char* which isn't StrPtr *) + if CilType.Typ.equal (AD.type_of a) charPtrType then Int (AD.to_string_length a) (* else compute strlen in array domain *) else diff --git a/src/cdomains/valueDomain.ml b/src/cdomains/valueDomain.ml index 4a83447e97..774bced523 100644 --- a/src/cdomains/valueDomain.ml +++ b/src/cdomains/valueDomain.ml @@ -733,6 +733,7 @@ struct | _, Bot -> Bot (* Leave uninitialized value (from malloc) alone in free to avoid trashing everything. TODO: sound? *) | t , _ -> top_value t + (* TODO: why is this separately needed? *) let rec invalidate_abstract_value = function | Top -> Top | Int i -> Int (ID.top_of (ID.ikind i)) From 0d299f40809e29c11f0579f424762e5a4a5b2854 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 12 Dec 2023 10:43:12 +0200 Subject: [PATCH 1242/1312] Add NullByteSet to API documentation (PR #1076) --- src/cdomains/nullByteSet.ml | 18 ++++++++++-------- src/goblint_lib.ml | 1 + 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/cdomains/nullByteSet.ml b/src/cdomains/nullByteSet.ml index 38fe5cbda9..6a16b0b592 100644 --- a/src/cdomains/nullByteSet.ml +++ b/src/cdomains/nullByteSet.ml @@ -1,3 +1,5 @@ +(** Abstract domains for tracking [NULL] bytes in C arrays. *) + module MustSet = struct module M = SetDomain.Reverse (SetDomain.ToppedSet (IntDomain.BigInt) (struct let topname = "All Null" end)) include M @@ -109,7 +111,7 @@ module MustMaySet = struct | Definitely -> MustSet.interval_mem (l,u) musts | Possibly -> failwith "not implemented" - let remove mode i (musts, mays) min_size = + let remove mode i (musts, mays) min_size = match mode with | Definitely -> (MustSet.remove i musts min_size, MaySet.remove i mays min_size) | Possibly -> (MustSet.remove i musts min_size, mays) @@ -133,7 +135,7 @@ module MustMaySet = struct in let mays = match maxfull with - | Some Some maxfull when Z.equal l Z.zero && Z.geq u maxfull -> + | Some Some maxfull when Z.equal l Z.zero && Z.geq u maxfull -> MaySet.top () | _ -> add_indexes l u mays @@ -141,12 +143,12 @@ module MustMaySet = struct match mode with | Definitely -> (add_indexes l u musts, mays) | Possibly -> (musts, mays) - + let remove_interval mode (l,u) min_size (musts, mays) = match mode with | Definitely -> failwith "todo" | Possibly -> - if Z.equal l Z.zero && Z.geq u min_size then + if Z.equal l Z.zero && Z.geq u min_size then (MustSet.top (), mays) else (MustSet.filter ~min_size (fun x -> (Z.lt x l || Z.gt x u) && Z.lt x min_size) musts, mays) @@ -164,8 +166,8 @@ module MustMaySet = struct let is_full_set mode (musts, mays) = match mode with | Definitely -> MustSet.is_bot musts - | Possibly -> MaySet.is_top mays - + | Possibly -> MaySet.is_top mays + let get_set mode (musts, mays) = match mode with | Definitely -> musts @@ -174,10 +176,10 @@ module MustMaySet = struct let elements ?max_size ?min_size mode (musts, mays) = match mode with | Definitely ->failwith "todo" - | Possibly -> MaySet.elements ?max_size mays + | Possibly -> MaySet.elements ?max_size mays let union_mays (must,mays) (_,mays2) = (must, MaySet.join mays mays2) - + let precise_singleton i = (MustSet.singleton i, MaySet.singleton i) diff --git a/src/goblint_lib.ml b/src/goblint_lib.ml index 5a2e0d3e0e..e402cc33fe 100644 --- a/src/goblint_lib.ml +++ b/src/goblint_lib.ml @@ -219,6 +219,7 @@ module AddressDomain = AddressDomain module StructDomain = StructDomain module UnionDomain = UnionDomain module ArrayDomain = ArrayDomain +module NullByteSet = NullByteSet module JmpBufDomain = JmpBufDomain (** {5 Combined} From 6500d35f26e8800c83f562368b8f1f355b3ddfde Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 12 Dec 2023 10:46:46 +0200 Subject: [PATCH 1243/1312] Fix NULL byte domain indentation (PR #1076) --- src/analyses/base.ml | 30 ++--- src/cdomains/arrayDomain.ml | 238 ++++++++++++++++++------------------ src/cdomains/nullByteSet.ml | 8 +- 3 files changed, 138 insertions(+), 138 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 993df9a26a..7cc937b201 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2215,21 +2215,21 @@ struct if CilType.Typ.equal s1_typ charPtrType && CilType.Typ.equal s2_typ charPtrType then begin match lv, op_addr with | Some lv_val, Some f -> - (* when whished types coincide, compute result of operation op_addr, otherwise use top *) - let lv_a = eval_lv (Analyses.ask_of_ctx ctx) gs st lv_val in - let lv_typ = Cilfacade.typeOfLval lv_val in - if all && typeSig s1_typ = typeSig s2_typ && typeSig s2_typ = typeSig lv_typ then (* all types need to coincide *) - set ~ctx (Analyses.ask_of_ctx ctx) gs st lv_a lv_typ (f s1_a s2_a) - else if not all && typeSig s1_typ = typeSig s2_typ then (* only the types of s1 and s2 need to coincide *) - set ~ctx (Analyses.ask_of_ctx ctx) gs st lv_a lv_typ (f s1_a s2_a) - else - set ~ctx (Analyses.ask_of_ctx ctx) gs st lv_a lv_typ (VD.top_value (unrollType lv_typ)) + (* when whished types coincide, compute result of operation op_addr, otherwise use top *) + let lv_a = eval_lv (Analyses.ask_of_ctx ctx) gs st lv_val in + let lv_typ = Cilfacade.typeOfLval lv_val in + if all && typeSig s1_typ = typeSig s2_typ && typeSig s2_typ = typeSig lv_typ then (* all types need to coincide *) + set ~ctx (Analyses.ask_of_ctx ctx) gs st lv_a lv_typ (f s1_a s2_a) + else if not all && typeSig s1_typ = typeSig s2_typ then (* only the types of s1 and s2 need to coincide *) + set ~ctx (Analyses.ask_of_ctx ctx) gs st lv_a lv_typ (f s1_a s2_a) + else + set ~ctx (Analyses.ask_of_ctx ctx) gs st lv_a lv_typ (VD.top_value (unrollType lv_typ)) | _ -> (* check if s1 is potentially a string literal as writing to it would be undefined behavior; then return top *) let _ = AD.string_writing_defined s1_a in set ~ctx (Analyses.ask_of_ctx ctx) gs st s1_a s1_typ (VD.top_value (unrollType s1_typ)) end - (* else compute value in array domain *) + (* else compute value in array domain *) else let lv_a, lv_typ = match lv with | Some lv_val -> eval_lv (Analyses.ask_of_ctx ctx) gs st lv_val, Cilfacade.typeOfLval lv_val @@ -2326,11 +2326,11 @@ struct if needle is substring, assign the substring of haystack starting at the first occurrence of needle to dest, if it surely isn't, assign a null_ptr *) string_manipulation haystack needle lv true (Some (fun h_a n_a -> Address (AD.substring_extraction h_a n_a))) - (fun h_ar n_ar -> match CArrays.substring_extraction h_ar n_ar with - | CArrays.IsNotSubstr -> Address (AD.null_ptr) - | CArrays.IsSubstrAtIndex0 -> Address (eval_lv (Analyses.ask_of_ctx ctx) gs st (mkMem ~addr:(Cil.stripCasts haystack) ~off:NoOffset)) - | CArrays.IsMaybeSubstr -> Address (AD.join (eval_lv (Analyses.ask_of_ctx ctx) gs st - (mkMem ~addr:(Cil.stripCasts haystack) ~off:(Index (Offset.Index.Exp.any, NoOffset)))) (AD.null_ptr))) + (fun h_ar n_ar -> match CArrays.substring_extraction h_ar n_ar with + | CArrays.IsNotSubstr -> Address (AD.null_ptr) + | CArrays.IsSubstrAtIndex0 -> Address (eval_lv (Analyses.ask_of_ctx ctx) gs st (mkMem ~addr:(Cil.stripCasts haystack) ~off:NoOffset)) + | CArrays.IsMaybeSubstr -> Address (AD.join (eval_lv (Analyses.ask_of_ctx ctx) gs st + (mkMem ~addr:(Cil.stripCasts haystack) ~off:(Index (Offset.Index.Exp.any, NoOffset)))) (AD.null_ptr))) | None -> st end | Strcmp { s1; s2; n }, _ -> diff --git a/src/cdomains/arrayDomain.ml b/src/cdomains/arrayDomain.ml index 6c47f1e87a..d4d5a46e98 100644 --- a/src/cdomains/arrayDomain.ml +++ b/src/cdomains/arrayDomain.ml @@ -1074,21 +1074,21 @@ struct (* if size has no upper limit *) | None -> (match Val.is_null v with - | NotNull -> - Nulls.remove (if Nulls.is_full_set Possibly nulls then Possibly else Definitely) i nulls min_size - (* ... and value <> null, remove i from must_nulls_set and also from may_nulls_set if not top *) - | Null -> - Nulls.add (if i <. min_size then Definitely else Possibly) i nulls - (* i < minimal size and value = null, add i to must_nulls_set and may_nulls_set *) - (* i >= minimal size and value = null, add i only to may_nulls_set *) - | Maybe -> - let removed = Nulls.remove Possibly i nulls min_size in - Nulls.add Possibly i removed) + | NotNull -> + Nulls.remove (if Nulls.is_full_set Possibly nulls then Possibly else Definitely) i nulls min_size + (* ... and value <> null, remove i from must_nulls_set and also from may_nulls_set if not top *) + | Null -> + Nulls.add (if i <. min_size then Definitely else Possibly) i nulls + (* i < minimal size and value = null, add i to must_nulls_set and may_nulls_set *) + (* i >= minimal size and value = null, add i only to may_nulls_set *) + | Maybe -> + let removed = Nulls.remove Possibly i nulls min_size in + Nulls.add Possibly i removed) | Some max_size -> (match Val.is_null v with | NotNull -> Nulls.remove Definitely i nulls min_size - (* if value <> null, remove i from must_nulls_set and may_nulls_set *) + (* if value <> null, remove i from must_nulls_set and may_nulls_set *) | Null when i <. min_size -> Nulls.add Definitely i nulls | Null when i <. max_size -> @@ -1114,43 +1114,43 @@ struct (* warn if index is (potentially) out of bounds *) array_oob_check (module Idx) (Nulls.get_set Possibly, size) (e, i); let nulls = match max_i with - (* if no maximum number in index interval *) - | None -> - (* ..., value = null *) - (if Val.is_null v = Null && Idx.maximal size = None then - match Idx.maximal size with - (* ... and there is no maximal size, modify may_nulls_set to top *) - | None -> Nulls.add_all Possibly nulls - (* ... and there is a maximal size, add all i from minimal index to maximal size to may_nulls_set *) - | Some max_size -> Nulls.add_interval Possibly (min_i, Z.pred max_size) nulls - (* ... and value <> null, only keep indexes < minimal index in must_nulls_set *) - else if Val.is_null v = NotNull then - Nulls.filter_musts (Z.gt min_i) min_size nulls - (*..., value unknown *) - else - match Idx.minimal size, Idx.maximal size with - (* ... and size unknown, modify both sets to top *) - | None, None -> Nulls.top () - (* ... and only minimal size known, remove all indexes < minimal size from must_nulls_set and modify may_nulls_set to top *) - | Some min_size, None -> - let nulls = Nulls.add_all Possibly nulls in - Nulls.filter_musts (Z.gt min_size) min_size nulls - (* ... and only maximal size known, modify must_nulls_set to top and add all i from minimal index to maximal size to may_nulls_set *) - | None, Some max_size -> - let nulls = Nulls.remove_all Possibly nulls in - Nulls.add_interval Possibly (min_i, Z.pred max_size) nulls - (* ... and size is known, remove all indexes < minimal size from must_nulls_set and add all i from minimal index to maximal size to may_nulls_set *) - | Some min_size, Some max_size -> - let nulls = Nulls.filter_musts (Z.gt min_size) min_size nulls in - Nulls.add_interval Possibly (min_i, Z.pred max_size) nulls - ) - | Some max_i when max_i >=. Z.zero -> - if min_i =. max_i then - set_exact_nulls min_i - else - set_interval min_i max_i - (* if maximum number in interval is invalid, i.e. negative, return tuple unmodified *) - | _ -> nulls + (* if no maximum number in index interval *) + | None -> + (* ..., value = null *) + (if Val.is_null v = Null && Idx.maximal size = None then + match Idx.maximal size with + (* ... and there is no maximal size, modify may_nulls_set to top *) + | None -> Nulls.add_all Possibly nulls + (* ... and there is a maximal size, add all i from minimal index to maximal size to may_nulls_set *) + | Some max_size -> Nulls.add_interval Possibly (min_i, Z.pred max_size) nulls + (* ... and value <> null, only keep indexes < minimal index in must_nulls_set *) + else if Val.is_null v = NotNull then + Nulls.filter_musts (Z.gt min_i) min_size nulls + (*..., value unknown *) + else + match Idx.minimal size, Idx.maximal size with + (* ... and size unknown, modify both sets to top *) + | None, None -> Nulls.top () + (* ... and only minimal size known, remove all indexes < minimal size from must_nulls_set and modify may_nulls_set to top *) + | Some min_size, None -> + let nulls = Nulls.add_all Possibly nulls in + Nulls.filter_musts (Z.gt min_size) min_size nulls + (* ... and only maximal size known, modify must_nulls_set to top and add all i from minimal index to maximal size to may_nulls_set *) + | None, Some max_size -> + let nulls = Nulls.remove_all Possibly nulls in + Nulls.add_interval Possibly (min_i, Z.pred max_size) nulls + (* ... and size is known, remove all indexes < minimal size from must_nulls_set and add all i from minimal index to maximal size to may_nulls_set *) + | Some min_size, Some max_size -> + let nulls = Nulls.filter_musts (Z.gt min_size) min_size nulls in + Nulls.add_interval Possibly (min_i, Z.pred max_size) nulls + ) + | Some max_i when max_i >=. Z.zero -> + if min_i =. max_i then + set_exact_nulls min_i + else + set_interval min_i max_i + (* if maximum number in interval is invalid, i.e. negative, return tuple unmodified *) + | _ -> nulls in (nulls, size) @@ -1236,7 +1236,7 @@ struct let nulls = if min_must_null =. min_may_null then Nulls.precise_singleton min_must_null - (* else return empty must_nulls_set and keep every index up to smallest index of must_nulls_set included in may_nulls_set *) + (* else return empty must_nulls_set and keep every index up to smallest index of must_nulls_set included in may_nulls_set *) else match Idx.maximal size with | Some max_size -> @@ -1263,59 +1263,59 @@ struct M.warn "Resulting string might not be null-terminated because src doesn't contain a null byte in the first n bytes" else (match min_must_null with - | Some min_must_null when not (min_must_null >=. n || min_must_null >. min_may_null) -> () - | _ -> - M.warn "Resulting string might not be null-terminated because src might not contain a null byte in the first n bytes" + | Some min_must_null when not (min_must_null >=. n || min_must_null >. min_may_null) -> () + | _ -> + M.warn "Resulting string might not be null-terminated because src might not contain a null byte in the first n bytes" ) in (match Idx.minimal size, Idx.maximal size with - | Some min_size, Some max_size -> - if n >. max_size then - warn_past_end "Array size is smaller than n bytes; can cause a buffer overflow" - else if n >. min_size then - warn_past_end "Array size might be smaller than n bytes; can cause a buffer overflow" - | Some min_size, None -> - if n >. min_size then - warn_past_end "Array size might be smaller than n bytes; can cause a buffer overflow" - | None, Some max_size -> - if n >. max_size then - warn_past_end "Array size is smaller than n bytes; can cause a buffer overflow" - | None, None -> ()); + | Some min_size, Some max_size -> + if n >. max_size then + warn_past_end "Array size is smaller than n bytes; can cause a buffer overflow" + else if n >. min_size then + warn_past_end "Array size might be smaller than n bytes; can cause a buffer overflow" + | Some min_size, None -> + if n >. min_size then + warn_past_end "Array size might be smaller than n bytes; can cause a buffer overflow" + | None, Some max_size -> + if n >. max_size then + warn_past_end "Array size is smaller than n bytes; can cause a buffer overflow" + | None, None -> ()); let nulls = - (* if definitely no null byte in array, i.e. must_nulls_set = may_nulls_set = empty set *) - if Nulls.is_empty Definitely nulls then - (warn_past_end - "Resulting string might not be null-terminated because src doesn't contain a null byte"; - match Idx.maximal size with - (* ... there *may* be null bytes from maximal size to n - 1 if maximal size < n (i.e. past end) *) - | Some max_size when Z.geq max_size Z.zero -> Nulls.add_interval Possibly (max_size, Z.pred n) nulls - | _ -> nulls) - (* if only must_nulls_set empty, remove indexes >= n from may_nulls_set and add all indexes from minimal may null index to n - 1; - * warn as in any case, resulting array not guaranteed to contain null byte *) - else if Nulls.is_empty Possibly nulls then - let min_may_null = Nulls.min_elem Possibly nulls in - warn_no_null None min_may_null; - if min_may_null =. Z.zero then - Nulls.add_all Possibly nulls - else - let nulls = Nulls.add_interval Possibly (min_may_null, Z.pred n) nulls in - Nulls.filter (fun x -> x <. n) nulls - else - let min_must_null = Nulls.min_elem Definitely nulls in - let min_may_null = Nulls.min_elem Possibly nulls in - (* warn if resulting array may not contain null byte *) - warn_no_null (Some min_must_null) min_may_null; - (* if min_must_null = min_may_null, remove indexes >= n and add all indexes from minimal must/may null to n - 1 in the sets *) - if min_must_null =. min_may_null then - if min_must_null =. Z.zero then - Nulls.full_set () + (* if definitely no null byte in array, i.e. must_nulls_set = may_nulls_set = empty set *) + if Nulls.is_empty Definitely nulls then + (warn_past_end + "Resulting string might not be null-terminated because src doesn't contain a null byte"; + match Idx.maximal size with + (* ... there *may* be null bytes from maximal size to n - 1 if maximal size < n (i.e. past end) *) + | Some max_size when Z.geq max_size Z.zero -> Nulls.add_interval Possibly (max_size, Z.pred n) nulls + | _ -> nulls) + (* if only must_nulls_set empty, remove indexes >= n from may_nulls_set and add all indexes from minimal may null index to n - 1; + * warn as in any case, resulting array not guaranteed to contain null byte *) + else if Nulls.is_empty Possibly nulls then + let min_may_null = Nulls.min_elem Possibly nulls in + warn_no_null None min_may_null; + if min_may_null =. Z.zero then + Nulls.add_all Possibly nulls else - let nulls = Nulls.add_interval Definitely (min_must_null, Z.pred n) nulls in let nulls = Nulls.add_interval Possibly (min_may_null, Z.pred n) nulls in Nulls.filter (fun x -> x <. n) nulls - else if min_may_null =. Z.zero then + else + let min_must_null = Nulls.min_elem Definitely nulls in + let min_may_null = Nulls.min_elem Possibly nulls in + (* warn if resulting array may not contain null byte *) + warn_no_null (Some min_must_null) min_may_null; + (* if min_must_null = min_may_null, remove indexes >= n and add all indexes from minimal must/may null to n - 1 in the sets *) + if min_must_null =. min_may_null then + if min_must_null =. Z.zero then + Nulls.full_set () + else + let nulls = Nulls.add_interval Definitely (min_must_null, Z.pred n) nulls in + let nulls = Nulls.add_interval Possibly (min_may_null, Z.pred n) nulls in + Nulls.filter (fun x -> x <. n) nulls + else if min_may_null =. Z.zero then Nulls.top () - else + else let nulls = Nulls.remove_all Possibly nulls in let nulls = Nulls.add_interval Possibly (min_may_null, Z.pred n) nulls in Nulls.filter (fun x -> x <. n) nulls @@ -1328,11 +1328,11 @@ struct (warn_past_end "Array doesn't contain a null byte: buffer overflow"; Idx.starting !Cil.kindOfSizeOf (BatOption.default Z.zero (Idx.minimal size)) ) - (* if only must_nulls_set empty, no guarantee that null ever encountered in array => return interval [minimal may null, inf) and *) + (* if only must_nulls_set empty, no guarantee that null ever encountered in array => return interval [minimal may null, inf) and *) else if Nulls.is_empty Possibly nulls then (warn_past_end "Array might not contain a null byte: potential buffer overflow"; Idx.starting !Cil.kindOfSizeOf (Nulls.min_elem Possibly nulls)) - (* else return interval [minimal may null, minimal must null] *) + (* else return interval [minimal may null, minimal must null] *) else Idx.of_interval !Cil.kindOfSizeOf (Nulls.min_elem Possibly nulls, Nulls.min_elem Definitely nulls) @@ -1441,13 +1441,13 @@ struct let update_sets min_size1 max_size1 minlen1 maxlen1 minlen2 (maxlen2: Z.t option) nulls2' = (* track any potential buffer overflow and issue warning if needed *) (if GobOption.exists (fun x -> x <=. (minlen1 +. minlen2)) max_size1 then - warn_past_end + warn_past_end "The length of the concatenation of the strings in src and dest is greater than the allocated size for dest" else - (match maxlen1, maxlen2 with - | Some maxlen1, Some maxlen2 when min_size1 >. (maxlen1 +. maxlen2) -> () - | _ -> warn_past_end - "The length of the concatenation of the strings in src and dest may be greater than the allocated size for dest") + (match maxlen1, maxlen2 with + | Some maxlen1, Some maxlen2 when min_size1 >. (maxlen1 +. maxlen2) -> () + | _ -> warn_past_end + "The length of the concatenation of the strings in src and dest may be greater than the allocated size for dest") ); (* if any must_nulls_set empty, result must_nulls_set also empty; * for all i1, i2 in may_nulls_set1, may_nulls_set2: add i1 + i2 if it is <= strlen(dest) + strlen(src) to new may_nulls_set @@ -1473,21 +1473,21 @@ struct (r, size1) | None when Nulls.may_can_benefit_from_filter nulls1 && Nulls.may_can_benefit_from_filter nulls2 -> (match maxlen1, maxlen2 with - | Some maxlen1, Some maxlen2-> - let nulls1_no_must = Nulls.remove_all Possibly nulls1 in - let r = - nulls1_no_must - (* filter ensures we have the concete representation *) - |> Nulls.filter (fun x -> x <=. (maxlen1 +. maxlen2)) - |> Nulls.elements Possibly - |> BatList.cartesian_product (Nulls.elements Possibly nulls2') - |> List.map (fun (i1, i2) -> i1 +. i2) - |> (fun x -> Nulls.add_list Possibly x (Nulls.filter (Z.lt (minlen1 +. minlen2)) nulls1_no_must)) - in - (r, size1) - | _ -> (Nulls.top (), size1)) + | Some maxlen1, Some maxlen2-> + let nulls1_no_must = Nulls.remove_all Possibly nulls1 in + let r = + nulls1_no_must + (* filter ensures we have the concete representation *) + |> Nulls.filter (fun x -> x <=. (maxlen1 +. maxlen2)) + |> Nulls.elements Possibly + |> BatList.cartesian_product (Nulls.elements Possibly nulls2') + |> List.map (fun (i1, i2) -> i1 +. i2) + |> (fun x -> Nulls.add_list Possibly x (Nulls.filter (Z.lt (minlen1 +. minlen2)) nulls1_no_must)) + in + (r, size1) + | _ -> (Nulls.top (), size1)) | _ -> (Nulls.top (), size1) - (* if minimal must null = minimal may null in ar1 and ar2, add them together and keep indexes > strlen(dest) + strlen(src) of ar1 *) + (* if minimal must null = minimal may null in ar1 and ar2, add them together and keep indexes > strlen(dest) + strlen(src) of ar1 *) else if Nulls.min_elem_precise nulls1 && Nulls.min_elem_precise nulls2' then let min_i1 = Nulls.min_elem Definitely nulls1 in let min_i2 = Nulls.min_elem Definitely nulls2' in @@ -1616,14 +1616,14 @@ struct let min_must1 = Nulls.min_elem Definitely nulls1 in let min_must2 = Nulls.min_elem Definitely nulls2 in if not (min_must1 =. min_must2) - && min_must1 =.(Nulls.min_elem Possibly nulls1) - && min_must2 =. (Nulls.min_elem Possibly nulls2) - && (BatOption.map_default (fun x -> min_must1 <. x || min_must2 <. x) true n) + && min_must1 =.(Nulls.min_elem Possibly nulls1) + && min_must2 =. (Nulls.min_elem Possibly nulls2) + && (BatOption.map_default (fun x -> min_must1 <. x || min_must2 <. x) true n) then (* if first null bytes are certain, have different indexes and are before index n if n present, return integer <> 0 *) Idx.of_excl_list IInt [Z.zero] else - Idx.top_of IInt + Idx.top_of IInt with Not_found -> Idx.top_of IInt in diff --git a/src/cdomains/nullByteSet.ml b/src/cdomains/nullByteSet.ml index 6a16b0b592..ff5d0270e0 100644 --- a/src/cdomains/nullByteSet.ml +++ b/src/cdomains/nullByteSet.ml @@ -148,10 +148,10 @@ module MustMaySet = struct match mode with | Definitely -> failwith "todo" | Possibly -> - if Z.equal l Z.zero && Z.geq u min_size then - (MustSet.top (), mays) - else - (MustSet.filter ~min_size (fun x -> (Z.lt x l || Z.gt x u) && Z.lt x min_size) musts, mays) + if Z.equal l Z.zero && Z.geq u min_size then + (MustSet.top (), mays) + else + (MustSet.filter ~min_size (fun x -> (Z.lt x l || Z.gt x u) && Z.lt x min_size) musts, mays) let add_all mode (musts, mays) = match mode with From ea83c30f1db59c6f0cd7922a25e225ef1e4c4475 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 13 Dec 2023 16:11:38 +0100 Subject: [PATCH 1244/1312] Be more conservative for `ioctl` --- src/util/library/libraryFunctions.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/library/libraryFunctions.ml b/src/util/library/libraryFunctions.ml index ee8d58d886..d260ebb070 100644 --- a/src/util/library/libraryFunctions.ml +++ b/src/util/library/libraryFunctions.ml @@ -750,7 +750,7 @@ let linux_kernel_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__kmalloc", special [__ "size" []; drop "flags" []] @@ fun size -> Malloc size); ("kzalloc", special [__ "size" []; drop "flags" []] @@ fun size -> Calloc {count = Cil.one; size}); ("usb_alloc_urb", special [__ "iso_packets" []; drop "mem_flags" []] @@ fun iso_packets -> Malloc MyCFG.unknown_exp); - ("ioctl", unknown (drop "fd" [] :: drop "request" [] :: VarArgs (drop' [r]))); + ("ioctl", unknown (drop "fd" [] :: drop "request" [] :: VarArgs (drop' [r_deep; w_deep]))); ] (** Goblint functions. *) From 7b38a7353750b8bb9ae94fd966b0107ddb36728b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 13 Dec 2023 20:56:35 +0000 Subject: [PATCH 1245/1312] Bump github/codeql-action from 2 to 3 Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2 to 3. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v2...v3) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/semgrep.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/semgrep.yml b/.github/workflows/semgrep.yml index bd2dfd285c..c22eee5181 100644 --- a/.github/workflows/semgrep.yml +++ b/.github/workflows/semgrep.yml @@ -22,7 +22,7 @@ jobs: run: semgrep scan --config .semgrep/ --sarif > semgrep.sarif - name: Upload SARIF file to GitHub Advanced Security Dashboard - uses: github/codeql-action/upload-sarif@v2 + uses: github/codeql-action/upload-sarif@v3 with: sarif_file: semgrep.sarif if: always() From 8a2a977ff5bf5807e800a836a0479d5f356e6608 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 14 Dec 2023 11:47:04 +0200 Subject: [PATCH 1246/1312] Do not use plain CIL printers in user messages --- src/analyses/base.ml | 2 +- src/analyses/baseInvariant.ml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 7cc937b201..46a54af2ba 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1878,7 +1878,7 @@ struct let invalidate ?(deep=true) ~ctx ask (gs:glob_fun) (st:store) (exps: exp list): store = if M.tracing && exps <> [] then M.tracel "invalidate" "Will invalidate expressions [%a]\n" (d_list ", " d_plainexp) exps; - if exps <> [] then M.info ~category:Imprecise "Invalidating expressions: %a" (d_list ", " d_plainexp) exps; + if exps <> [] then M.info ~category:Imprecise "Invalidating expressions: %a" (d_list ", " d_exp) exps; (* To invalidate a single address, we create a pair with its corresponding * top value. *) let invalidate_address st a = diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index 304d3e55ad..2c783edcf9 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -243,7 +243,7 @@ struct refine_lv_fallback ctx a gs st lval value tv | None -> if M.tracing then M.traceu "invariant" "Doing nothing.\n"; - M.debug ~category:Analyzer "Invariant failed: expression \"%a\" not understood." d_plainexp exp; + M.debug ~category:Analyzer "Invariant failed: expression \"%a\" not understood." d_exp exp; st let invariant ctx a gs st exp tv: D.t = From 4b77174ca1a21bf8c58a99b0f2e8de6d9a12455e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 14 Dec 2023 11:47:27 +0200 Subject: [PATCH 1247/1312] Make BaseInvariant fallback reason printing lazy --- src/analyses/baseInvariant.ml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index 2c783edcf9..f18eeed24f 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -248,7 +248,7 @@ struct let invariant ctx a gs st exp tv: D.t = let fallback reason st = - if M.tracing then M.tracel "inv" "Can't handle %a.\n%s\n" d_plainexp exp reason; + if M.tracing then M.tracel "inv" "Can't handle %a.\n%t\n" d_plainexp exp reason; invariant_fallback ctx a gs st exp tv in (* inverse values for binary operation a `op` b == c *) @@ -689,7 +689,7 @@ struct (* Mixed Float and Int cases should never happen, as there are no binary operators with one float and one int parameter ?!*) | Int _, Float _ | Float _, Int _ -> failwith "ill-typed program"; (* | Address a, Address b -> ... *) - | a1, a2 -> fallback (GobPretty.sprintf "binop: got abstract values that are not Int: %a and %a" VD.pretty a1 VD.pretty a2) st) + | a1, a2 -> fallback (fun () -> Pretty.dprintf "binop: got abstract values that are not Int: %a and %a" VD.pretty a1 VD.pretty a2) st) (* use closures to avoid unused casts *) in (match c_typed with | Int c -> invert_binary_op c ID.pretty (fun ik -> ID.cast_to ik c) (fun fk -> FD.of_int fk c) @@ -778,7 +778,7 @@ struct | TFloat (fk, _), FLongDouble | TFloat (FDouble as fk, _), FDouble | TFloat (FFloat as fk, _), FFloat -> inv_exp (Float (FD.cast_to fk c)) e st - | _ -> fallback ("CastE: incompatible types") st) + | _ -> fallback (fun () -> Pretty.text "CastE: incompatible types") st) | CastE ((TInt (ik, _)) as t, e), Int c | CastE ((TEnum ({ekind = ik; _ }, _)) as t, e), Int c -> (* Can only meet the t part of an Lval in e with c (unless we meet with all overflow possibilities)! Since there is no good way to do this, we only continue if e has no values outside of t. *) (match eval e st with @@ -791,11 +791,11 @@ struct let c' = ID.cast_to ik_e (ID.meet c (ID.cast_to ik (ID.top_of ik_e))) in (* TODO: cast without overflow, is this right for normal invariant? *) if M.tracing then M.tracel "inv" "cast: %a from %a to %a: i = %a; cast c = %a to %a = %a\n" d_exp e d_ikind ik_e d_ikind ik ID.pretty i ID.pretty c d_ikind ik_e ID.pretty c'; inv_exp (Int c') e st - | x -> fallback (GobPretty.sprintf "CastE: e did evaluate to Int, but the type did not match %a" CilType.Typ.pretty t) st + | x -> fallback (fun () -> Pretty.dprintf "CastE: e did evaluate to Int, but the type did not match %a" CilType.Typ.pretty t) st else - fallback (GobPretty.sprintf "CastE: %a evaluates to %a which is bigger than the type it is cast to which is %a" d_plainexp e ID.pretty i CilType.Typ.pretty t) st - | v -> fallback (GobPretty.sprintf "CastE: e did not evaluate to Int, but %a" VD.pretty v) st) - | e, _ -> fallback (GobPretty.sprintf "%a not implemented" d_plainexp e) st + fallback (fun () -> Pretty.dprintf "CastE: %a evaluates to %a which is bigger than the type it is cast to which is %a" d_plainexp e ID.pretty i CilType.Typ.pretty t) st + | v -> fallback (fun () -> Pretty.dprintf "CastE: e did not evaluate to Int, but %a" VD.pretty v) st) + | e, _ -> fallback (fun () -> Pretty.dprintf "%a not implemented" d_plainexp e) st in if eval_bool exp st = Some (not tv) then contra st (* we already know that the branch is dead *) else From 0dd43968f8bf44993bb52360b2eb830ce0adc9c4 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 14 Dec 2023 12:07:08 +0200 Subject: [PATCH 1248/1312] Make Offset.Type_of_error string construction lazy --- src/cdomains/offset.ml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/cdomains/offset.ml b/src/cdomains/offset.ml index 52cfe9eb41..62bab39eb7 100644 --- a/src/cdomains/offset.ml +++ b/src/cdomains/offset.ml @@ -142,15 +142,11 @@ struct | TPtr (t,_), `Index (i,o) -> type_of ~base:t o | TComp (ci,_), `Field (f,o) -> let fi = try getCompField ci f.fname - with Not_found -> - let s = GobPretty.sprintf "Addr.type_offset: field %s not found in type %a" f.fname d_plaintype t in - raise (Type_of_error (t, s)) + with Not_found -> raise (Type_of_error (t, show o)) in type_of ~base:fi.ftype o (* TODO: Why? Imprecise on zstd-thread-pool regression tests. *) (* | TComp _, `Index (_,o) -> type_of ~base:t o (* this happens (hmmer, perlbench). safe? *) *) - | t,o -> - let s = GobPretty.sprintf "Addr.type_offset: could not follow offset in type. type: %a, offset: %a" d_plaintype t pretty o in - raise (Type_of_error (t, s)) + | t, o -> raise (Type_of_error (t, show o)) let rec prefix (x: t) (y: t): t option = match x,y with | `Index (x, xs), `Index (y, ys) when Idx.equal x y -> prefix xs ys @@ -261,3 +257,9 @@ struct | `Index (i,o) -> Index (i, to_cil o) | `Field (f,o) -> Field (f, to_cil o) end + + +let () = Printexc.register_printer (function + | Type_of_error (t, o) -> Some (GobPretty.sprintf "Offset.Type_of_error(%a, %s)" d_plaintype t o) + | _ -> None (* for other exceptions *) + ) From 86ab2390a4fd2a84c0944b99e9e755d5ea329b7b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 14 Dec 2023 12:41:36 +0200 Subject: [PATCH 1249/1312] Promote cram tests after invalidating expressions output change --- tests/regression/04-mutex/49-type-invariants.t | 8 ++++---- tests/regression/04-mutex/77-type-nested-fields.t | 4 ++-- tests/regression/04-mutex/79-type-nested-fields-deep1.t | 4 ++-- tests/regression/04-mutex/80-type-nested-fields-deep2.t | 4 ++-- tests/regression/04-mutex/90-distribute-fields-type-1.t | 4 ++-- tests/regression/04-mutex/91-distribute-fields-type-2.t | 4 ++-- .../regression/04-mutex/92-distribute-fields-type-deep.t | 4 ++-- .../04-mutex/93-distribute-fields-type-global.t | 4 ++-- 8 files changed, 18 insertions(+), 18 deletions(-) diff --git a/tests/regression/04-mutex/49-type-invariants.t b/tests/regression/04-mutex/49-type-invariants.t index 4b8118eec1..b6c43d21bc 100644 --- a/tests/regression/04-mutex/49-type-invariants.t +++ b/tests/regression/04-mutex/49-type-invariants.t @@ -16,8 +16,8 @@ total lines: 7 [Info][Unsound] Write to unknown address: privatization is unsound. (49-type-invariants.c:21:3-21:21) [Info][Imprecise] INVALIDATING ALL GLOBALS! (49-type-invariants.c:21:3-21:21) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(s, NoOffset)) (49-type-invariants.c:21:3-21:21) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (49-type-invariants.c:21:3-21:21) + [Info][Imprecise] Invalidating expressions: & s (49-type-invariants.c:21:3-21:21) + [Info][Imprecise] Invalidating expressions: & tmp (49-type-invariants.c:21:3-21:21) [Error][Imprecise][Unsound] Function definition missing for getS (49-type-invariants.c:21:3-21:21) [Error][Imprecise][Unsound] Function definition missing @@ -39,7 +39,7 @@ total lines: 7 [Info][Unsound] Write to unknown address: privatization is unsound. (49-type-invariants.c:21:3-21:21) [Info][Imprecise] INVALIDATING ALL GLOBALS! (49-type-invariants.c:21:3-21:21) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(s, NoOffset)) (49-type-invariants.c:21:3-21:21) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (49-type-invariants.c:21:3-21:21) + [Info][Imprecise] Invalidating expressions: & s (49-type-invariants.c:21:3-21:21) + [Info][Imprecise] Invalidating expressions: & tmp (49-type-invariants.c:21:3-21:21) [Error][Imprecise][Unsound] Function definition missing for getS (49-type-invariants.c:21:3-21:21) [Error][Imprecise][Unsound] Function definition missing diff --git a/tests/regression/04-mutex/77-type-nested-fields.t b/tests/regression/04-mutex/77-type-nested-fields.t index 68d9cdb779..0ecf051578 100644 --- a/tests/regression/04-mutex/77-type-nested-fields.t +++ b/tests/regression/04-mutex/77-type-nested-fields.t @@ -18,9 +18,9 @@ [Info][Unsound] Write to unknown address: privatization is unsound. (77-type-nested-fields.c:31:3-31:20) [Info][Unsound] Write to unknown address: privatization is unsound. (77-type-nested-fields.c:38:3-38:22) [Info][Imprecise] INVALIDATING ALL GLOBALS! (77-type-nested-fields.c:31:3-31:20) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (77-type-nested-fields.c:31:3-31:20) + [Info][Imprecise] Invalidating expressions: & tmp (77-type-nested-fields.c:31:3-31:20) [Info][Imprecise] INVALIDATING ALL GLOBALS! (77-type-nested-fields.c:38:3-38:22) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (77-type-nested-fields.c:38:3-38:22) + [Info][Imprecise] Invalidating expressions: & tmp (77-type-nested-fields.c:38:3-38:22) [Error][Imprecise][Unsound] Function definition missing for getS (77-type-nested-fields.c:31:3-31:20) [Error][Imprecise][Unsound] Function definition missing for getT (77-type-nested-fields.c:38:3-38:22) [Error][Imprecise][Unsound] Function definition missing diff --git a/tests/regression/04-mutex/79-type-nested-fields-deep1.t b/tests/regression/04-mutex/79-type-nested-fields-deep1.t index 85f7bfb709..611a70a7c3 100644 --- a/tests/regression/04-mutex/79-type-nested-fields-deep1.t +++ b/tests/regression/04-mutex/79-type-nested-fields-deep1.t @@ -18,9 +18,9 @@ [Info][Unsound] Write to unknown address: privatization is unsound. (79-type-nested-fields-deep1.c:36:3-36:20) [Info][Unsound] Write to unknown address: privatization is unsound. (79-type-nested-fields-deep1.c:43:3-43:24) [Info][Imprecise] INVALIDATING ALL GLOBALS! (79-type-nested-fields-deep1.c:36:3-36:20) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (79-type-nested-fields-deep1.c:36:3-36:20) + [Info][Imprecise] Invalidating expressions: & tmp (79-type-nested-fields-deep1.c:36:3-36:20) [Info][Imprecise] INVALIDATING ALL GLOBALS! (79-type-nested-fields-deep1.c:43:3-43:24) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (79-type-nested-fields-deep1.c:43:3-43:24) + [Info][Imprecise] Invalidating expressions: & tmp (79-type-nested-fields-deep1.c:43:3-43:24) [Error][Imprecise][Unsound] Function definition missing for getS (79-type-nested-fields-deep1.c:36:3-36:20) [Error][Imprecise][Unsound] Function definition missing for getU (79-type-nested-fields-deep1.c:43:3-43:24) [Error][Imprecise][Unsound] Function definition missing diff --git a/tests/regression/04-mutex/80-type-nested-fields-deep2.t b/tests/regression/04-mutex/80-type-nested-fields-deep2.t index a2e9e2ab15..7ddbdc4fd7 100644 --- a/tests/regression/04-mutex/80-type-nested-fields-deep2.t +++ b/tests/regression/04-mutex/80-type-nested-fields-deep2.t @@ -18,9 +18,9 @@ [Info][Unsound] Write to unknown address: privatization is unsound. (80-type-nested-fields-deep2.c:36:3-36:22) [Info][Unsound] Write to unknown address: privatization is unsound. (80-type-nested-fields-deep2.c:43:3-43:24) [Info][Imprecise] INVALIDATING ALL GLOBALS! (80-type-nested-fields-deep2.c:36:3-36:22) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (80-type-nested-fields-deep2.c:36:3-36:22) + [Info][Imprecise] Invalidating expressions: & tmp (80-type-nested-fields-deep2.c:36:3-36:22) [Info][Imprecise] INVALIDATING ALL GLOBALS! (80-type-nested-fields-deep2.c:43:3-43:24) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (80-type-nested-fields-deep2.c:43:3-43:24) + [Info][Imprecise] Invalidating expressions: & tmp (80-type-nested-fields-deep2.c:43:3-43:24) [Error][Imprecise][Unsound] Function definition missing for getT (80-type-nested-fields-deep2.c:36:3-36:22) [Error][Imprecise][Unsound] Function definition missing for getU (80-type-nested-fields-deep2.c:43:3-43:24) [Error][Imprecise][Unsound] Function definition missing diff --git a/tests/regression/04-mutex/90-distribute-fields-type-1.t b/tests/regression/04-mutex/90-distribute-fields-type-1.t index a3b5faf083..587e943b36 100644 --- a/tests/regression/04-mutex/90-distribute-fields-type-1.t +++ b/tests/regression/04-mutex/90-distribute-fields-type-1.t @@ -20,9 +20,9 @@ [Info][Unsound] Write to unknown address: privatization is unsound. (90-distribute-fields-type-1.c:31:3-31:20) [Info][Unsound] Write to unknown address: privatization is unsound. (90-distribute-fields-type-1.c:39:3-39:17) [Info][Imprecise] INVALIDATING ALL GLOBALS! (90-distribute-fields-type-1.c:31:3-31:20) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (90-distribute-fields-type-1.c:31:3-31:20) + [Info][Imprecise] Invalidating expressions: & tmp (90-distribute-fields-type-1.c:31:3-31:20) [Info][Imprecise] INVALIDATING ALL GLOBALS! (90-distribute-fields-type-1.c:39:3-39:17) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (90-distribute-fields-type-1.c:39:3-39:17) + [Info][Imprecise] Invalidating expressions: & tmp (90-distribute-fields-type-1.c:39:3-39:17) [Error][Imprecise][Unsound] Function definition missing for getS (90-distribute-fields-type-1.c:31:3-31:20) [Error][Imprecise][Unsound] Function definition missing for getT (90-distribute-fields-type-1.c:39:3-39:17) [Error][Imprecise][Unsound] Function definition missing diff --git a/tests/regression/04-mutex/91-distribute-fields-type-2.t b/tests/regression/04-mutex/91-distribute-fields-type-2.t index 5773245114..afb01fdced 100644 --- a/tests/regression/04-mutex/91-distribute-fields-type-2.t +++ b/tests/regression/04-mutex/91-distribute-fields-type-2.t @@ -20,9 +20,9 @@ [Info][Unsound] Write to unknown address: privatization is unsound. (91-distribute-fields-type-2.c:32:3-32:17) [Info][Unsound] Write to unknown address: privatization is unsound. (91-distribute-fields-type-2.c:40:3-40:17) [Info][Imprecise] INVALIDATING ALL GLOBALS! (91-distribute-fields-type-2.c:32:3-32:17) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (91-distribute-fields-type-2.c:32:3-32:17) + [Info][Imprecise] Invalidating expressions: & tmp (91-distribute-fields-type-2.c:32:3-32:17) [Info][Imprecise] INVALIDATING ALL GLOBALS! (91-distribute-fields-type-2.c:40:3-40:17) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (91-distribute-fields-type-2.c:40:3-40:17) + [Info][Imprecise] Invalidating expressions: & tmp (91-distribute-fields-type-2.c:40:3-40:17) [Error][Imprecise][Unsound] Function definition missing for getS (91-distribute-fields-type-2.c:32:3-32:17) [Error][Imprecise][Unsound] Function definition missing for getT (91-distribute-fields-type-2.c:40:3-40:17) [Error][Imprecise][Unsound] Function definition missing diff --git a/tests/regression/04-mutex/92-distribute-fields-type-deep.t b/tests/regression/04-mutex/92-distribute-fields-type-deep.t index 798374d63c..1748b245e2 100644 --- a/tests/regression/04-mutex/92-distribute-fields-type-deep.t +++ b/tests/regression/04-mutex/92-distribute-fields-type-deep.t @@ -20,9 +20,9 @@ [Info][Unsound] Write to unknown address: privatization is unsound. (92-distribute-fields-type-deep.c:36:3-36:20) [Info][Unsound] Write to unknown address: privatization is unsound. (92-distribute-fields-type-deep.c:44:3-44:17) [Info][Imprecise] INVALIDATING ALL GLOBALS! (92-distribute-fields-type-deep.c:36:3-36:20) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (92-distribute-fields-type-deep.c:36:3-36:20) + [Info][Imprecise] Invalidating expressions: & tmp (92-distribute-fields-type-deep.c:36:3-36:20) [Info][Imprecise] INVALIDATING ALL GLOBALS! (92-distribute-fields-type-deep.c:44:3-44:17) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (92-distribute-fields-type-deep.c:44:3-44:17) + [Info][Imprecise] Invalidating expressions: & tmp (92-distribute-fields-type-deep.c:44:3-44:17) [Error][Imprecise][Unsound] Function definition missing for getS (92-distribute-fields-type-deep.c:36:3-36:20) [Error][Imprecise][Unsound] Function definition missing for getU (92-distribute-fields-type-deep.c:44:3-44:17) [Error][Imprecise][Unsound] Function definition missing diff --git a/tests/regression/04-mutex/93-distribute-fields-type-global.t b/tests/regression/04-mutex/93-distribute-fields-type-global.t index 07999854ff..50c72aa289 100644 --- a/tests/regression/04-mutex/93-distribute-fields-type-global.t +++ b/tests/regression/04-mutex/93-distribute-fields-type-global.t @@ -18,7 +18,7 @@ total lines: 7 [Info][Unsound] Write to unknown address: privatization is unsound. (93-distribute-fields-type-global.c:13:3-13:29) [Info][Imprecise] INVALIDATING ALL GLOBALS! (93-distribute-fields-type-global.c:13:3-13:29) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(s, NoOffset)) (93-distribute-fields-type-global.c:13:3-13:29) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (93-distribute-fields-type-global.c:13:3-13:29) + [Info][Imprecise] Invalidating expressions: & s (93-distribute-fields-type-global.c:13:3-13:29) + [Info][Imprecise] Invalidating expressions: & tmp (93-distribute-fields-type-global.c:13:3-13:29) [Error][Imprecise][Unsound] Function definition missing for getS (93-distribute-fields-type-global.c:13:3-13:29) [Error][Imprecise][Unsound] Function definition missing From f4d6197ee0ea2520b71036557e56e8f90cb635d5 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 14 Dec 2023 15:12:09 +0200 Subject: [PATCH 1250/1312] Add Printable.EitherConf --- src/analyses/commonPriv.ml | 2 +- src/common/domains/printable.ml | 34 ++++++++++++++++++++++++--------- src/framework/analyses.ml | 4 ++-- src/framework/constraints.ml | 2 +- 4 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index 73a2e75de1..87490a814a 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -173,7 +173,7 @@ struct module V = struct - include Printable.Either (MutexGlobals.V) (TID) + include Printable.EitherConf (struct let expand1 = false let expand2 = true end) (MutexGlobals.V) (TID) let mutex x = `Left (MutexGlobals.V.mutex x) let mutex_inits = `Left MutexGlobals.V.mutex_inits let global x = `Left (MutexGlobals.V.global x) diff --git a/src/common/domains/printable.ml b/src/common/domains/printable.ml index cc01718ee8..a1f33efdad 100644 --- a/src/common/domains/printable.ml +++ b/src/common/domains/printable.ml @@ -244,35 +244,51 @@ struct ] (* S TODO: decide frequencies *) end -module Either (Base1: S) (Base2: S) = +module type EitherConf = +sig + val expand1: bool + val expand2: bool +end + +module EitherConf (Conf: EitherConf) (Base1: S) (Base2: S) = struct type t = [`Left of Base1.t | `Right of Base2.t] [@@deriving eq, ord, hash] include Std let pretty () (state:t) = match state with - | `Left n -> Pretty.dprintf "%s:%a" (Base1.name ()) Base1.pretty n - | `Right n -> Pretty.dprintf "%s:%a" (Base2.name ()) Base2.pretty n + | `Left n when Conf.expand1 -> Pretty.dprintf "%s:%a" (Base1.name ()) Base1.pretty n + | `Left n -> Base1.pretty () n + | `Right n when Conf.expand2 -> Pretty.dprintf "%s:%a" (Base2.name ()) Base2.pretty n + | `Right n -> Base2.pretty () n let show state = match state with - | `Left n -> (Base1.name ()) ^ ":" ^ Base1.show n - | `Right n -> (Base2.name ()) ^ ":" ^ Base2.show n + | `Left n when Conf.expand1 -> (Base1.name ()) ^ ":" ^ Base1.show n + | `Left n -> Base1.show n + | `Right n when Conf.expand2 -> (Base2.name ()) ^ ":" ^ Base2.show n + | `Right n -> Base2.show n let name () = "either " ^ Base1.name () ^ " or " ^ Base2.name () let printXml f = function - | `Left x -> BatPrintf.fprintf f "\n\nLeft\n\n%a\n\n" Base1.printXml x - | `Right x -> BatPrintf.fprintf f "\n\nRight\n\n%a\n\n" Base2.printXml x + | `Left x when Conf.expand1 -> BatPrintf.fprintf f "\n\nLeft\n\n%a\n\n" Base1.printXml x + | `Left x -> Base1.printXml f x + | `Right x when Conf.expand2 -> BatPrintf.fprintf f "\n\nRight\n\n%a\n\n" Base2.printXml x + | `Right x -> Base2.printXml f x let to_yojson = function - | `Left x -> `Assoc [ Base1.name (), Base1.to_yojson x ] - | `Right x -> `Assoc [ Base2.name (), Base2.to_yojson x ] + | `Left x when Conf.expand1 -> `Assoc [ Base1.name (), Base1.to_yojson x ] + | `Left x -> Base1.to_yojson x + | `Right x when Conf.expand2 -> `Assoc [ Base2.name (), Base2.to_yojson x ] + | `Right x -> Base2.to_yojson x let relift = function | `Left x -> `Left (Base1.relift x) | `Right x -> `Right (Base2.relift x) end +module Either = EitherConf (struct let expand1 = true let expand2 = true end) + module Either3 (Base1: S) (Base2: S) (Base3: S) = struct type t = [`Left of Base1.t | `Middle of Base2.t | `Right of Base3.t] [@@deriving eq, ord, hash] diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index a37a3043c2..44f1f1894e 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -74,7 +74,7 @@ end module GVarF (V: SpecSysVar) = struct - include Printable.Either (V) (CilType.Fundec) + include Printable.EitherConf (struct let expand1 = false let expand2 = true end) (V) (CilType.Fundec) let name () = "FromSpec" let spec x = `Left x let contexts x = `Right x @@ -90,7 +90,7 @@ end module GVarFC (V:SpecSysVar) (C:Printable.S) = struct - include Printable.Either (V) (Printable.Prod (CilType.Fundec) (C)) + include Printable.EitherConf (struct let expand1 = false let expand2 = true end) (V) (Printable.Prod (CilType.Fundec) (C)) let name () = "FromSpec" let spec x = `Left x let call (x, c) = `Right (x, c) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 77d3a38186..25b2060e0c 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1338,7 +1338,7 @@ struct module V = struct - include Printable.Either (S.V) (Node) + include Printable.EitherConf (struct let expand1 = false let expand2 = true end) (S.V) (Node) let name () = "DeadBranch" let s x = `Left x let node x = `Right x From 2509d22f2b4254ca69e19dcd0f6cca9a026985aa Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 14 Dec 2023 15:29:53 +0200 Subject: [PATCH 1251/1312] Add Printable.Either3Conf --- src/common/domains/printable.ml | 46 +++++++++++++++++++++++---------- src/framework/constraints.ml | 2 +- 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/src/common/domains/printable.ml b/src/common/domains/printable.ml index a1f33efdad..8311dd2ef0 100644 --- a/src/common/domains/printable.ml +++ b/src/common/domains/printable.ml @@ -289,33 +289,51 @@ end module Either = EitherConf (struct let expand1 = true let expand2 = true end) -module Either3 (Base1: S) (Base2: S) (Base3: S) = +module type Either3Conf = +sig + include EitherConf + val expand3: bool +end + +module Either3Conf (Conf: Either3Conf) (Base1: S) (Base2: S) (Base3: S) = struct type t = [`Left of Base1.t | `Middle of Base2.t | `Right of Base3.t] [@@deriving eq, ord, hash] include Std let pretty () (state:t) = match state with - | `Left n -> Pretty.dprintf "%s:%a" (Base1.name ()) Base1.pretty n - | `Middle n -> Pretty.dprintf "%s:%a" (Base2.name ()) Base2.pretty n - | `Right n -> Pretty.dprintf "%s:%a" (Base3.name ()) Base3.pretty n + | `Left n when Conf.expand1 -> Pretty.dprintf "%s:%a" (Base1.name ()) Base1.pretty n + | `Left n -> Base1.pretty () n + | `Middle n when Conf.expand2 -> Pretty.dprintf "%s:%a" (Base2.name ()) Base2.pretty n + | `Middle n -> Base2.pretty () n + | `Right n when Conf.expand3 -> Pretty.dprintf "%s:%a" (Base3.name ()) Base3.pretty n + | `Right n -> Base3.pretty () n let show state = match state with - | `Left n -> (Base1.name ()) ^ ":" ^ Base1.show n - | `Middle n -> (Base2.name ()) ^ ":" ^ Base2.show n - | `Right n -> (Base3.name ()) ^ ":" ^ Base3.show n + | `Left n when Conf.expand1 -> (Base1.name ()) ^ ":" ^ Base1.show n + | `Left n -> Base1.show n + | `Middle n when Conf.expand2 -> (Base2.name ()) ^ ":" ^ Base2.show n + | `Middle n -> Base2.show n + | `Right n when Conf.expand3 -> (Base3.name ()) ^ ":" ^ Base3.show n + | `Right n -> Base3.show n let name () = "either " ^ Base1.name () ^ " or " ^ Base2.name () ^ " or " ^ Base3.name () let printXml f = function - | `Left x -> BatPrintf.fprintf f "\n\nLeft\n\n%a\n\n" Base1.printXml x - | `Middle x -> BatPrintf.fprintf f "\n\nMiddle\n\n%a\n\n" Base2.printXml x - | `Right x -> BatPrintf.fprintf f "\n\nRight\n\n%a\n\n" Base3.printXml x + | `Left x when Conf.expand1 -> BatPrintf.fprintf f "\n\nLeft\n\n%a\n\n" Base1.printXml x + | `Left x -> Base1.printXml f x + | `Middle x when Conf.expand2 -> BatPrintf.fprintf f "\n\nMiddle\n\n%a\n\n" Base2.printXml x + | `Middle x -> Base2.printXml f x + | `Right x when Conf.expand3 -> BatPrintf.fprintf f "\n\nRight\n\n%a\n\n" Base3.printXml x + | `Right x -> Base3.printXml f x let to_yojson = function - | `Left x -> `Assoc [ Base1.name (), Base1.to_yojson x ] - | `Middle x -> `Assoc [ Base2.name (), Base2.to_yojson x ] - | `Right x -> `Assoc [ Base3.name (), Base3.to_yojson x ] + | `Left x when Conf.expand1 -> `Assoc [ Base1.name (), Base1.to_yojson x ] + | `Left x -> Base1.to_yojson x + | `Middle x when Conf.expand2 -> `Assoc [ Base2.name (), Base2.to_yojson x ] + | `Middle x -> Base2.to_yojson x + | `Right x when Conf.expand3 -> `Assoc [ Base3.name (), Base3.to_yojson x ] + | `Right x -> Base3.to_yojson x let relift = function | `Left x -> `Left (Base1.relift x) @@ -323,6 +341,8 @@ struct | `Right x -> `Right (Base3.relift x) end +module Either3 = Either3Conf (struct let expand1 = true let expand2 = true let expand3 = true end) + module Option (Base: S) (N: Name) = struct type t = Base.t option [@@deriving eq, ord, hash] diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 25b2060e0c..ee1ea00a01 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1472,7 +1472,7 @@ struct module V = struct - include Printable.Either3 (S.V) (Printable.Prod (Node) (C)) (Printable.Prod (CilType.Fundec) (C)) + include Printable.Either3Conf (struct let expand1 = false let expand2 = true let expand3 = true end) (S.V) (Printable.Prod (Node) (C)) (Printable.Prod (CilType.Fundec) (C)) let name () = "longjmp" let s x = `Left x let longjmpto x = `Middle x From 38942f96edb2cca3143ff66d19d2ba12ecc0b2fa Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 14 Dec 2023 15:32:20 +0200 Subject: [PATCH 1252/1312] Remove variant name duplication in privatizations --- src/analyses/basePriv.ml | 12 +----------- src/analyses/commonPriv.ml | 2 -- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index 0126449413..72854d474d 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -544,7 +544,7 @@ struct ) ) else ( - if ConcDomain.ThreadSet.is_top tids then + if ConcDomain.ThreadSet.is_top tids then st else match ConcDomain.ThreadSet.elements tids with @@ -660,21 +660,11 @@ struct struct include VarinfoV (* [g]' *) let name () = "unprotected" - let show x = show x ^ ":unprotected" (* distinguishable variant names for html *) - include Printable.SimpleShow (struct - type nonrec t = t - let show = show - end) end module VProt = struct include VarinfoV (* [g] *) let name () = "protected" - let show x = show x ^ ":protected" (* distinguishable variant names for html *) - include Printable.SimpleShow (struct - type nonrec t = t - let show = show - end) end module V = struct diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index 87490a814a..0453862bc0 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -74,14 +74,12 @@ struct struct include LockDomain.Addr let name () = "mutex" - let show x = show x ^ ":mutex" (* distinguishable variant names for html *) end module VMutexInits = Printable.UnitConf (struct let name = "MUTEX_INITS" end) module VGlobal = struct include VarinfoV let name () = "global" - let show x = show x ^ ":global" (* distinguishable variant names for html *) end module V = struct From 3d5c65db7de3912c889193132208846d2c990ff9 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 14 Dec 2023 15:48:07 +0200 Subject: [PATCH 1253/1312] Add Lattice.Lift2Conf --- src/analyses/base.ml | 2 +- src/analyses/basePriv.ml | 2 +- src/analyses/commonPriv.ml | 2 +- src/analyses/mutexAnalysis.ml | 2 +- src/analyses/raceAnalysis.ml | 2 +- src/common/domains/printable.ml | 18 +++++++++++++----- src/domain/lattice.ml | 6 ++++-- 7 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 7cc937b201..8c4bb67b0b 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -54,7 +54,7 @@ struct module G = struct - include Lattice.Lift2 (Priv.G) (VD) (Printable.DefaultNames) + include Lattice.Lift2Conf (struct let expand1 = false let expand2 = false end) (Priv.G) (VD) (Printable.DefaultNames) let priv = function | `Bot -> Priv.G.bot () diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index 72854d474d..b486dfd552 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -799,7 +799,7 @@ struct struct (* weak: G -> (2^M -> WeakRange) *) (* sync: M -> (2^M -> SyncRange) *) - include Lattice.Lift2 (GWeak) (GSync) (Printable.DefaultNames) + include Lattice.Lift2Conf (struct let expand1 = false let expand2 = false end) (GWeak) (GSync) (Printable.DefaultNames) let weak = function | `Bot -> GWeak.bot () diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index 0453862bc0..1bf03581c2 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -198,7 +198,7 @@ struct module G = struct - include Lattice.Lift2 (GMutex) (GThread) (Printable.DefaultNames) + include Lattice.Lift2Conf (struct let expand1 = false let expand2 = false end) (GMutex) (GThread) (Printable.DefaultNames) let mutex = function | `Bot -> GMutex.bot () diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index ee050f55ca..1b52f5dd40 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -132,7 +132,7 @@ struct module G = struct - include Lattice.Lift2 (GProtecting) (GProtected) (Printable.DefaultNames) + include Lattice.Lift2Conf (struct let expand1 = false let expand2 = false end) (GProtecting) (GProtected) (Printable.DefaultNames) let protecting = function | `Bot -> GProtecting.bot () diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index 9c2272fabb..241bcb14f8 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -194,7 +194,7 @@ struct module G = struct - include Lattice.Lift2 (OffsetTrie) (MemoSet) (Printable.DefaultNames) + include Lattice.Lift2Conf (struct let expand1 = false let expand2 = false end) (OffsetTrie) (MemoSet) (Printable.DefaultNames) let access = function | `Bot -> OffsetTrie.bot () diff --git a/src/common/domains/printable.ml b/src/common/domains/printable.ml index 8311dd2ef0..882cb30bf5 100644 --- a/src/common/domains/printable.ml +++ b/src/common/domains/printable.ml @@ -370,7 +370,7 @@ struct let relift = Option.map Base.relift end -module Lift2 (Base1: S) (Base2: S) (N: LiftingNames) = +module Lift2Conf (Conf: EitherConf) (Base1: S) (Base2: S) (N: LiftingNames) = struct type t = [`Bot | `Lifted1 of Base1.t | `Lifted2 of Base2.t | `Top] [@@deriving eq, ord, hash] include Std @@ -378,6 +378,7 @@ struct let pretty () (state:t) = match state with + (* TODO: expand *) | `Lifted1 n -> Base1.pretty () n | `Lifted2 n -> Base2.pretty () n | `Bot -> text bot_name @@ -385,6 +386,7 @@ struct let show state = match state with + (* TODO: expand *) | `Lifted1 n -> Base1.show n | `Lifted2 n -> Base2.show n | `Bot -> bot_name @@ -399,16 +401,22 @@ struct let printXml f = function | `Bot -> BatPrintf.fprintf f "\n\n%s\n\n\n" N.bot_name | `Top -> BatPrintf.fprintf f "\n\n%s\n\n\n" N.top_name - | `Lifted1 x -> BatPrintf.fprintf f "\n\n\nLifted1\n\n%a\n\n" Base1.printXml x - | `Lifted2 x -> BatPrintf.fprintf f "\n\n\nLifted2\n\n%a\n\n" Base2.printXml x + | `Lifted1 x when Conf.expand1 -> BatPrintf.fprintf f "\n\n\nLifted1\n\n%a\n\n" Base1.printXml x + | `Lifted1 x -> Base1.printXml f x + | `Lifted2 x when Conf.expand2 -> BatPrintf.fprintf f "\n\n\nLifted2\n\n%a\n\n" Base2.printXml x + | `Lifted2 x -> Base2.printXml f x let to_yojson = function | `Bot -> `String N.bot_name | `Top -> `String N.top_name - | `Lifted1 x -> `Assoc [ Base1.name (), Base1.to_yojson x ] - | `Lifted2 x -> `Assoc [ Base2.name (), Base2.to_yojson x ] + | `Lifted1 x when Conf.expand1 -> `Assoc [ Base1.name (), Base1.to_yojson x ] + | `Lifted1 x -> Base1.to_yojson x + | `Lifted2 x when Conf.expand2 -> `Assoc [ Base2.name (), Base2.to_yojson x ] + | `Lifted2 x -> Base2.to_yojson x end +module Lift2 = Lift2Conf (struct let expand1 = true let expand2 = true end) + module type ProdConfiguration = sig val expand_fst: bool diff --git a/src/domain/lattice.ml b/src/domain/lattice.ml index 9ea3f74635..448f801ec1 100644 --- a/src/domain/lattice.ml +++ b/src/domain/lattice.ml @@ -336,9 +336,9 @@ struct | _ -> x end -module Lift2 (Base1: S) (Base2: S) (N: Printable.LiftingNames) = +module Lift2Conf (Conf: Printable.EitherConf) (Base1: S) (Base2: S) (N: Printable.LiftingNames) = struct - include Printable.Lift2 (Base1) (Base2) (N) + include Printable.Lift2Conf (Conf) (Base1) (Base2) (N) let bot () = `Bot let is_bot x = x = `Bot @@ -408,6 +408,8 @@ struct end +module Lift2 = Lift2Conf (struct let expand1 = true let expand2 = true end) + module ProdConf (C: Printable.ProdConfiguration) (Base1: S) (Base2: S) = struct include Printable.ProdConf (C) (Base1) (Base2) From b71518c7d51ab0bf9444d062ff305895ede10e73 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 14 Dec 2023 16:18:03 +0200 Subject: [PATCH 1254/1312] Refactor Printable.LiftingNames --- src/analyses/base.ml | 2 +- src/analyses/basePriv.ml | 2 +- src/analyses/commonPriv.ml | 2 +- src/analyses/loopTermination.ml | 2 +- src/analyses/mCPRegistry.ml | 2 +- src/analyses/mutexAnalysis.ml | 2 +- src/analyses/raceAnalysis.ml | 2 +- src/analyses/threadId.ml | 2 +- src/analyses/tutorials/signs.ml | 2 +- src/analyses/wrapperFunctionAnalysis0.ml | 5 +- src/cdomains/intDomain.ml | 10 ++-- src/cdomains/mutexAttrDomain.ml | 2 +- src/cdomains/regionDomain.ml | 2 +- src/cdomains/stackDomain.ml | 2 +- src/cdomains/threadIdDomain.ml | 5 +- src/cdomains/unionDomain.ml | 5 +- src/common/domains/printable.ml | 60 ++++++++++++++---------- src/domain/boolDomain.ml | 5 +- src/domain/lattice.ml | 18 +++---- src/domains/invariant.ml | 3 +- src/domains/queries.ml | 15 +++--- src/domains/valueDomainQueries.ml | 2 +- src/framework/analyses.ml | 9 ++-- src/framework/constraints.ml | 12 ++--- src/util/library/libraryDesc.ml | 5 +- src/witness/observerAnalysis.ml | 2 +- 26 files changed, 101 insertions(+), 79 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 8c4bb67b0b..92ddf3f12b 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -54,7 +54,7 @@ struct module G = struct - include Lattice.Lift2Conf (struct let expand1 = false let expand2 = false end) (Priv.G) (VD) (Printable.DefaultNames) + include Lattice.Lift2Conf (struct include Printable.DefaultConf let expand1 = false let expand2 = false end) (Priv.G) (VD) let priv = function | `Bot -> Priv.G.bot () diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index b486dfd552..10deaa4d16 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -799,7 +799,7 @@ struct struct (* weak: G -> (2^M -> WeakRange) *) (* sync: M -> (2^M -> SyncRange) *) - include Lattice.Lift2Conf (struct let expand1 = false let expand2 = false end) (GWeak) (GSync) (Printable.DefaultNames) + include Lattice.Lift2Conf (struct include Printable.DefaultConf let expand1 = false let expand2 = false end) (GWeak) (GSync) let weak = function | `Bot -> GWeak.bot () diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index 1bf03581c2..35b801e32b 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -198,7 +198,7 @@ struct module G = struct - include Lattice.Lift2Conf (struct let expand1 = false let expand2 = false end) (GMutex) (GThread) (Printable.DefaultNames) + include Lattice.Lift2Conf (struct include Printable.DefaultConf let expand1 = false let expand2 = false end) (GMutex) (GThread) let mutex = function | `Bot -> GMutex.bot () diff --git a/src/analyses/loopTermination.ml b/src/analyses/loopTermination.ml index 10e0f5c5f4..66cbd5772f 100644 --- a/src/analyses/loopTermination.ml +++ b/src/analyses/loopTermination.ml @@ -19,7 +19,7 @@ let check_bounded ctx varinfo = (** We want to record termination information of loops and use the loop * statements for that. We use this lifting because we need to have a * lattice. *) -module Statements = Lattice.Flat (CilType.Stmt) (Printable.DefaultNames) +module Statements = Lattice.Flat (Printable.DefaultConf) (CilType.Stmt) (** The termination analysis considering loops and gotos *) module Spec : Analyses.MCPSpec = diff --git a/src/analyses/mCPRegistry.ml b/src/analyses/mCPRegistry.ml index 5d0174d44c..a685b31798 100644 --- a/src/analyses/mCPRegistry.ml +++ b/src/analyses/mCPRegistry.ml @@ -426,7 +426,7 @@ end module DomVariantLattice (DLSpec : DomainListLatticeSpec) = struct - include Lattice.Lift (DomVariantLattice0 (DLSpec)) (Printable.DefaultNames) + include Lattice.Lift (Printable.DefaultConf) (DomVariantLattice0 (DLSpec)) let name () = "MCP.G" end diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 1b52f5dd40..a13c8d6bfd 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -132,7 +132,7 @@ struct module G = struct - include Lattice.Lift2Conf (struct let expand1 = false let expand2 = false end) (GProtecting) (GProtected) (Printable.DefaultNames) + include Lattice.Lift2Conf (struct include Printable.DefaultConf let expand1 = false let expand2 = false end) (GProtecting) (GProtected) let protecting = function | `Bot -> GProtecting.bot () diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index 241bcb14f8..f35e6756a1 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -194,7 +194,7 @@ struct module G = struct - include Lattice.Lift2Conf (struct let expand1 = false let expand2 = false end) (OffsetTrie) (MemoSet) (Printable.DefaultNames) + include Lattice.Lift2Conf (struct include Printable.DefaultConf let expand1 = false let expand2 = false end) (OffsetTrie) (MemoSet) let access = function | `Bot -> OffsetTrie.bot () diff --git a/src/analyses/threadId.ml b/src/analyses/threadId.ml index da2c688ad1..f954077836 100644 --- a/src/analyses/threadId.ml +++ b/src/analyses/threadId.ml @@ -31,7 +31,7 @@ struct module N = struct - include Lattice.Flat (VNI) (struct let bot_name = "unknown node" let top_name = "unknown node" end) + include Lattice.Flat (struct include Printable.DefaultConf let bot_name = "unknown node" let top_name = "unknown node" end) (VNI) let name () = "wrapper call" end module TD = Thread.D diff --git a/src/analyses/tutorials/signs.ml b/src/analyses/tutorials/signs.ml index 31168df86a..6ba720d0ea 100644 --- a/src/analyses/tutorials/signs.ml +++ b/src/analyses/tutorials/signs.ml @@ -36,7 +36,7 @@ end * We then lift the above operations to the lattice. *) module SL = struct - include Lattice.Flat (Signs) (Printable.DefaultNames) + include Lattice.Flat (Printable.DefaultConf) (Signs) let of_int i = `Lifted (Signs.of_int i) let lt x y = match x, y with diff --git a/src/analyses/wrapperFunctionAnalysis0.ml b/src/analyses/wrapperFunctionAnalysis0.ml index 9ea9c0c9aa..ba04c7ed7f 100644 --- a/src/analyses/wrapperFunctionAnalysis0.ml +++ b/src/analyses/wrapperFunctionAnalysis0.ml @@ -36,7 +36,8 @@ module ThreadCreateUniqueCount = MakeUniqueCount (val unique_count_args_from_config "ana.thread.unique_thread_id_count") (* since the query also references NodeFlatLattice, it also needs to reside here *) -module NodeFlatLattice = Lattice.Flat (Node) (struct +module NodeFlatLattice = Lattice.Flat (struct + include Printable.DefaultConf let top_name = "Unknown node" let bot_name = "Unreachable node" - end) + end) (Node) diff --git a/src/cdomains/intDomain.ml b/src/cdomains/intDomain.ml index 5d5174744f..23f4d88e25 100644 --- a/src/cdomains/intDomain.ml +++ b/src/cdomains/intDomain.ml @@ -1713,10 +1713,11 @@ end module Flat (Base: IkindUnawareS) = (* identical to Lift, but goes to `Top/`Bot if Base raises Unknown/Error *) struct type int_t = Base.int_t - include Lattice.Flat (Base) (struct + include Lattice.Flat (struct + include Printable.DefaultConf let top_name = "Unknown int" let bot_name = "Error int" - end) + end) (Base) let top_of ik = top () let bot_of ik = bot () @@ -1792,10 +1793,11 @@ end module Lift (Base: IkindUnawareS) = (* identical to Flat, but does not go to `Top/Bot` if Base raises Unknown/Error *) struct - include Lattice.LiftPO (Base) (struct + include Lattice.LiftPO (struct + include Printable.DefaultConf let top_name = "MaxInt" let bot_name = "MinInt" - end) + end) (Base) type int_t = Base.int_t let top_of ik = top () let bot_of ik = bot () diff --git a/src/cdomains/mutexAttrDomain.ml b/src/cdomains/mutexAttrDomain.ml index 748ede0ff5..b7c18a3cae 100644 --- a/src/cdomains/mutexAttrDomain.ml +++ b/src/cdomains/mutexAttrDomain.ml @@ -18,7 +18,7 @@ struct end) end -include Lattice.Flat(MutexKind) (struct let bot_name = "Uninitialized" let top_name = "Top" end) +include Lattice.Flat (struct include Printable.DefaultConf let bot_name = "Uninitialized" let top_name = "Top" end) (MutexKind) (* Needed because OS X is weird and assigns different constants than normal systems... :( *) let recursive_int = lazy ( diff --git a/src/cdomains/regionDomain.ml b/src/cdomains/regionDomain.ml index 681eb79007..b0f8d5d57e 100644 --- a/src/cdomains/regionDomain.ml +++ b/src/cdomains/regionDomain.ml @@ -252,4 +252,4 @@ struct end (* TODO: remove Lift *) -module RegionDom = Lattice.Lift (RegMap) (struct let top_name = "Unknown" let bot_name = "Error" end) +module RegionDom = Lattice.Lift (struct include Printable.DefaultConf let top_name = "Unknown" let bot_name = "Error" end) (RegMap) diff --git a/src/cdomains/stackDomain.ml b/src/cdomains/stackDomain.ml index 3a83c78503..bd77a7d82f 100644 --- a/src/cdomains/stackDomain.ml +++ b/src/cdomains/stackDomain.ml @@ -30,7 +30,7 @@ struct module VarLat = Lattice.Fake (Basetype.Variables) - module Var = Lattice.Lift (VarLat) (struct let top_name="top" let bot_name="⊥" end) + module Var = Lattice.Lift (struct include Printable.DefaultConf let top_name="top" let bot_name="⊥" end) (VarLat) include Lattice.Liszt (Var) let top () : t = [] diff --git a/src/cdomains/threadIdDomain.ml b/src/cdomains/threadIdDomain.ml index d0c3f7b61b..ed9ad2c854 100644 --- a/src/cdomains/threadIdDomain.ml +++ b/src/cdomains/threadIdDomain.ml @@ -196,12 +196,13 @@ struct end module ThreadLiftNames = struct + include Printable.DefaultConf let bot_name = "Bot Threads" let top_name = "Top Threads" end module Lift (Thread: S) = struct - include Lattice.Flat (Thread) (ThreadLiftNames) + include Lattice.Flat (ThreadLiftNames) (Thread) let name () = "Thread" end @@ -217,7 +218,7 @@ struct let name = "FlagConfiguredTID" end) - module D = Lattice.Lift2(H.D)(P.D)(struct let bot_name = "bot" let top_name = "top" end) + module D = Lattice.Lift2 (H.D) (P.D) let history_enabled () = match GobConfig.get_string "ana.thread.domain" with diff --git a/src/cdomains/unionDomain.ml b/src/cdomains/unionDomain.ml index ac25450c6a..9871b95e1b 100644 --- a/src/cdomains/unionDomain.ml +++ b/src/cdomains/unionDomain.ml @@ -16,10 +16,11 @@ sig end module Field = struct - include Lattice.Flat (CilType.Fieldinfo) (struct + include Lattice.Flat (struct + include Printable.DefaultConf let top_name = "Unknown field" let bot_name = "If you see this, you are special!" - end) + end) (CilType.Fieldinfo) let meet f g = if equal f g then diff --git a/src/common/domains/printable.ml b/src/common/domains/printable.ml index 882cb30bf5..d52f6a4d2a 100644 --- a/src/common/domains/printable.ml +++ b/src/common/domains/printable.ml @@ -103,18 +103,6 @@ struct end module Unit = UnitConf (struct let name = "()" end) -module type LiftingNames = -sig - val bot_name: string - val top_name: string -end - -module DefaultNames = -struct - let bot_name = "bot" - let top_name = "top" -end - (* HAS SIDE-EFFECTS ---- PLEASE INSTANCIATE ONLY ONCE!!! *) module HConsed (Base:S) = struct @@ -195,11 +183,27 @@ struct let tag = lift_f M.tag end -module Lift (Base: S) (N: LiftingNames) = + +module type LiftConf = +sig + val bot_name: string + val top_name: string + val expand1: bool +end + +module DefaultConf = +struct + let bot_name = "bot" + let top_name = "top" + let expand1 = true + let expand2 = true +end + +module LiftConf (Conf: LiftConf) (Base: S) = struct type t = [`Bot | `Lifted of Base.t | `Top] [@@deriving eq, ord, hash] include Std - include N + open Conf let lift x = `Lifted x @@ -217,13 +221,13 @@ struct let name () = "lifted " ^ Base.name () let printXml f = function - | `Bot -> BatPrintf.fprintf f "\n\n%s\n\n\n" (XmlUtil.escape N.bot_name) - | `Top -> BatPrintf.fprintf f "\n\n%s\n\n\n" (XmlUtil.escape N.top_name) + | `Bot -> BatPrintf.fprintf f "\n\n%s\n\n\n" (XmlUtil.escape bot_name) + | `Top -> BatPrintf.fprintf f "\n\n%s\n\n\n" (XmlUtil.escape top_name) | `Lifted x -> Base.printXml f x let to_yojson = function - | `Bot -> `String N.bot_name - | `Top -> `String N.top_name + | `Bot -> `String bot_name + | `Top -> `String top_name | `Lifted x -> Base.to_yojson x let relift x = match x with @@ -370,11 +374,17 @@ struct let relift = Option.map Base.relift end -module Lift2Conf (Conf: EitherConf) (Base1: S) (Base2: S) (N: LiftingNames) = +module type Lift2Conf = +sig + include LiftConf + val expand2: bool +end + +module Lift2Conf (Conf: Lift2Conf) (Base1: S) (Base2: S) = struct type t = [`Bot | `Lifted1 of Base1.t | `Lifted2 of Base2.t | `Top] [@@deriving eq, ord, hash] include Std - include N + open Conf let pretty () (state:t) = match state with @@ -399,23 +409,23 @@ struct let name () = "lifted " ^ Base1.name () ^ " and " ^ Base2.name () let printXml f = function - | `Bot -> BatPrintf.fprintf f "\n\n%s\n\n\n" N.bot_name - | `Top -> BatPrintf.fprintf f "\n\n%s\n\n\n" N.top_name + | `Bot -> BatPrintf.fprintf f "\n\n%s\n\n\n" bot_name + | `Top -> BatPrintf.fprintf f "\n\n%s\n\n\n" top_name | `Lifted1 x when Conf.expand1 -> BatPrintf.fprintf f "\n\n\nLifted1\n\n%a\n\n" Base1.printXml x | `Lifted1 x -> Base1.printXml f x | `Lifted2 x when Conf.expand2 -> BatPrintf.fprintf f "\n\n\nLifted2\n\n%a\n\n" Base2.printXml x | `Lifted2 x -> Base2.printXml f x let to_yojson = function - | `Bot -> `String N.bot_name - | `Top -> `String N.top_name + | `Bot -> `String bot_name + | `Top -> `String top_name | `Lifted1 x when Conf.expand1 -> `Assoc [ Base1.name (), Base1.to_yojson x ] | `Lifted1 x -> Base1.to_yojson x | `Lifted2 x when Conf.expand2 -> `Assoc [ Base2.name (), Base2.to_yojson x ] | `Lifted2 x -> Base2.to_yojson x end -module Lift2 = Lift2Conf (struct let expand1 = true let expand2 = true end) +module Lift2 = Lift2Conf (DefaultConf) module type ProdConfiguration = sig diff --git a/src/domain/boolDomain.ml b/src/domain/boolDomain.ml index 08be66a602..a4bd45c052 100644 --- a/src/domain/boolDomain.ml +++ b/src/domain/boolDomain.ml @@ -41,7 +41,8 @@ struct end module FlatBool: Lattice.S with type t = [`Bot | `Lifted of bool | `Top] = - Lattice.Flat (Bool) (struct + Lattice.Flat (struct + include Printable.DefaultConf let top_name = "?" let bot_name = "-" - end) + end) (Bool) diff --git a/src/domain/lattice.ml b/src/domain/lattice.ml index 448f801ec1..0d21a1a320 100644 --- a/src/domain/lattice.ml +++ b/src/domain/lattice.ml @@ -183,9 +183,9 @@ struct let pretty_diff () ((x:t),(y:t)): Pretty.doc = M.pretty_diff () (unlift x, unlift y) end -module Flat (Base: Printable.S) (N: Printable.LiftingNames) = +module Flat (Conf: Printable.LiftConf) (Base: Printable.S) = struct - include Printable.Lift (Base) (N) + include Printable.LiftConf (Conf) (Base) let bot () = `Bot let is_bot x = x = `Bot let top () = `Top @@ -228,9 +228,9 @@ struct end -module Lift (Base: S) (N: Printable.LiftingNames) = +module Lift (Conf: Printable.LiftConf) (Base: S) = struct - include Printable.Lift (Base) (N) + include Printable.LiftConf (Conf) (Base) let bot () = `Bot let is_bot x = x = `Bot @@ -278,9 +278,9 @@ struct | _ -> x end -module LiftPO (Base: PO) (N: Printable.LiftingNames) = +module LiftPO (Conf: Printable.LiftConf) (Base: PO) = struct - include Printable.Lift (Base) (N) + include Printable.LiftConf (Conf) (Base) let bot () = `Bot let is_bot x = x = `Bot @@ -336,9 +336,9 @@ struct | _ -> x end -module Lift2Conf (Conf: Printable.EitherConf) (Base1: S) (Base2: S) (N: Printable.LiftingNames) = +module Lift2Conf (Conf: Printable.Lift2Conf) (Base1: S) (Base2: S) = struct - include Printable.Lift2Conf (Conf) (Base1) (Base2) (N) + include Printable.Lift2Conf (Conf) (Base1) (Base2) let bot () = `Bot let is_bot x = x = `Bot @@ -408,7 +408,7 @@ struct end -module Lift2 = Lift2Conf (struct let expand1 = true let expand2 = true end) +module Lift2 = Lift2Conf (Printable.DefaultConf) module ProdConf (C: Printable.ProdConfiguration) (Base1: S) (Base2: S) = struct diff --git a/src/domains/invariant.ml b/src/domains/invariant.ml index 1a0c3c033c..d719f8b9c1 100644 --- a/src/domains/invariant.ml +++ b/src/domains/invariant.ml @@ -28,11 +28,12 @@ end module N = struct + include Printable.DefaultConf let bot_name = "false" let top_name = "true" end -include Lattice.Lift (ExpLat) (N) +include Lattice.Lift (N) (ExpLat) let none = top () let of_exp = lift diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 228320bef3..526e82cb5e 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -17,26 +17,29 @@ module TC = WrapperFunctionAnalysis0.ThreadCreateUniqueCount module ThreadNodeLattice = Lattice.Prod (NFL) (TC) module ML = LibraryDesc.MathLifted -module VI = Lattice.Flat (Basetype.Variables) (struct +module VI = Lattice.Flat (struct + include Printable.DefaultConf let top_name = "Unknown line" let bot_name = "Unreachable line" - end) + end) (Basetype.Variables) type iterprevvar = int -> (MyCFG.node * Obj.t * int) -> MyARG.inline_edge -> unit type itervar = int -> unit let compare_itervar _ _ = 0 let compare_iterprevvar _ _ = 0 -module FlatYojson = Lattice.Flat (Printable.Yojson) (struct +module FlatYojson = Lattice.Flat (struct + include Printable.DefaultConf let top_name = "top yojson" let bot_name = "bot yojson" - end) + end) (Printable.Yojson) module SD: Lattice.S with type t = [`Bot | `Lifted of string | `Top] = - Lattice.Flat (Basetype.RawStrings) (struct + Lattice.Flat (struct + include Printable.DefaultConf let top_name = "?" let bot_name = "-" - end) + end) (Basetype.RawStrings) module VD = ValueDomain.Compound module AD = ValueDomain.AD diff --git a/src/domains/valueDomainQueries.ml b/src/domains/valueDomainQueries.ml index 8266582ac2..b7644a32ed 100644 --- a/src/domains/valueDomainQueries.ml +++ b/src/domains/valueDomainQueries.ml @@ -9,7 +9,7 @@ module AD = PreValueDomain.AD module ID = struct module I = IntDomain.IntDomTuple - include Lattice.Lift (I) (Printable.DefaultNames) + include Lattice.Lift (Printable.DefaultConf) (I) let lift op x = `Lifted (op x) let unlift op x = match x with diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 44f1f1894e..6734b67121 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -117,7 +117,7 @@ struct let name () = "contexts" end - include Lattice.Lift2 (G) (CSet) (Printable.DefaultNames) + include Lattice.Lift2 (G) (CSet) let spec = function | `Bot -> G.bot () @@ -142,10 +142,11 @@ exception Deadcode (** [Dom (D)] produces D lifted where bottom means dead-code *) module Dom (LD: Lattice.S) = struct - include Lattice.Lift (LD) (struct + include Lattice.Lift (struct + include Printable.DefaultConf let bot_name = "Dead code" let top_name = "Totally unknown and messed up" - end) + end) (LD) let lift (x:LD.t) : t = `Lifted x @@ -155,7 +156,7 @@ struct | _ -> raise Deadcode let printXml f = function - | `Top -> BatPrintf.fprintf f "%s" (XmlUtil.escape top_name) + | `Top -> BatPrintf.fprintf f "%s" (XmlUtil.escape Printable.DefaultConf.top_name) | `Bot -> () | `Lifted x -> LD.printXml f x end diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index ee1ea00a01..8039a867d8 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1075,15 +1075,15 @@ module EqIncrSolverFromEqSolver (Sol: GenericEqSolver): GenericEqIncrSolver = (** Translate a [GlobConstrSys] into a [EqConstrSys] *) module EqConstrSysFromGlobConstrSys (S:GlobConstrSys) : EqConstrSys with type v = Var2(S.LVar)(S.GVar).t - and type d = Lattice.Lift2(S.G)(S.D)(Printable.DefaultNames).t + and type d = Lattice.Lift2(S.G)(S.D).t and module Var = Var2(S.LVar)(S.GVar) - and module Dom = Lattice.Lift2(S.G)(S.D)(Printable.DefaultNames) + and module Dom = Lattice.Lift2(S.G)(S.D) = struct module Var = Var2(S.LVar)(S.GVar) module Dom = struct - include Lattice.Lift2(S.G)(S.D)(Printable.DefaultNames) + include Lattice.Lift2 (S.G) (S.D) let printXml f = function | `Lifted1 a -> S.G.printXml f a | `Lifted2 a -> S.D.printXml f a @@ -1355,7 +1355,7 @@ struct module G = struct - include Lattice.Lift2 (S.G) (EM) (Printable.DefaultNames) + include Lattice.Lift2 (S.G) (EM) let name () = "deadbranch" let s = function @@ -1484,7 +1484,7 @@ struct module G = struct - include Lattice.Lift2 (S.G) (S.D) (Printable.DefaultNames) + include Lattice.Lift2 (S.G) (S.D) let s = function | `Bot -> S.G.bot () @@ -1737,7 +1737,7 @@ struct module G = struct - include Lattice.Lift2 (G) (CallerSet) (Printable.DefaultNames) + include Lattice.Lift2 (G) (CallerSet) let spec = function | `Bot -> G.bot () diff --git a/src/util/library/libraryDesc.ml b/src/util/library/libraryDesc.ml index 4997b306a9..a07c0ee27f 100644 --- a/src/util/library/libraryDesc.ml +++ b/src/util/library/libraryDesc.ml @@ -184,7 +184,8 @@ module MathPrintable = struct ) end -module MathLifted = Lattice.Flat (MathPrintable) (struct +module MathLifted = Lattice.Flat (struct + include Printable.DefaultConf let top_name = "Unknown or no math desc" let bot_name = "Nonexistent math desc" - end) + end) (MathPrintable) diff --git a/src/witness/observerAnalysis.ml b/src/witness/observerAnalysis.ml index e8daf56155..d4af989ebc 100644 --- a/src/witness/observerAnalysis.ml +++ b/src/witness/observerAnalysis.ml @@ -29,7 +29,7 @@ struct let n () = -1 let names x = "state " ^ string_of_int x end - module D = Lattice.Flat (Printable.Chain (ChainParams)) (Printable.DefaultNames) + module D = Lattice.Flat (Printable.DefaultConf) (Printable.Chain (ChainParams)) module C = D module P = IdentityP (D) (* fully path-sensitive *) From ea029bc5b13f1a50772c7d053ad0aec0e2cec8cc Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 14 Dec 2023 16:29:23 +0200 Subject: [PATCH 1255/1312] Simplify default Lattice.Flat usage --- src/analyses/loopTermination.ml | 2 +- src/analyses/mCPRegistry.ml | 2 +- src/analyses/threadId.ml | 2 +- src/analyses/tutorials/signs.ml | 2 +- src/analyses/wrapperFunctionAnalysis0.ml | 2 +- src/cdomains/intDomain.ml | 2 +- src/cdomains/mutexAttrDomain.ml | 2 +- src/cdomains/regionDomain.ml | 2 +- src/cdomains/stackDomain.ml | 2 +- src/cdomains/threadIdDomain.ml | 2 +- src/cdomains/unionDomain.ml | 2 +- src/common/domains/printable.ml | 7 +++---- src/domain/boolDomain.ml | 2 +- src/domain/lattice.ml | 8 ++++++-- src/domains/invariant.ml | 2 +- src/domains/queries.ml | 18 +++--------------- src/domains/valueDomainQueries.ml | 2 +- src/framework/analyses.ml | 2 +- src/util/library/libraryDesc.ml | 2 +- src/witness/observerAnalysis.ml | 2 +- 20 files changed, 29 insertions(+), 38 deletions(-) diff --git a/src/analyses/loopTermination.ml b/src/analyses/loopTermination.ml index 66cbd5772f..857b6189d0 100644 --- a/src/analyses/loopTermination.ml +++ b/src/analyses/loopTermination.ml @@ -19,7 +19,7 @@ let check_bounded ctx varinfo = (** We want to record termination information of loops and use the loop * statements for that. We use this lifting because we need to have a * lattice. *) -module Statements = Lattice.Flat (Printable.DefaultConf) (CilType.Stmt) +module Statements = Lattice.Flat (CilType.Stmt) (** The termination analysis considering loops and gotos *) module Spec : Analyses.MCPSpec = diff --git a/src/analyses/mCPRegistry.ml b/src/analyses/mCPRegistry.ml index a685b31798..663a1d8862 100644 --- a/src/analyses/mCPRegistry.ml +++ b/src/analyses/mCPRegistry.ml @@ -426,7 +426,7 @@ end module DomVariantLattice (DLSpec : DomainListLatticeSpec) = struct - include Lattice.Lift (Printable.DefaultConf) (DomVariantLattice0 (DLSpec)) + include Lattice.Lift (DomVariantLattice0 (DLSpec)) let name () = "MCP.G" end diff --git a/src/analyses/threadId.ml b/src/analyses/threadId.ml index f954077836..86e7f770a8 100644 --- a/src/analyses/threadId.ml +++ b/src/analyses/threadId.ml @@ -31,7 +31,7 @@ struct module N = struct - include Lattice.Flat (struct include Printable.DefaultConf let bot_name = "unknown node" let top_name = "unknown node" end) (VNI) + include Lattice.FlatConf (struct include Printable.DefaultConf let bot_name = "unknown node" let top_name = "unknown node" end) (VNI) let name () = "wrapper call" end module TD = Thread.D diff --git a/src/analyses/tutorials/signs.ml b/src/analyses/tutorials/signs.ml index 6ba720d0ea..2c26ad33b6 100644 --- a/src/analyses/tutorials/signs.ml +++ b/src/analyses/tutorials/signs.ml @@ -36,7 +36,7 @@ end * We then lift the above operations to the lattice. *) module SL = struct - include Lattice.Flat (Printable.DefaultConf) (Signs) + include Lattice.Flat (Signs) let of_int i = `Lifted (Signs.of_int i) let lt x y = match x, y with diff --git a/src/analyses/wrapperFunctionAnalysis0.ml b/src/analyses/wrapperFunctionAnalysis0.ml index ba04c7ed7f..cd5940011e 100644 --- a/src/analyses/wrapperFunctionAnalysis0.ml +++ b/src/analyses/wrapperFunctionAnalysis0.ml @@ -36,7 +36,7 @@ module ThreadCreateUniqueCount = MakeUniqueCount (val unique_count_args_from_config "ana.thread.unique_thread_id_count") (* since the query also references NodeFlatLattice, it also needs to reside here *) -module NodeFlatLattice = Lattice.Flat (struct +module NodeFlatLattice = Lattice.FlatConf (struct include Printable.DefaultConf let top_name = "Unknown node" let bot_name = "Unreachable node" diff --git a/src/cdomains/intDomain.ml b/src/cdomains/intDomain.ml index 23f4d88e25..376dab71c2 100644 --- a/src/cdomains/intDomain.ml +++ b/src/cdomains/intDomain.ml @@ -1713,7 +1713,7 @@ end module Flat (Base: IkindUnawareS) = (* identical to Lift, but goes to `Top/`Bot if Base raises Unknown/Error *) struct type int_t = Base.int_t - include Lattice.Flat (struct + include Lattice.FlatConf (struct include Printable.DefaultConf let top_name = "Unknown int" let bot_name = "Error int" diff --git a/src/cdomains/mutexAttrDomain.ml b/src/cdomains/mutexAttrDomain.ml index b7c18a3cae..ea9696d26f 100644 --- a/src/cdomains/mutexAttrDomain.ml +++ b/src/cdomains/mutexAttrDomain.ml @@ -18,7 +18,7 @@ struct end) end -include Lattice.Flat (struct include Printable.DefaultConf let bot_name = "Uninitialized" let top_name = "Top" end) (MutexKind) +include Lattice.FlatConf (struct include Printable.DefaultConf let bot_name = "Uninitialized" let top_name = "Top" end) (MutexKind) (* Needed because OS X is weird and assigns different constants than normal systems... :( *) let recursive_int = lazy ( diff --git a/src/cdomains/regionDomain.ml b/src/cdomains/regionDomain.ml index b0f8d5d57e..26a89f1013 100644 --- a/src/cdomains/regionDomain.ml +++ b/src/cdomains/regionDomain.ml @@ -252,4 +252,4 @@ struct end (* TODO: remove Lift *) -module RegionDom = Lattice.Lift (struct include Printable.DefaultConf let top_name = "Unknown" let bot_name = "Error" end) (RegMap) +module RegionDom = Lattice.LiftConf (struct include Printable.DefaultConf let top_name = "Unknown" let bot_name = "Error" end) (RegMap) diff --git a/src/cdomains/stackDomain.ml b/src/cdomains/stackDomain.ml index bd77a7d82f..50864d6294 100644 --- a/src/cdomains/stackDomain.ml +++ b/src/cdomains/stackDomain.ml @@ -30,7 +30,7 @@ struct module VarLat = Lattice.Fake (Basetype.Variables) - module Var = Lattice.Lift (struct include Printable.DefaultConf let top_name="top" let bot_name="⊥" end) (VarLat) + module Var = Lattice.LiftConf (struct include Printable.DefaultConf let top_name="top" let bot_name="⊥" end) (VarLat) include Lattice.Liszt (Var) let top () : t = [] diff --git a/src/cdomains/threadIdDomain.ml b/src/cdomains/threadIdDomain.ml index ed9ad2c854..c21bb40628 100644 --- a/src/cdomains/threadIdDomain.ml +++ b/src/cdomains/threadIdDomain.ml @@ -202,7 +202,7 @@ module ThreadLiftNames = struct end module Lift (Thread: S) = struct - include Lattice.Flat (ThreadLiftNames) (Thread) + include Lattice.FlatConf (ThreadLiftNames) (Thread) let name () = "Thread" end diff --git a/src/cdomains/unionDomain.ml b/src/cdomains/unionDomain.ml index 9871b95e1b..ad5c531061 100644 --- a/src/cdomains/unionDomain.ml +++ b/src/cdomains/unionDomain.ml @@ -16,7 +16,7 @@ sig end module Field = struct - include Lattice.Flat (struct + include Lattice.FlatConf (struct include Printable.DefaultConf let top_name = "Unknown field" let bot_name = "If you see this, you are special!" diff --git a/src/common/domains/printable.ml b/src/common/domains/printable.ml index d52f6a4d2a..37dd88f9ac 100644 --- a/src/common/domains/printable.ml +++ b/src/common/domains/printable.ml @@ -197,6 +197,7 @@ struct let top_name = "top" let expand1 = true let expand2 = true + let expand3 = true end module LiftConf (Conf: LiftConf) (Base: S) = @@ -291,7 +292,7 @@ struct | `Right x -> `Right (Base2.relift x) end -module Either = EitherConf (struct let expand1 = true let expand2 = true end) +module Either = EitherConf (DefaultConf) module type Either3Conf = sig @@ -345,7 +346,7 @@ struct | `Right x -> `Right (Base3.relift x) end -module Either3 = Either3Conf (struct let expand1 = true let expand2 = true let expand3 = true end) +module Either3 = Either3Conf (DefaultConf) module Option (Base: S) (N: Name) = struct @@ -425,8 +426,6 @@ struct | `Lifted2 x -> Base2.to_yojson x end -module Lift2 = Lift2Conf (DefaultConf) - module type ProdConfiguration = sig val expand_fst: bool diff --git a/src/domain/boolDomain.ml b/src/domain/boolDomain.ml index a4bd45c052..d92d716d7a 100644 --- a/src/domain/boolDomain.ml +++ b/src/domain/boolDomain.ml @@ -41,7 +41,7 @@ struct end module FlatBool: Lattice.S with type t = [`Bot | `Lifted of bool | `Top] = - Lattice.Flat (struct + Lattice.FlatConf (struct include Printable.DefaultConf let top_name = "?" let bot_name = "-" diff --git a/src/domain/lattice.ml b/src/domain/lattice.ml index 0d21a1a320..99322c09d8 100644 --- a/src/domain/lattice.ml +++ b/src/domain/lattice.ml @@ -183,7 +183,7 @@ struct let pretty_diff () ((x:t),(y:t)): Pretty.doc = M.pretty_diff () (unlift x, unlift y) end -module Flat (Conf: Printable.LiftConf) (Base: Printable.S) = +module FlatConf (Conf: Printable.LiftConf) (Base: Printable.S) = struct include Printable.LiftConf (Conf) (Base) let bot () = `Bot @@ -227,8 +227,10 @@ struct end +module Flat = FlatConf (Printable.DefaultConf) -module Lift (Conf: Printable.LiftConf) (Base: S) = + +module LiftConf (Conf: Printable.LiftConf) (Base: S) = struct include Printable.LiftConf (Conf) (Base) @@ -278,6 +280,8 @@ struct | _ -> x end +module Lift = LiftConf (Printable.DefaultConf) + module LiftPO (Conf: Printable.LiftConf) (Base: PO) = struct include Printable.LiftConf (Conf) (Base) diff --git a/src/domains/invariant.ml b/src/domains/invariant.ml index d719f8b9c1..b281e8f7b3 100644 --- a/src/domains/invariant.ml +++ b/src/domains/invariant.ml @@ -33,7 +33,7 @@ struct let top_name = "true" end -include Lattice.Lift (N) (ExpLat) +include Lattice.LiftConf (N) (ExpLat) let none = top () let of_exp = lift diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 526e82cb5e..24e5d45593 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -17,29 +17,17 @@ module TC = WrapperFunctionAnalysis0.ThreadCreateUniqueCount module ThreadNodeLattice = Lattice.Prod (NFL) (TC) module ML = LibraryDesc.MathLifted -module VI = Lattice.Flat (struct - include Printable.DefaultConf - let top_name = "Unknown line" - let bot_name = "Unreachable line" - end) (Basetype.Variables) +module VI = Lattice.Flat (Basetype.Variables) type iterprevvar = int -> (MyCFG.node * Obj.t * int) -> MyARG.inline_edge -> unit type itervar = int -> unit let compare_itervar _ _ = 0 let compare_iterprevvar _ _ = 0 -module FlatYojson = Lattice.Flat (struct - include Printable.DefaultConf - let top_name = "top yojson" - let bot_name = "bot yojson" - end) (Printable.Yojson) +module FlatYojson = Lattice.Flat (Printable.Yojson) module SD: Lattice.S with type t = [`Bot | `Lifted of string | `Top] = - Lattice.Flat (struct - include Printable.DefaultConf - let top_name = "?" - let bot_name = "-" - end) (Basetype.RawStrings) + Lattice.Flat (Basetype.RawStrings) module VD = ValueDomain.Compound module AD = ValueDomain.AD diff --git a/src/domains/valueDomainQueries.ml b/src/domains/valueDomainQueries.ml index b7644a32ed..bafec3f8bd 100644 --- a/src/domains/valueDomainQueries.ml +++ b/src/domains/valueDomainQueries.ml @@ -9,7 +9,7 @@ module AD = PreValueDomain.AD module ID = struct module I = IntDomain.IntDomTuple - include Lattice.Lift (Printable.DefaultConf) (I) + include Lattice.Lift (I) let lift op x = `Lifted (op x) let unlift op x = match x with diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 6734b67121..405df5b6a6 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -142,7 +142,7 @@ exception Deadcode (** [Dom (D)] produces D lifted where bottom means dead-code *) module Dom (LD: Lattice.S) = struct - include Lattice.Lift (struct + include Lattice.LiftConf (struct include Printable.DefaultConf let bot_name = "Dead code" let top_name = "Totally unknown and messed up" diff --git a/src/util/library/libraryDesc.ml b/src/util/library/libraryDesc.ml index a07c0ee27f..78a72b1741 100644 --- a/src/util/library/libraryDesc.ml +++ b/src/util/library/libraryDesc.ml @@ -184,7 +184,7 @@ module MathPrintable = struct ) end -module MathLifted = Lattice.Flat (struct +module MathLifted = Lattice.FlatConf (struct include Printable.DefaultConf let top_name = "Unknown or no math desc" let bot_name = "Nonexistent math desc" diff --git a/src/witness/observerAnalysis.ml b/src/witness/observerAnalysis.ml index d4af989ebc..58b5b31fe4 100644 --- a/src/witness/observerAnalysis.ml +++ b/src/witness/observerAnalysis.ml @@ -29,7 +29,7 @@ struct let n () = -1 let names x = "state " ^ string_of_int x end - module D = Lattice.Flat (Printable.DefaultConf) (Printable.Chain (ChainParams)) + module D = Lattice.Flat (Printable.Chain (ChainParams)) module C = D module P = IdentityP (D) (* fully path-sensitive *) From 838a17baf93a1e3008fc0262f1921529ba03ab52 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Dec 2023 20:57:46 +0000 Subject: [PATCH 1256/1312] Bump actions/upload-artifact from 3 to 4 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/coverage.yml | 2 +- .github/workflows/locked.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 0208af7c7a..4b47a66e15 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -88,7 +88,7 @@ jobs: COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} PULL_REQUEST_NUMBER: ${{ github.event.number }} - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 if: always() with: name: suite_result diff --git a/.github/workflows/locked.yml b/.github/workflows/locked.yml index 8604e7f52c..ab9385c737 100644 --- a/.github/workflows/locked.yml +++ b/.github/workflows/locked.yml @@ -82,7 +82,7 @@ jobs: - name: Test incremental regression with cfg comparison run: ruby scripts/update_suite.rb -c - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 if: always() with: name: suite_result From dceb4bea539b167647066346679cab4a0e168987 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 15 Dec 2023 10:56:20 +0200 Subject: [PATCH 1257/1312] Extract Printable.PrefixName functor to deduplicate expand code --- src/common/domains/printable.ml | 77 ++++++++++++++++++++++----------- 1 file changed, 51 insertions(+), 26 deletions(-) diff --git a/src/common/domains/printable.ml b/src/common/domains/printable.ml index 37dd88f9ac..7e08157898 100644 --- a/src/common/domains/printable.ml +++ b/src/common/domains/printable.ml @@ -184,6 +184,41 @@ struct end +module type PrefixNameConf = +sig + val expand: bool +end + +module PrefixName (Conf: PrefixNameConf) (Base: S): S with type t = Base.t = +struct + include Base + + let pretty () x = + if Conf.expand then + Pretty.dprintf "%s:%a" (Base.name ()) Base.pretty x + else + Base.pretty () x + + let show x = + if Conf.expand then + Base.name () ^ ":" ^ Base.show x + else + Base.show x + + let printXml f x = + if Conf.expand then + BatPrintf.fprintf f "\n\n%s\n\n%a\n\n" (Base.name ()) Base.printXml x + else + Base.printXml f x + + let to_yojson x = + if Conf.expand then + `Assoc [(Base.name (), Base.to_yojson x)] + else + Base.to_yojson x +end + + module type LiftConf = sig val bot_name: string @@ -257,34 +292,31 @@ end module EitherConf (Conf: EitherConf) (Base1: S) (Base2: S) = struct + open struct + module Base1 = PrefixName (struct let expand = Conf.expand1 end) (Base1) + module Base2 = PrefixName (struct let expand = Conf.expand2 end) (Base2) + end + type t = [`Left of Base1.t | `Right of Base2.t] [@@deriving eq, ord, hash] include Std let pretty () (state:t) = match state with - | `Left n when Conf.expand1 -> Pretty.dprintf "%s:%a" (Base1.name ()) Base1.pretty n | `Left n -> Base1.pretty () n - | `Right n when Conf.expand2 -> Pretty.dprintf "%s:%a" (Base2.name ()) Base2.pretty n | `Right n -> Base2.pretty () n let show state = match state with - | `Left n when Conf.expand1 -> (Base1.name ()) ^ ":" ^ Base1.show n | `Left n -> Base1.show n - | `Right n when Conf.expand2 -> (Base2.name ()) ^ ":" ^ Base2.show n | `Right n -> Base2.show n let name () = "either " ^ Base1.name () ^ " or " ^ Base2.name () let printXml f = function - | `Left x when Conf.expand1 -> BatPrintf.fprintf f "\n\nLeft\n\n%a\n\n" Base1.printXml x | `Left x -> Base1.printXml f x - | `Right x when Conf.expand2 -> BatPrintf.fprintf f "\n\nRight\n\n%a\n\n" Base2.printXml x | `Right x -> Base2.printXml f x let to_yojson = function - | `Left x when Conf.expand1 -> `Assoc [ Base1.name (), Base1.to_yojson x ] | `Left x -> Base1.to_yojson x - | `Right x when Conf.expand2 -> `Assoc [ Base2.name (), Base2.to_yojson x ] | `Right x -> Base2.to_yojson x let relift = function @@ -302,42 +334,36 @@ end module Either3Conf (Conf: Either3Conf) (Base1: S) (Base2: S) (Base3: S) = struct + open struct + module Base1 = PrefixName (struct let expand = Conf.expand1 end) (Base1) + module Base2 = PrefixName (struct let expand = Conf.expand2 end) (Base2) + module Base3 = PrefixName (struct let expand = Conf.expand3 end) (Base3) + end + type t = [`Left of Base1.t | `Middle of Base2.t | `Right of Base3.t] [@@deriving eq, ord, hash] include Std let pretty () (state:t) = match state with - | `Left n when Conf.expand1 -> Pretty.dprintf "%s:%a" (Base1.name ()) Base1.pretty n | `Left n -> Base1.pretty () n - | `Middle n when Conf.expand2 -> Pretty.dprintf "%s:%a" (Base2.name ()) Base2.pretty n | `Middle n -> Base2.pretty () n - | `Right n when Conf.expand3 -> Pretty.dprintf "%s:%a" (Base3.name ()) Base3.pretty n | `Right n -> Base3.pretty () n let show state = match state with - | `Left n when Conf.expand1 -> (Base1.name ()) ^ ":" ^ Base1.show n | `Left n -> Base1.show n - | `Middle n when Conf.expand2 -> (Base2.name ()) ^ ":" ^ Base2.show n | `Middle n -> Base2.show n - | `Right n when Conf.expand3 -> (Base3.name ()) ^ ":" ^ Base3.show n | `Right n -> Base3.show n let name () = "either " ^ Base1.name () ^ " or " ^ Base2.name () ^ " or " ^ Base3.name () let printXml f = function - | `Left x when Conf.expand1 -> BatPrintf.fprintf f "\n\nLeft\n\n%a\n\n" Base1.printXml x | `Left x -> Base1.printXml f x - | `Middle x when Conf.expand2 -> BatPrintf.fprintf f "\n\nMiddle\n\n%a\n\n" Base2.printXml x | `Middle x -> Base2.printXml f x - | `Right x when Conf.expand3 -> BatPrintf.fprintf f "\n\nRight\n\n%a\n\n" Base3.printXml x | `Right x -> Base3.printXml f x let to_yojson = function - | `Left x when Conf.expand1 -> `Assoc [ Base1.name (), Base1.to_yojson x ] | `Left x -> Base1.to_yojson x - | `Middle x when Conf.expand2 -> `Assoc [ Base2.name (), Base2.to_yojson x ] | `Middle x -> Base2.to_yojson x - | `Right x when Conf.expand3 -> `Assoc [ Base3.name (), Base3.to_yojson x ] | `Right x -> Base3.to_yojson x let relift = function @@ -383,13 +409,17 @@ end module Lift2Conf (Conf: Lift2Conf) (Base1: S) (Base2: S) = struct + open struct + module Base1 = PrefixName (struct let expand = Conf.expand1 end) (Base1) + module Base2 = PrefixName (struct let expand = Conf.expand2 end) (Base2) + end + type t = [`Bot | `Lifted1 of Base1.t | `Lifted2 of Base2.t | `Top] [@@deriving eq, ord, hash] include Std open Conf let pretty () (state:t) = match state with - (* TODO: expand *) | `Lifted1 n -> Base1.pretty () n | `Lifted2 n -> Base2.pretty () n | `Bot -> text bot_name @@ -397,7 +427,6 @@ struct let show state = match state with - (* TODO: expand *) | `Lifted1 n -> Base1.show n | `Lifted2 n -> Base2.show n | `Bot -> bot_name @@ -412,17 +441,13 @@ struct let printXml f = function | `Bot -> BatPrintf.fprintf f "\n\n%s\n\n\n" bot_name | `Top -> BatPrintf.fprintf f "\n\n%s\n\n\n" top_name - | `Lifted1 x when Conf.expand1 -> BatPrintf.fprintf f "\n\n\nLifted1\n\n%a\n\n" Base1.printXml x | `Lifted1 x -> Base1.printXml f x - | `Lifted2 x when Conf.expand2 -> BatPrintf.fprintf f "\n\n\nLifted2\n\n%a\n\n" Base2.printXml x | `Lifted2 x -> Base2.printXml f x let to_yojson = function | `Bot -> `String bot_name | `Top -> `String top_name - | `Lifted1 x when Conf.expand1 -> `Assoc [ Base1.name (), Base1.to_yojson x ] | `Lifted1 x -> Base1.to_yojson x - | `Lifted2 x when Conf.expand2 -> `Assoc [ Base2.name (), Base2.to_yojson x ] | `Lifted2 x -> Base2.to_yojson x end From cc39ddd112604c30aeffef657ae1c8e6b63064d5 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 15 Dec 2023 11:05:30 +0200 Subject: [PATCH 1258/1312] Use Conf in Printable.LiftConf --- src/analyses/mCPRegistry.ml | 2 +- src/common/domains/printable.ml | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/analyses/mCPRegistry.ml b/src/analyses/mCPRegistry.ml index 663a1d8862..3961bc4d60 100644 --- a/src/analyses/mCPRegistry.ml +++ b/src/analyses/mCPRegistry.ml @@ -426,7 +426,7 @@ end module DomVariantLattice (DLSpec : DomainListLatticeSpec) = struct - include Lattice.Lift (DomVariantLattice0 (DLSpec)) + include Lattice.LiftConf (struct include Printable.DefaultConf let expand1 = false end) (DomVariantLattice0 (DLSpec)) let name () = "MCP.G" end diff --git a/src/common/domains/printable.ml b/src/common/domains/printable.ml index 7e08157898..0b1769e99c 100644 --- a/src/common/domains/printable.ml +++ b/src/common/domains/printable.ml @@ -237,6 +237,10 @@ end module LiftConf (Conf: LiftConf) (Base: S) = struct + open struct + module Base = PrefixName (struct let expand = Conf.expand1 end) (Base) + end + type t = [`Bot | `Lifted of Base.t | `Top] [@@deriving eq, ord, hash] include Std open Conf From 318f2c2d787d2f41f58528f6f95329396907bf8a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 15 Dec 2023 11:19:06 +0200 Subject: [PATCH 1259/1312] Do not expand MUTEX_INITS unknown --- src/analyses/commonPriv.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index 35b801e32b..90e5b28f82 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -83,7 +83,7 @@ struct end module V = struct - include Printable.Either3 (VMutex) (VMutexInits) (VGlobal) + include Printable.Either3Conf (struct include Printable.DefaultConf let expand2 = false end) (VMutex) (VMutexInits) (VGlobal) let name () = "MutexGlobals" let mutex x: t = `Left x let mutex_inits: t = `Middle () From c1e1632a2641def5717602bf553c5086f70d4c90 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 15 Dec 2023 11:19:21 +0200 Subject: [PATCH 1260/1312] Simplify RegionDomain.VFB printing --- src/cdomains/regionDomain.ml | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/cdomains/regionDomain.ml b/src/cdomains/regionDomain.ml index 26a89f1013..cd9141876c 100644 --- a/src/cdomains/regionDomain.ml +++ b/src/cdomains/regionDomain.ml @@ -8,23 +8,9 @@ module B = Printable.UnitConf (struct let name = "•" end) module VFB = struct - include Printable.Either (VF) (B) + include Printable.EitherConf (struct include Printable.DefaultConf let expand1 = false let expand2 = false end) (VF) (B) let name () = "region" - let pretty () = function - | `Right () -> Pretty.text "•" - | `Left x -> VF.pretty () x - - let show = function - | `Right () -> "•" - | `Left x -> VF.show x - - let printXml f = function - | `Right () -> - BatPrintf.fprintf f "\n\n•\n\n\n" - | `Left x -> - BatPrintf.fprintf f "\n\n%a\n\n\n" VF.printXml x - let collapse (x:t) (y:t): bool = match x,y with | `Right (), `Right () -> true From bbe86ae4fc521aa510a7fe7952f64f9295a60086 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 15 Dec 2023 11:27:46 +0200 Subject: [PATCH 1261/1312] Simplify SymbLocks.A.E printing --- src/analyses/symbLocks.ml | 12 ++++++------ src/cdomains/symbLocksDomain.ml | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/analyses/symbLocks.ml b/src/analyses/symbLocks.ml index f6fdd96c2e..c237967a7a 100644 --- a/src/analyses/symbLocks.ml +++ b/src/analyses/symbLocks.ml @@ -106,13 +106,12 @@ struct module A = struct - module E = struct - include Printable.Either (CilType.Offset) (ILock) - - let pretty () = function - | `Left o -> Pretty.dprintf "p-lock:%a" (d_offset (text "*")) o - | `Right addr -> Pretty.dprintf "i-lock:%a" ILock.pretty addr + module PLock = + struct + include CilType.Offset + let name () = "p-lock" + let pretty = d_offset (text "*") include Printable.SimplePretty ( struct type nonrec t = t @@ -120,6 +119,7 @@ struct end ) end + module E = Printable.Either (PLock) (ILock) include SetDomain.Make (E) let name () = "symblock" diff --git a/src/cdomains/symbLocksDomain.ml b/src/cdomains/symbLocksDomain.ml index 4a44911a53..85578d5fad 100644 --- a/src/cdomains/symbLocksDomain.ml +++ b/src/cdomains/symbLocksDomain.ml @@ -306,6 +306,7 @@ struct end include AddressDomain.AddressPrintable (Mval.MakePrintable (Offset.MakePrintable (Idx))) + let name () = "i-lock" let rec conv_const_offset x = match x with From 152b54baa51e85b54452b82011e17178f7ce00ce Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 15 Dec 2023 11:34:18 +0200 Subject: [PATCH 1262/1312] Do not expand lifted thread ID --- src/cdomains/threadIdDomain.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cdomains/threadIdDomain.ml b/src/cdomains/threadIdDomain.ml index c21bb40628..85f9a0297b 100644 --- a/src/cdomains/threadIdDomain.ml +++ b/src/cdomains/threadIdDomain.ml @@ -199,6 +199,7 @@ module ThreadLiftNames = struct include Printable.DefaultConf let bot_name = "Bot Threads" let top_name = "Top Threads" + let expand1 = false end module Lift (Thread: S) = struct From 1a3e00852ac5dfb10ee39958adbcc4974c11e327 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 15 Dec 2023 11:44:55 +0200 Subject: [PATCH 1263/1312] Make locked workflow artifact names unique --- .github/workflows/locked.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/locked.yml b/.github/workflows/locked.yml index ab9385c737..e25ccfcea1 100644 --- a/.github/workflows/locked.yml +++ b/.github/workflows/locked.yml @@ -85,7 +85,7 @@ jobs: - uses: actions/upload-artifact@v4 if: always() with: - name: suite_result + name: suite_result-${{ matrix.os }} path: tests/suite_result/ extraction: From 88d4d32f761d67489a918f310bcb6809c465c9d9 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 15 Dec 2023 12:05:25 +0200 Subject: [PATCH 1264/1312] Move SV-COMP scripts to scripts/ --- docs/developer-guide/releasing.md | 6 +++--- {sv-comp => scripts/sv-comp}/archive.sh | 4 ++-- {sv-comp => scripts/sv-comp}/sv-comp-run-no-overflow.py | 0 {sv-comp => scripts/sv-comp}/sv-comp-run.py | 0 {sv-comp => scripts/sv-comp}/witness-isomorphism.py | 0 {sv-comp => scripts/sv-comp}/yed-sv-comp.cnfx | 0 6 files changed, 5 insertions(+), 5 deletions(-) rename {sv-comp => scripts/sv-comp}/archive.sh (93%) rename {sv-comp => scripts/sv-comp}/sv-comp-run-no-overflow.py (100%) rename {sv-comp => scripts/sv-comp}/sv-comp-run.py (100%) rename {sv-comp => scripts/sv-comp}/witness-isomorphism.py (100%) rename {sv-comp => scripts/sv-comp}/yed-sv-comp.cnfx (100%) diff --git a/docs/developer-guide/releasing.md b/docs/developer-guide/releasing.md index fcf69ea533..d875c0d3bf 100644 --- a/docs/developer-guide/releasing.md +++ b/docs/developer-guide/releasing.md @@ -70,7 +70,7 @@ This is required such that the created archive would have everything in a single directory called `goblint`. -4. Update SV-COMP year in `sv-comp/archive.sh`. +4. Update SV-COMP year in `scripts/sv-comp/archive.sh`. This includes: git tag name, git tag message and zipped conf file. @@ -83,9 +83,9 @@ 2. Make sure you have nothing valuable that would be deleted by `make clean`. 3. Delete git tag from previous prerun: `git tag -d svcompXY`. -4. Create archive: `./sv-comp/archive.sh`. +4. Create archive: `./scripts/sv-comp/archive.sh`. - The resulting archive is `sv-comp/goblint.zip`. + The resulting archive is `scripts/sv-comp/goblint.zip`. 5. Check unextracted archive in latest SV-COMP container image: . diff --git a/sv-comp/archive.sh b/scripts/sv-comp/archive.sh similarity index 93% rename from sv-comp/archive.sh rename to scripts/sv-comp/archive.sh index 5d8605dc70..37fa2758d9 100755 --- a/sv-comp/archive.sh +++ b/scripts/sv-comp/archive.sh @@ -23,9 +23,9 @@ wget -O lib/LICENSE.APRON https://raw.githubusercontent.com/antoinemine/apron/ma # done outside to ensure archive contains goblint/ directory cd .. -rm goblint/sv-comp/goblint.zip +rm goblint/scripts/sv-comp/goblint.zip -zip goblint/sv-comp/goblint.zip \ +zip goblint/scripts/sv-comp/goblint.zip \ goblint/goblint \ goblint/lib/libapron.so \ goblint/lib/liboctD.so \ diff --git a/sv-comp/sv-comp-run-no-overflow.py b/scripts/sv-comp/sv-comp-run-no-overflow.py similarity index 100% rename from sv-comp/sv-comp-run-no-overflow.py rename to scripts/sv-comp/sv-comp-run-no-overflow.py diff --git a/sv-comp/sv-comp-run.py b/scripts/sv-comp/sv-comp-run.py similarity index 100% rename from sv-comp/sv-comp-run.py rename to scripts/sv-comp/sv-comp-run.py diff --git a/sv-comp/witness-isomorphism.py b/scripts/sv-comp/witness-isomorphism.py similarity index 100% rename from sv-comp/witness-isomorphism.py rename to scripts/sv-comp/witness-isomorphism.py diff --git a/sv-comp/yed-sv-comp.cnfx b/scripts/sv-comp/yed-sv-comp.cnfx similarity index 100% rename from sv-comp/yed-sv-comp.cnfx rename to scripts/sv-comp/yed-sv-comp.cnfx From b98438306a97e0a7431130d9bf1985fab526a266 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 15 Dec 2023 12:17:46 +0200 Subject: [PATCH 1265/1312] Move SV-COMP README to documentation --- docs/user-guide/inspecting.md | 17 +++++++++++++++++ docs/user-guide/running.md | 17 +++++++++++++++++ sv-comp/README.md | 28 ---------------------------- 3 files changed, 34 insertions(+), 28 deletions(-) delete mode 100644 sv-comp/README.md diff --git a/docs/user-guide/inspecting.md b/docs/user-guide/inspecting.md index f4f6036f1b..266a4866c6 100644 --- a/docs/user-guide/inspecting.md +++ b/docs/user-guide/inspecting.md @@ -23,3 +23,20 @@ To build GobView (also for development): `./_build/default/gobview/goblint-http-server/goblint_http.exe -with-goblint ../analyzer/goblint -goblint --set files[+] "../analyzer/tests/regression/00-sanity/01-assert.c"` 4. Visit + +## Witnesses + +### GraphML + +#### yEd + +1. Open (Ctrl+o) `witness.graphml` from Goblint root directory. +2. Click menu "Edit" → "Properties Mapper". + 1. _First time:_ Click button "Imports additional configurations" and open `scripts/sv-comp/yed-sv-comp.cnfx`. + 2. Select "SV-COMP (Node)" and click "Apply". + 3. Select "SV-COMP (Edge)" and click "Ok". +3. Click menu "Layout" → "Hierarchial" (Alt+shift+h). + 1. _First time:_ Click tab "Labeling", select "Hierarchic" in "Edge Labeling". + 2. Click "Ok". + +yEd manual for the Properties Mapper: . diff --git a/docs/user-guide/running.md b/docs/user-guide/running.md index 97d2587be8..aac1c21ca6 100644 --- a/docs/user-guide/running.md +++ b/docs/user-guide/running.md @@ -67,3 +67,20 @@ Here is a list of issues and workarounds for different compilation database gene #### bear 1. Bear 2.3.11 from Ubuntu 18.04 produces incomplete database (, ). * Bear 3.0.8 seems fine. + + +## SV-COMP +The most up-to-date SV-COMP configuration is in `conf/svcomp.json`. +There are also per-year configurations (e.g. `conf/svcomp24.json`) which try to reflect that year's submission using current option names. +Due to unconfigurable changes (e.g. bug fixes) these do not _exactly_ behave as that year's submission. +See SV-COMP submissions in GitHub releases for exact submitted versions. + +In SV-COMP Goblint is run as follows: +```console +./goblint --conf conf/svcomp.json --set ana.specification property.prp --set exp.architecture {32bit,64bit} input.c +``` + +Goblint YAML correctness witness validator is run as: +```console +./goblint --conf conf/svcomp.json --set ana.specification property.prp --set exp.architecture {32bit,64bit} --set witness.yaml.unassume witness.yml --set witness.yaml.validate witness.yml input.c +``` diff --git a/sv-comp/README.md b/sv-comp/README.md deleted file mode 100644 index 9f5c203213..0000000000 --- a/sv-comp/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# Goblint for SV-COMP -All the SV-COMP configuration is in `conf/svcomp.json`. - -## Run Goblint in SV-COMP mode -### ReachSafety -``` -./goblint --conf conf/svcomp.json --set ana.specification ../sv-benchmarks/c/properties/unreach-call.prp ../sv-benchmarks/c/DIR/FILE.i -``` - -### NoDataRace -``` -./goblint --conf conf/svcomp.json --set ana.specification ../sv-benchmarks/c/properties/no-data-race.prp ../sv-benchmarks/c/DIR/FILE.i -``` - - -# Inspecting witnesses -## yEd - -1. Open (Ctrl+o) `witness.graphml` from Goblint root directory. -2. Click menu "Edit" → "Properties Mapper". - 1. _First time:_ Click button "Imports additional configurations" and open `yed-sv-comp.cnfx` from this directory. - 2. Select "SV-COMP (Node)" and click "Apply". - 3. Select "SV-COMP (Edge)" and click "Ok". -3. Click menu "Layout" → "Hierarchial" (Alt+shift+h). - 1. _First time:_ Click tab "Labeling", select "Hierarchic" in "Edge Labeling". - 2. Click "Ok". - -yEd manual for the Properties Mapper: https://yed.yworks.com/support/manual/properties_mapper.html. From cbece4fbafdf662aefbe3fd503df48e4cfc31392 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 15 Dec 2023 12:18:36 +0200 Subject: [PATCH 1266/1312] Remove outdated my-bench-sv-comp --- sv-comp/my-bench-sv-comp/.gitignore | 1 - sv-comp/my-bench-sv-comp/README.md | 46 ------------ .../cpa-validate-correctness.xml | 25 ------- .../cpa-validate-violation.xml | 30 -------- sv-comp/my-bench-sv-comp/goblint-all-fast.sh | 26 ------- sv-comp/my-bench-sv-comp/goblint-all-fast.xml | 74 ------------------- sv-comp/my-bench-sv-comp/goblint-data-race.sh | 26 ------- .../my-bench-sv-comp/goblint-data-race.xml | 17 ----- sv-comp/my-bench-sv-comp/goblint-lint.sh | 42 ----------- sv-comp/my-bench-sv-comp/goblint-lint.xml | 68 ----------------- sv-comp/my-bench-sv-comp/goblint.sh | 63 ---------------- sv-comp/my-bench-sv-comp/goblint.xml | 38 ---------- .../table-generator-all-fast.xml | 17 ----- .../table-generator-data-race.xml | 13 ---- .../my-bench-sv-comp/table-generator-lint.xml | 16 ---- .../table-generator-witness.xml | 20 ----- .../uautomizer-validate-correctness.xml | 33 --------- .../uautomizer-validate-violation.xml | 32 -------- .../my-bench-sv-comp/witnesslint-validate.xml | 17 ----- .../witnesslint-validate2.xml | 31 -------- 20 files changed, 635 deletions(-) delete mode 100644 sv-comp/my-bench-sv-comp/.gitignore delete mode 100644 sv-comp/my-bench-sv-comp/README.md delete mode 100644 sv-comp/my-bench-sv-comp/cpa-validate-correctness.xml delete mode 100644 sv-comp/my-bench-sv-comp/cpa-validate-violation.xml delete mode 100755 sv-comp/my-bench-sv-comp/goblint-all-fast.sh delete mode 100644 sv-comp/my-bench-sv-comp/goblint-all-fast.xml delete mode 100755 sv-comp/my-bench-sv-comp/goblint-data-race.sh delete mode 100644 sv-comp/my-bench-sv-comp/goblint-data-race.xml delete mode 100755 sv-comp/my-bench-sv-comp/goblint-lint.sh delete mode 100644 sv-comp/my-bench-sv-comp/goblint-lint.xml delete mode 100755 sv-comp/my-bench-sv-comp/goblint.sh delete mode 100644 sv-comp/my-bench-sv-comp/goblint.xml delete mode 100644 sv-comp/my-bench-sv-comp/table-generator-all-fast.xml delete mode 100644 sv-comp/my-bench-sv-comp/table-generator-data-race.xml delete mode 100644 sv-comp/my-bench-sv-comp/table-generator-lint.xml delete mode 100644 sv-comp/my-bench-sv-comp/table-generator-witness.xml delete mode 100644 sv-comp/my-bench-sv-comp/uautomizer-validate-correctness.xml delete mode 100644 sv-comp/my-bench-sv-comp/uautomizer-validate-violation.xml delete mode 100644 sv-comp/my-bench-sv-comp/witnesslint-validate.xml delete mode 100644 sv-comp/my-bench-sv-comp/witnesslint-validate2.xml diff --git a/sv-comp/my-bench-sv-comp/.gitignore b/sv-comp/my-bench-sv-comp/.gitignore deleted file mode 100644 index 2eb047c8d6..0000000000 --- a/sv-comp/my-bench-sv-comp/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*-tmp.xml diff --git a/sv-comp/my-bench-sv-comp/README.md b/sv-comp/my-bench-sv-comp/README.md deleted file mode 100644 index b401a1898c..0000000000 --- a/sv-comp/my-bench-sv-comp/README.md +++ /dev/null @@ -1,46 +0,0 @@ -# my-bench-sv-comp -This directory contains BenchExec benchmark and table definitions for a number of use cases and shell scripts for running them. - -## goblint-all-fast -Run Goblint on a large number of reachability benchmarks with decreased timeout. - -Files: -* `goblint-all-fast.sh` -* `goblint-all-fast.xml` -* `table-generator-all-fast.xml` - - -## goblint-data-race -Run Goblint on data-race benchmarks. - -Files: -* `goblint-data-race.sh` -* `goblint-data-race.xml` -* `table-generator-data-race.xml` - - -## goblint-lint -Run Goblint and validate witnesses using witnesslinter. - -Files: -* `goblint-lint.sh` -* `goblint-lint.xml` -* `table-generator-lint.xml` -* `witnesslint-validate.xml` - - -## goblint -Run Goblint and validate witnesses using: -* CPAChecker, -* Ultimate Automizer, -* witnesslinter. - -Files: -* `cpa-validate-correctness.xml` -* `cpa-validate-violation.xml` -* `goblint.sh` -* `goblint.xml` -* `table-generator-witness.xml` -* `uautomizer-validate-correctness.xml` -* `uautomizer-validate-violation.xml` -* `witnesslint-validate2.xml` diff --git a/sv-comp/my-bench-sv-comp/cpa-validate-correctness.xml b/sv-comp/my-bench-sv-comp/cpa-validate-correctness.xml deleted file mode 100644 index dca5c52c6d..0000000000 --- a/sv-comp/my-bench-sv-comp/cpa-validate-correctness.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - **.graphml - - - - - - - RESULTSDIR/LOGDIR/${rundefinition_name}/${taskdef_name}/witness.graphml - - - - /home/simmo/benchexec/sv-benchmarks/c/SoftwareSystems-DeviceDriversLinux64-ReachSafety.set - /home/simmo/benchexec/sv-benchmarks/c/properties/unreach-call.prp - - - - diff --git a/sv-comp/my-bench-sv-comp/cpa-validate-violation.xml b/sv-comp/my-bench-sv-comp/cpa-validate-violation.xml deleted file mode 100644 index 8fcffd7321..0000000000 --- a/sv-comp/my-bench-sv-comp/cpa-validate-violation.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - **.graphml - - - - - - - - - - - - RESULTSDIR/LOGDIR/${rundefinition_name}/${taskdef_name}/witness.graphml - - - - /home/simmo/benchexec/sv-benchmarks/c/SoftwareSystems-DeviceDriversLinux64-ReachSafety.set - /home/simmo/benchexec/sv-benchmarks/c/properties/unreach-call.prp - - - - diff --git a/sv-comp/my-bench-sv-comp/goblint-all-fast.sh b/sv-comp/my-bench-sv-comp/goblint-all-fast.sh deleted file mode 100755 index c47ff10141..0000000000 --- a/sv-comp/my-bench-sv-comp/goblint-all-fast.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env bash - -shopt -s extglob - -MYBENCHDIR=/mnt/goblint-svcomp/benchexec/my-bench-sv-comp -RESULTSDIR=/mnt/goblint-svcomp/benchexec/results/70-all-fast-no-interval -GOBLINTPARALLEL=14 - -mkdir $RESULTSDIR - -# Run verification -cd /mnt/goblint-svcomp/sv-comp/goblint -# read-only and overlay dirs for Value too large for defined data type workaround -benchexec --read-only-dir / --overlay-dir . --hidden-dir /home --outputpath $RESULTSDIR --numOfThreads $GOBLINTPARALLEL $MYBENCHDIR/goblint-all-fast.xml - -# Extract witness directory -cd $RESULTSDIR -LOGDIR=`echo goblint*.files` -echo $LOGDIR - -# Generate table with merged results and witness validation results -sed -e "s/LOGDIR/$LOGDIR/" $MYBENCHDIR/table-generator-all-fast.xml > table-generator.xml -table-generator -x table-generator.xml - -# Decompress all tool outputs for table HTML links -unzip -o goblint*.logfiles.zip \ No newline at end of file diff --git a/sv-comp/my-bench-sv-comp/goblint-all-fast.xml b/sv-comp/my-bench-sv-comp/goblint-all-fast.xml deleted file mode 100644 index 6d4bb8fc3c..0000000000 --- a/sv-comp/my-bench-sv-comp/goblint-all-fast.xml +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - - - - - - - - - - - /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/ConcurrencySafety-Main.set - /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/properties/unreach-call.prp - /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/pthread-wmm/* - - - - - - /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/SoftwareSystems-DeviceDriversLinux64-ReachSafety.set - /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/properties/unreach-call.prp - - - - /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/SoftwareSystems-DeviceDriversLinux64Large-ReachSafety.set - /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/properties/unreach-call.prp - - - - diff --git a/sv-comp/my-bench-sv-comp/goblint-data-race.sh b/sv-comp/my-bench-sv-comp/goblint-data-race.sh deleted file mode 100755 index b42e69d5ce..0000000000 --- a/sv-comp/my-bench-sv-comp/goblint-data-race.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env bash - -shopt -s extglob - -MYBENCHDIR=/mnt/goblint-svcomp/benchexec/my-bench-sv-comp -RESULTSDIR=/mnt/goblint-svcomp/benchexec/results/data-race-results21-concurrencysafety-new -GOBLINTPARALLEL=14 - -mkdir $RESULTSDIR - -# Run verification -cd /mnt/goblint-svcomp/sv-comp/goblint -# read-only and overlay dirs for Value too large for defined data type workaround -benchexec --read-only-dir / --overlay-dir . --hidden-dir /home --outputpath $RESULTSDIR --numOfThreads $GOBLINTPARALLEL $MYBENCHDIR/goblint-data-race.xml - -# Extract witness directory -cd $RESULTSDIR -LOGDIR=`echo goblint*.files` -echo $LOGDIR - -# Generate table with merged results and witness validation results -sed -e "s/LOGDIR/$LOGDIR/" $MYBENCHDIR/table-generator-data-race.xml > table-generator.xml -table-generator -x table-generator.xml - -# Decompress all tool outputs for table HTML links -unzip -o goblint*.logfiles.zip \ No newline at end of file diff --git a/sv-comp/my-bench-sv-comp/goblint-data-race.xml b/sv-comp/my-bench-sv-comp/goblint-data-race.xml deleted file mode 100644 index f8c00b582a..0000000000 --- a/sv-comp/my-bench-sv-comp/goblint-data-race.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/NoDataRace-ConcurrencySafety.set - /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/properties/no-data-race.prp - - - - diff --git a/sv-comp/my-bench-sv-comp/goblint-lint.sh b/sv-comp/my-bench-sv-comp/goblint-lint.sh deleted file mode 100755 index bbd1270a31..0000000000 --- a/sv-comp/my-bench-sv-comp/goblint-lint.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/bash - -shopt -s extglob - -MYBENCHDIR=/mnt/goblint-svcomp/benchexec/my-bench-sv-comp -RESULTSDIR=/mnt/goblint-svcomp/benchexec/results/new-results28-all-fast-systems-witness-linter -GOBLINTPARALLEL=15 -VALIDATEPARALLEL=15 - -mkdir $RESULTSDIR - -# Run verification -cd /mnt/goblint-svcomp/sv-comp/goblint -# read-only and overlay dirs for Value too large for defined data type workaround -benchexec --read-only-dir / --overlay-dir . --hidden-dir /home --outputpath $RESULTSDIR --numOfThreads $GOBLINTPARALLEL $MYBENCHDIR/goblint-lint.xml - -# Extract witness directory -cd $RESULTSDIR -LOGDIR=`echo goblint*.files` -echo $LOGDIR - -# Construct validation XMLs -cd $MYBENCHDIR -# witnesslint -sed -e "s|RESULTSDIR|$RESULTSDIR|" -e "s/LOGDIR/$LOGDIR/" witnesslint-validate.xml > witnesslint-validate-tmp.xml - -# Run validation -# witnesslint -cd /mnt/goblint-svcomp/benchexec/tools/witnesslint -benchexec --read-only-dir / --overlay-dir . --hidden-dir /home --outputpath $RESULTSDIR --numOfThreads $VALIDATEPARALLEL $MYBENCHDIR/witnesslint-validate-tmp.xml - -# Merge witness validation results -cd $RESULTSDIR -python3 /mnt/goblint-svcomp/benchexec/benchexec/contrib/mergeBenchmarkSets.py goblint*.results.!(*merged*).xml.bz2 witnesslint-validate-tmp.*.results.*.xml.bz2 - -# Generate table with merged results and witness validation results -sed -e "s/LOGDIR/$LOGDIR/" $MYBENCHDIR/table-generator-lint.xml > table-generator.xml -table-generator -x table-generator.xml - -# Decompress all tool outputs for table HTML links -unzip -o goblint*.logfiles.zip -unzip -o witnesslint-validate-tmp.*.logfiles.zip \ No newline at end of file diff --git a/sv-comp/my-bench-sv-comp/goblint-lint.xml b/sv-comp/my-bench-sv-comp/goblint-lint.xml deleted file mode 100644 index 8cae0a2c69..0000000000 --- a/sv-comp/my-bench-sv-comp/goblint-lint.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - **.graphml - - - - - - - - - - - - - - /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/SoftwareSystems-DeviceDriversLinux64-ReachSafety.set - /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/properties/unreach-call.prp - - - - diff --git a/sv-comp/my-bench-sv-comp/goblint.sh b/sv-comp/my-bench-sv-comp/goblint.sh deleted file mode 100755 index eaf74350de..0000000000 --- a/sv-comp/my-bench-sv-comp/goblint.sh +++ /dev/null @@ -1,63 +0,0 @@ -#!/bin/bash - -shopt -s extglob - -MYBENCHDIR=/mnt/goblint-svcomp/benchexec/my-bench-sv-comp -RESULTSDIR=/mnt/goblint-svcomp/benchexec/results/new-results32-overflow -GOBLINTPARALLEL=15 -VALIDATEPARALLEL=4 # not enough memory for more - -mkdir $RESULTSDIR - -# Run verification -cd /mnt/goblint-svcomp/sv-comp/goblint -# read-only and overlay dirs for Value too large for defined data type workaround -benchexec --read-only-dir / --overlay-dir . --hidden-dir /home --outputpath $RESULTSDIR --numOfThreads $GOBLINTPARALLEL $MYBENCHDIR/goblint.xml - -# Extract witness directory -cd $RESULTSDIR -LOGDIR=`echo goblint.*.files` -echo $LOGDIR - -# Construct validation XMLs -cd $MYBENCHDIR -# witnesslint -sed -e "s|RESULTSDIR|$RESULTSDIR|" -e "s/LOGDIR/$LOGDIR/" witnesslint-validate2.xml > witnesslint-validate2-tmp.xml -# CPAChecker -# sed -e "s|RESULTSDIR|$RESULTSDIR|" -e "s/LOGDIR/$LOGDIR/" cpa-validate-correctness.xml > cpa-validate-correctness-tmp.xml -# sed -e "s|RESULTSDIR|$RESULTSDIR|" -e "s/LOGDIR/$LOGDIR/" cpa-validate-violation.xml > cpa-validate-violation-tmp.xml -# Ultimate -sed -e "s|RESULTSDIR|$RESULTSDIR|" -e "s/LOGDIR/$LOGDIR/" uautomizer-validate-correctness.xml > uautomizer-validate-correctness-tmp.xml -sed -e "s|RESULTSDIR|$RESULTSDIR|" -e "s/LOGDIR/$LOGDIR/" uautomizer-validate-violation.xml > uautomizer-validate-violation-tmp.xml - -# Run validation -# witnesslint -cd /mnt/goblint-svcomp/benchexec/tools/witnesslint -benchexec --read-only-dir / --overlay-dir . --hidden-dir /home --outputpath $RESULTSDIR --numOfThreads $VALIDATEPARALLEL $MYBENCHDIR/witnesslint-validate2-tmp.xml -# CPAChecker -# cd /home/simmo/benchexec/tools/CPAchecker-1.9-unix -# benchexec --outputpath $RESULTSDIR --numOfThreads $VALIDATEPARALLEL $MYBENCHDIR/cpa-validate-correctness-tmp.xml -# benchexec --outputpath $RESULTSDIR --numOfThreads $VALIDATEPARALLEL $MYBENCHDIR/cpa-validate-violation-tmp.xml -# Ultimate -cd /mnt/goblint-svcomp/benchexec/tools/UAutomizer-linux -benchexec --read-only-dir / --overlay-dir . --hidden-dir /home --outputpath $RESULTSDIR --numOfThreads $VALIDATEPARALLEL $MYBENCHDIR/uautomizer-validate-correctness-tmp.xml -benchexec --read-only-dir / --overlay-dir . --hidden-dir /home --outputpath $RESULTSDIR --numOfThreads $VALIDATEPARALLEL $MYBENCHDIR/uautomizer-validate-violation-tmp.xml - -# Merge witness validation results -cd $RESULTSDIR -# python3 /mnt/goblint-svcomp/benchexec/benchexec/contrib/mergeBenchmarkSets.py goblint.*.results.!(*merged*).xml.bz2 cpa-validate-correctness-tmp.*.results.*.xml.bz2 cpa-validate-violation-tmp.*.results.*.xml.bz2 uautomizer-validate-correctness-tmp.*.results.*.xml.bz2 uautomizer-validate-violation-tmp.*.results.*.xml.bz2 -# python3 /mnt/goblint-svcomp/benchexec/benchexec/contrib/mergeBenchmarkSets.py goblint.*.results.!(*merged*).xml.bz2 uautomizer-validate-correctness-tmp.*.results.*.xml.bz2 uautomizer-validate-violation-tmp.*.results.*.xml.bz2 witnesslint-validate2-tmp.*.results.*.xml.bz2 -python3 /mnt/goblint-svcomp/benchexec/benchexec/contrib/mergeBenchmarkSets.py goblint.*.results.*no-overflow.xml.bz2 uautomizer-validate-correctness-tmp.*.results.*no-overflow.xml.bz2 uautomizer-validate-violation-tmp.*.results.*no-overflow.xml.bz2 witnesslint-validate2-tmp.*.results.*no-overflow.xml.bz2 - -# Generate table with merged results and witness validation results -# table-generator goblint.*.results.*.xml.bz2.merged.xml.bz2 cpa-validate-correctness-tmp.*.results.*.xml.bz2 cpa-validate-violation-tmp.*.results.*.xml.bz2 uautomizer-validate-correctness-tmp.*.results.*.xml.bz2 uautomizer-validate-violation-tmp.*.results.*.xml.bz2 -sed -e "s/LOGDIR/$LOGDIR/" $MYBENCHDIR/table-generator-witness.xml > table-generator.xml -table-generator -x table-generator.xml - -# Decompress all tool outputs for table HTML links -unzip -o goblint.*.logfiles.zip -# unzip -o cpa-validate-correctness-tmp.*.logfiles.zip -# unzip -o cpa-validate-violation-tmp.*.logfiles.zip -unzip -o uautomizer-validate-correctness-tmp.*.logfiles.zip -unzip -o uautomizer-validate-violation-tmp.*.logfiles.zip -unzip -o witnesslint-validate2-tmp.*.logfiles.zip \ No newline at end of file diff --git a/sv-comp/my-bench-sv-comp/goblint.xml b/sv-comp/my-bench-sv-comp/goblint.xml deleted file mode 100644 index c5773f3569..0000000000 --- a/sv-comp/my-bench-sv-comp/goblint.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - **.graphml - - - - - - - - /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/NoOverflows-BitVectors.set - /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/properties/no-overflow.prp - - - /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/NoOverflows-Other.set - /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/properties/no-overflow.prp - - - - /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/SoftwareSystems-BusyBox-NoOverflows.set - /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/properties/no-overflow.prp - - - /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/SoftwareSystems-uthash-NoOverflows.set - /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/properties/no-overflow.prp - - - - - diff --git a/sv-comp/my-bench-sv-comp/table-generator-all-fast.xml b/sv-comp/my-bench-sv-comp/table-generator-all-fast.xml deleted file mode 100644 index c9b9932390..0000000000 --- a/sv-comp/my-bench-sv-comp/table-generator-all-fast.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - -
diff --git a/sv-comp/my-bench-sv-comp/table-generator-data-race.xml b/sv-comp/my-bench-sv-comp/table-generator-data-race.xml deleted file mode 100644 index 28410d1805..0000000000 --- a/sv-comp/my-bench-sv-comp/table-generator-data-race.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - -
diff --git a/sv-comp/my-bench-sv-comp/table-generator-lint.xml b/sv-comp/my-bench-sv-comp/table-generator-lint.xml deleted file mode 100644 index 6ca64dc84e..0000000000 --- a/sv-comp/my-bench-sv-comp/table-generator-lint.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - witness - - - - - - -
diff --git a/sv-comp/my-bench-sv-comp/table-generator-witness.xml b/sv-comp/my-bench-sv-comp/table-generator-witness.xml deleted file mode 100644 index 876c08d392..0000000000 --- a/sv-comp/my-bench-sv-comp/table-generator-witness.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - witness - - - - - - - - - - -
diff --git a/sv-comp/my-bench-sv-comp/uautomizer-validate-correctness.xml b/sv-comp/my-bench-sv-comp/uautomizer-validate-correctness.xml deleted file mode 100644 index efb0861775..0000000000 --- a/sv-comp/my-bench-sv-comp/uautomizer-validate-correctness.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - **.graphml - - diff --git a/sv-comp/my-bench-sv-comp/uautomizer-validate-violation.xml b/sv-comp/my-bench-sv-comp/uautomizer-validate-violation.xml deleted file mode 100644 index fdf61b1bab..0000000000 --- a/sv-comp/my-bench-sv-comp/uautomizer-validate-violation.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - **.graphml - - diff --git a/sv-comp/my-bench-sv-comp/witnesslint-validate.xml b/sv-comp/my-bench-sv-comp/witnesslint-validate.xml deleted file mode 100644 index 96a41ef731..0000000000 --- a/sv-comp/my-bench-sv-comp/witnesslint-validate.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - diff --git a/sv-comp/my-bench-sv-comp/witnesslint-validate2.xml b/sv-comp/my-bench-sv-comp/witnesslint-validate2.xml deleted file mode 100644 index 475bc9846e..0000000000 --- a/sv-comp/my-bench-sv-comp/witnesslint-validate2.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - From 3eadb60431a18538263a4e3537ea40e0c1d57c7f Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 15 Dec 2023 12:27:53 +0200 Subject: [PATCH 1267/1312] Remove old ocamldoc index file --- src/main.camldoc | 140 ----------------------------------------------- 1 file changed, 140 deletions(-) delete mode 100644 src/main.camldoc diff --git a/src/main.camldoc b/src/main.camldoc deleted file mode 100644 index 0a0e52035f..0000000000 --- a/src/main.camldoc +++ /dev/null @@ -1,140 +0,0 @@ - -This is the API of the Goblint static analyzer framework, developed at the Technische Universität München ({b TUM}) -and the University of Tartu ({b UT}). - -The API is divided into four logical sections: -the framework, constraint solvers, domains, and analysis instances. - -{2 Framework} -{!modules: -Maingoblint -Analyses -Constraints -Control -MyCFG -Version -Config -} - -{3 Util} -{!modules: -Cache -Cilfacade -Defaults -GobConfig -Goblintutil -Hash -Htmldump -Htmlutil -Json -Messages -MyLiveness -OilUtil -Printer -Questions -Report -Tracing -Xmldump -} - -{3 CIL components} -{!modules: -Cil -Pretty -} - -{2 Solvers} -{!modules: -EffectWCon -EffectWConEq -Generic -Interactive -SLR -Selector -SharirPnueli -TopDown -} - -{2 Domains} - -{!modules: - -ValueDomain -Basetype - -Exp -IntDomain -CircularInterval -ArrayDomain -StructDomain -UnionDomain - -Lval -AddressDomain -MemoryDomain -MusteqDomain -RegionDomain -ShapeDomain -ListDomain - -BaseDomain -ConcDomain -ContainDomain -EscapeDomain -FlagModeDomain -LockDomain -StackDomain -FileDomain -LvalMapDomain - -} - -{3 General Lattice Functors} - -{!modules: -Lattice -Printable -MapDomain -PartitionDomain -SetDomain -Queries -Glob -} - -{2 Analyses} -{!modules: -MCP -Base - -CondVars -Contain -Deadlock -DeadlocksByRaces -Depbase -Depmutex -FileUse -Flag -FlagModes -ImpVar -Malloc_null -MayLocks -MTFlag -Mutex -Region -Shapes -StackTrace -SymbLocks -Termination -ThreadEscape -Thread -Uninit -Unit -VarDep -VarEq - -LibraryFunctions -} - -{9 Indexes} - -{!indexlist} From 4cbfd1a97dd378a5550002b09c07aa3e50668d2e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 15 Dec 2023 12:55:20 +0200 Subject: [PATCH 1268/1312] Add bisect_ppx to extracted dune libraries --- src/common/dune | 3 ++- src/config/dune | 3 ++- src/domain/dune | 3 ++- src/incremental/dune | 3 ++- src/util/library/dune | 3 ++- src/util/std/dune | 3 ++- 6 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/common/dune b/src/common/dune index 7994798579..458ef02dcb 100644 --- a/src/common/dune +++ b/src/common/dune @@ -20,6 +20,7 @@ (pps ppx_deriving.std ppx_deriving_hash - ppx_deriving_yojson))) + ppx_deriving_yojson)) + (instrumentation (backend bisect_ppx))) (documentation) diff --git a/src/config/dune b/src/config/dune index 1508e2553e..ce5cb11559 100644 --- a/src/config/dune +++ b/src/config/dune @@ -18,6 +18,7 @@ (preprocess (pps ppx_blob)) - (preprocessor_deps (file options.schema.json))) + (preprocessor_deps (file options.schema.json)) + (instrumentation (backend bisect_ppx))) (documentation) diff --git a/src/domain/dune b/src/domain/dune index 169f4a1d5c..85e69a6246 100644 --- a/src/domain/dune +++ b/src/domain/dune @@ -14,6 +14,7 @@ (pps ppx_deriving.std ppx_deriving_hash - ppx_deriving_yojson))) + ppx_deriving_yojson)) + (instrumentation (backend bisect_ppx))) (documentation) diff --git a/src/incremental/dune b/src/incremental/dune index 595dba22f7..15c1d2a7af 100644 --- a/src/incremental/dune +++ b/src/incremental/dune @@ -17,6 +17,7 @@ (pps ppx_deriving.std ppx_deriving_hash - ppx_deriving_yojson))) + ppx_deriving_yojson)) + (instrumentation (backend bisect_ppx))) (documentation) diff --git a/src/util/library/dune b/src/util/library/dune index 075c01c35d..c7797db33f 100644 --- a/src/util/library/dune +++ b/src/util/library/dune @@ -13,6 +13,7 @@ (preprocess (pps ppx_deriving.std - ppx_deriving_hash))) + ppx_deriving_hash)) + (instrumentation (backend bisect_ppx))) (documentation) diff --git a/src/util/std/dune b/src/util/std/dune index b074a29937..2b814c677a 100644 --- a/src/util/std/dune +++ b/src/util/std/dune @@ -15,4 +15,5 @@ (pps ppx_deriving.std ppx_deriving_hash - ppx_deriving_yojson))) + ppx_deriving_yojson)) + (instrumentation (backend bisect_ppx))) From 8650d7282d75d131c4d15cb071ab16ea408a87f2 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 15 Dec 2023 16:54:35 +0200 Subject: [PATCH 1269/1312] Fix mismerge of Lincons1.num_vars usage in ed06c346dd7341c52fa7144ceaf51b0675768aef --- src/analyses/apron/relationAnalysis.apron.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index 5e128ffc30..e572755930 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -609,7 +609,7 @@ struct |> Enum.filter_map (fun (lincons1: Apron.Lincons1.t) -> (* filter one-vars and exact *) (* TODO: exact filtering doesn't really work with octagon because it returns two SUPEQ constraints instead *) - if (one_var || Apron.Linexpr0.get_size lincons1.lincons0.linexpr0 >= 2) && (exact || Apron.Lincons1.get_typ lincons1 <> EQ) then + if (one_var || GobApron.Lincons1.num_vars lincons1 >= 2) && (exact || Apron.Lincons1.get_typ lincons1 <> EQ) then RD.cil_exp_of_lincons1 lincons1 |> Option.map e_inv |> Option.filter (fun exp -> not (InvariantCil.exp_contains_tmp exp) && InvariantCil.exp_is_in_scope scope exp) From 87f7e02a69e9753155e3942e866881adab932aa6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Dec 2023 20:29:19 +0000 Subject: [PATCH 1270/1312] Bump actions/upload-pages-artifact from 2 to 3 Bumps [actions/upload-pages-artifact](https://github.com/actions/upload-pages-artifact) from 2 to 3. - [Release notes](https://github.com/actions/upload-pages-artifact/releases) - [Commits](https://github.com/actions/upload-pages-artifact/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/upload-pages-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 1d73e037f4..f793fa4c0d 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -55,7 +55,7 @@ jobs: run: opam exec -- dune build @doc - name: Upload artifact - uses: actions/upload-pages-artifact@v2 + uses: actions/upload-pages-artifact@v3 with: path: _build/default/_doc/_html/ From 502923b921c412d31ce3ca30a4b18f78d09989dc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Dec 2023 20:29:19 +0000 Subject: [PATCH 1271/1312] Bump actions/deploy-pages from 3 to 4 Bumps [actions/deploy-pages](https://github.com/actions/deploy-pages) from 3 to 4. - [Release notes](https://github.com/actions/deploy-pages/releases) - [Commits](https://github.com/actions/deploy-pages/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/deploy-pages dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 1d73e037f4..dedfe44ef8 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -68,4 +68,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v3 + uses: actions/deploy-pages@v4 From 9f5de689ba9aa0fa38936a0cdfdd5014a2489851 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 20 Dec 2023 14:09:48 +0200 Subject: [PATCH 1272/1312] Revert "Bump actions/upload-pages-artifact from 2 to 3" This reverts commit 87f7e02a69e9753155e3942e866881adab932aa6. --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index d1f7fb09e0..dedfe44ef8 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -55,7 +55,7 @@ jobs: run: opam exec -- dune build @doc - name: Upload artifact - uses: actions/upload-pages-artifact@v3 + uses: actions/upload-pages-artifact@v2 with: path: _build/default/_doc/_html/ From df2b39a1c57c9cc4b7f4466bdbc842749db909b8 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 20 Dec 2023 14:09:52 +0200 Subject: [PATCH 1273/1312] Revert "Bump actions/deploy-pages from 3 to 4" This reverts commit 502923b921c412d31ce3ca30a4b18f78d09989dc. --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index dedfe44ef8..1d73e037f4 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -68,4 +68,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@v3 From 8bb2c5f3c521713be593200dbf81a0c2ff6e8a40 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Wed, 27 Dec 2023 11:48:47 +0200 Subject: [PATCH 1274/1312] Add test for suppressing thread-unsafe lib fun calls in single-threaded mode #1260 --- .../00-sanity/52-thread-unsafe-libfuns-single-thread.c | 8 ++++++++ .../00-sanity/52-thread-unsafe-libfuns-single-thread.t | 5 +++++ 2 files changed, 13 insertions(+) create mode 100644 tests/regression/00-sanity/52-thread-unsafe-libfuns-single-thread.c create mode 100644 tests/regression/00-sanity/52-thread-unsafe-libfuns-single-thread.t diff --git a/tests/regression/00-sanity/52-thread-unsafe-libfuns-single-thread.c b/tests/regression/00-sanity/52-thread-unsafe-libfuns-single-thread.c new file mode 100644 index 0000000000..a83d9eeeb0 --- /dev/null +++ b/tests/regression/00-sanity/52-thread-unsafe-libfuns-single-thread.c @@ -0,0 +1,8 @@ +// PARAM: --enable allglobs + +#include + +int main() { + rand(); + return 0; +} \ No newline at end of file diff --git a/tests/regression/00-sanity/52-thread-unsafe-libfuns-single-thread.t b/tests/regression/00-sanity/52-thread-unsafe-libfuns-single-thread.t new file mode 100644 index 0000000000..0914c25439 --- /dev/null +++ b/tests/regression/00-sanity/52-thread-unsafe-libfuns-single-thread.t @@ -0,0 +1,5 @@ + $ goblint --enable allglobs 52-thread-unsafe-libfuns-single-thread.c + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 3 + dead: 0 + total lines: 3 From b7e43c505cf3fa98f91f2a595a646a10be06d500 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Wed, 27 Dec 2023 11:49:33 +0200 Subject: [PATCH 1275/1312] Do not record thread-unsafe lib fun calls in single-threaded mode #1260 --- src/analyses/raceAnalysis.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index f35e6756a1..5e03c6bfab 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -369,7 +369,7 @@ struct let special ctx (lvalOpt: lval option) (f:varinfo) (arglist:exp list) : D.t = (* perform shallow and deep invalidate according to Library descriptors *) let desc = LibraryFunctions.find f in - if List.mem LibraryDesc.ThreadUnsafe desc.attrs then ( + if List.mem LibraryDesc.ThreadUnsafe desc.attrs && not (ctx.ask (Queries.MustBeSingleThreaded {since_start=true})) then ( let exp = Lval (Var f, NoOffset) in let conf = 110 in let kind = AccessKind.Call in From ee33a8050f7ede2e9f1c5a0fb21906a321b67c70 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Wed, 27 Dec 2023 11:51:17 +0200 Subject: [PATCH 1276/1312] Fix old cram test according to new implementation --- tests/regression/29-svcomp/32-no-ov.t | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/regression/29-svcomp/32-no-ov.t b/tests/regression/29-svcomp/32-no-ov.t index 85eb90c185..1dc22ed89e 100644 --- a/tests/regression/29-svcomp/32-no-ov.t +++ b/tests/regression/29-svcomp/32-no-ov.t @@ -9,8 +9,3 @@ dead: 0 total lines: 3 SV-COMP result: true - [Info][Race] Memory locations race summary: - safe: 1 - vulnerable: 0 - unsafe: 0 - total memory locations: 1 From c26c83e8fca0ad859e405a8fa0c65cf1028a7998 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Wed, 27 Dec 2023 17:20:31 +0200 Subject: [PATCH 1277/1312] Make test for suppressing thread-unsafe lib fun calls check more cases --- .../52-thread-unsafe-libfuns-single-thread.c | 12 ++++++++++-- .../52-thread-unsafe-libfuns-single-thread.t | 6 +++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/tests/regression/00-sanity/52-thread-unsafe-libfuns-single-thread.c b/tests/regression/00-sanity/52-thread-unsafe-libfuns-single-thread.c index a83d9eeeb0..94c0f3efeb 100644 --- a/tests/regression/00-sanity/52-thread-unsafe-libfuns-single-thread.c +++ b/tests/regression/00-sanity/52-thread-unsafe-libfuns-single-thread.c @@ -1,8 +1,16 @@ -// PARAM: --enable allglobs - +// PARAM: --enable allglobs --set ana.activated[+] threadJoins #include +#include + +void *t_benign(void *arg) { + return NULL; +} int main() { + rand(); + pthread_t id; + pthread_create(&id, NULL, t_benign, NULL); + pthread_join(id, NULL); rand(); return 0; } \ No newline at end of file diff --git a/tests/regression/00-sanity/52-thread-unsafe-libfuns-single-thread.t b/tests/regression/00-sanity/52-thread-unsafe-libfuns-single-thread.t index 0914c25439..64413bae36 100644 --- a/tests/regression/00-sanity/52-thread-unsafe-libfuns-single-thread.t +++ b/tests/regression/00-sanity/52-thread-unsafe-libfuns-single-thread.t @@ -1,5 +1,5 @@ - $ goblint --enable allglobs 52-thread-unsafe-libfuns-single-thread.c + $ goblint --enable allglobs --set ana.activated[+] threadJoins 52-thread-unsafe-libfuns-single-thread.c [Info][Deadcode] Logical lines of code (LLoC) summary: - live: 3 + live: 8 dead: 0 - total lines: 3 + total lines: 8 From ecd0bc5452dde4e7aa8c2200c809f9470836fe13 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Wed, 27 Dec 2023 17:33:06 +0200 Subject: [PATCH 1278/1312] Do not record thread-unsafe lib fun calls after all threads have joined --- src/analyses/raceAnalysis.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index 5e03c6bfab..6b7217147e 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -369,7 +369,7 @@ struct let special ctx (lvalOpt: lval option) (f:varinfo) (arglist:exp list) : D.t = (* perform shallow and deep invalidate according to Library descriptors *) let desc = LibraryFunctions.find f in - if List.mem LibraryDesc.ThreadUnsafe desc.attrs && not (ctx.ask (Queries.MustBeSingleThreaded {since_start=true})) then ( + if List.mem LibraryDesc.ThreadUnsafe desc.attrs && ThreadFlag.is_currently_multi (Analyses.ask_of_ctx ctx) then ( let exp = Lval (Var f, NoOffset) in let conf = 110 in let kind = AccessKind.Call in From a5d0f39537933c3a3464cb386f2f9e82c668acc7 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 27 Dec 2023 18:38:06 +0100 Subject: [PATCH 1279/1312] Code cleanup --- .../apron/affineEqualityDomain.apron.ml | 109 +++++++++++------- 1 file changed, 69 insertions(+), 40 deletions(-) diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index 5aa1090dd4..5cd7714682 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -291,15 +291,21 @@ struct let meet t1 t2 = let sup_env = Environment.lce t1.env t2.env in - let t1, t2 = change_d t1 sup_env true false, change_d t2 sup_env true false - in if is_bot t1 || is_bot t2 then bot() else + let t1, t2 = change_d t1 sup_env true false, change_d t2 sup_env true false in + if is_bot t1 || is_bot t2 then + bot () + else + (* TODO: Why can I be sure that m1 && m2 are all Some here? *) let m1, m2 = Option.get t1.d, Option.get t2.d in - match m1, m2 with - | x, y when is_top_env t1-> {d = Some (dim_add (Environment.dimchange t2.env sup_env) y); env = sup_env} - | x, y when is_top_env t2 -> {d = Some (dim_add (Environment.dimchange t1.env sup_env) x); env = sup_env} - | x, y -> - let rref_matr = Matrix.rref_matrix_with (Matrix.copy x) (Matrix.copy y) in - if Option.is_none rref_matr then bot () else + if is_top_env t1 then + {d = Some (dim_add (Environment.dimchange t2.env sup_env) m2); env = sup_env} + else if is_top_env t2 then + {d = Some (dim_add (Environment.dimchange t1.env sup_env) m1); env = sup_env} + else + let rref_matr = Matrix.rref_matrix_with (Matrix.copy m1) (Matrix.copy m2) in + if Option.is_none rref_matr then + bot () + else {d = rref_matr; env = sup_env} @@ -312,12 +318,20 @@ struct let leq t1 t2 = let env_comp = Environment.compare t1.env t2.env in (* Apron's Environment.compare has defined return values. *) - if env_comp = -2 || env_comp > 0 then false else - if is_bot t1 || is_top_env t2 then true else - if is_bot t2 || is_top_env t1 then false else ( + if env_comp = -2 || env_comp > 0 then + (* -2: environments are not compatible (a variable has different types in the 2 environements *) + (* -1: if env1 is a subset of env2, (OK) *) + (* 0: if equality, (OK) *) + (* +1: if env1 is a superset of env2, and +2 otherwise (the lce exists and is a strict superset of both) *) + false + else if is_bot t1 || is_top_env t2 then + true + else if is_bot t2 || is_top_env t1 then + false + else let m1, m2 = Option.get t1.d, Option.get t2.d in let m1' = if env_comp = 0 then m1 else dim_add (Environment.dimchange t1.env t2.env) m1 in - Matrix.is_covered_by m2 m1') + Matrix.is_covered_by m2 m1' let leq a b = timing_wrap "leq" (leq a) b @@ -371,7 +385,11 @@ struct lin_disjunc new_r (s + 1) new_a new_b | _ -> failwith "Matrix not in rref form" end in - if is_bot a then b else if is_bot b then a else + if is_bot a then + b + else if is_bot b then + a + else match Option.get a.d, Option.get b.d with | x, y when is_top_env a || is_top_env b -> {d = Some (Matrix.empty ()); env = Environment.lce a.env b.env} | x, y when (Environment.compare a.env b.env <> 0) -> @@ -388,33 +406,34 @@ struct let res = join a b in if M.tracing then M.tracel "join" "join a: %s b: %s -> %s \n" (show a) (show b) (show res) ; res + let widen a b = - let a_env = a.env in - let b_env = b.env in - if Environment.equal a_env b_env then + if Environment.equal a.env b.env then join a b - else b + else + b let narrow a b = a + let pretty_diff () (x, y) = dprintf "%s: %a not leq %a" (name ()) pretty x pretty y - let remove_rels_with_var x var env imp = + let remove_rels_with_var x var env inplace = let j0 = Environment.dim_of_var env var in - if imp then (Matrix.reduce_col_with x j0; x) else Matrix.reduce_col x j0 + if inplace then + (Matrix.reduce_col_with x j0; x) + else + Matrix.reduce_col x j0 - let remove_rels_with_var x var env imp = timing_wrap "remove_rels_with_var" (remove_rels_with_var x var env) imp + let remove_rels_with_var x var env inplace = timing_wrap "remove_rels_with_var" (remove_rels_with_var x var env) inplace let forget_vars t vars = - if is_bot t || is_top_env t then t + if is_bot t || is_top_env t || List.is_empty vars then + t else let m = Option.get t.d in - if List.is_empty vars then t else - let rec rem_vars m vars' = - begin match vars' with - | [] -> m - | x :: xs -> rem_vars (remove_rels_with_var m x t.env true) xs end - in {d = Some (Matrix.remove_zero_rows @@ rem_vars (Matrix.copy m) vars); env = t.env} + let rem_from m = List.fold_left (fun m' x -> remove_rels_with_var m' x t.env true) m vars in + {d = Some (Matrix.remove_zero_rows @@ rem_from (Matrix.copy m)); env = t.env} let forget_vars t vars = let res = forget_vars t vars in @@ -472,6 +491,7 @@ struct if M.tracing then M.tracel "ops" "assign_exp t:\n %s \n var: %s \n exp: %a\n no_ov: %b -> \n %s\n" (show t) (Var.to_string var) d_exp exp (Lazy.force no_ov) (show res) ; res + let assign_var (t: VarManagement(Vc)(Mx).t) v v' = let t = add_vars t [v; v'] in let texpr1 = Texpr1.of_expr (t.env) (Var v') in @@ -489,14 +509,20 @@ struct let t_primed = add_vars t primed_vars in let multi_t = List.fold_left2 (fun t' v_prime (_,v') -> assign_var t' v_prime v') t_primed primed_vars vv's in match multi_t.d with - | Some m when not @@ is_top_env multi_t -> let replace_col m x y = let dim_x, dim_y = Environment.dim_of_var multi_t.env x, Environment.dim_of_var multi_t.env y in - let col_x = Matrix.get_col m dim_x in - Matrix.set_col_with m col_x dim_y in + | Some m when not @@ is_top_env multi_t -> + let replace_col m x y = + let dim_x, dim_y = Environment.dim_of_var multi_t.env x, Environment.dim_of_var multi_t.env y in + let col_x = Matrix.get_col m dim_x in + Matrix.set_col_with m col_x dim_y + in let m_cp = Matrix.copy m in - let switched_m = List.fold_left2 (fun m' x y -> replace_col m' x y) m_cp primed_vars assigned_vars in + let switched_m = List.fold_left2 replace_col m_cp primed_vars assigned_vars in let res = drop_vars {d = Some switched_m; env = multi_t.env} primed_vars true in let x = Option.get res.d in - if Matrix.normalize_with x then {d = Some x; env = res.env} else bot () + if Matrix.normalize_with x then + {d = Some x; env = res.env} + else + bot () | _ -> t let assign_var_parallel t vv's = @@ -561,14 +587,17 @@ struct | _, _ -> overflow_res res let meet_tcons t tcons expr = - let check_const cmp c = if cmp c Mpqf.zero then bot_env else t - in + let check_const cmp c = if cmp c Mpqf.zero then bot_env else t in let meet_vec e = (*Flip the sign of the const. val in coeff vec*) Vector.mapi_with (fun i x -> if Vector.compare_length_with e (i + 1) = 0 then Mpqf.mone *: x else x) e; - let res = if is_bot t then bot () else - let opt_m = Matrix.rref_vec_with (Matrix.copy @@ Option.get t.d) e - in if Option.is_none opt_m then bot () else {d = opt_m; env = t.env} in + let res = + if is_bot t then + bot () + else + let opt_m = Matrix.rref_vec_with (Matrix.copy @@ Option.get t.d) e in + if Option.is_none opt_m then bot () else {d = opt_m; env = t.env} + in meet_tcons_one_var_eq res expr in match get_coeff_vec t (Texpr1.to_expr @@ Tcons1.get_texpr1 tcons) with @@ -615,9 +644,7 @@ struct let relift t = t let invariant t = - match t.d with - | None -> [] - | Some m -> + let invariant m = let earray = Lincons1.array_make t.env (Matrix.num_rows m) in for i = 0 to Lincons1.array_length earray do let row = Matrix.get_row m i in @@ -631,6 +658,8 @@ struct Lincons1.{lincons0; env = array_env} ) |> List.of_enum + in + BatOption.map_default invariant [] t.d let cil_exp_of_lincons1 = Convert.cil_exp_of_lincons1 From fe4a58f5153a7edbfae229a35d0b393d93ab93cc Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 27 Dec 2023 18:57:27 +0100 Subject: [PATCH 1280/1312] Fix constant printing --- .../apron/affineEqualityDomain.apron.ml | 38 +++++++++++-------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index 5cd7714682..4f47f6f494 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -243,31 +243,37 @@ struct Vector.of_array @@ row in let vec_to_constraint vec env = - let vars, _ = Environment.vars env - in let dim_to_str var = - let vl = Vector.nth vec (Environment.dim_of_var env var) - in let var_str = Var.to_string var - in if vl =: Mpqf.one then "+" ^ var_str - else if vl =: Mpqf.mone then "-" ^ var_str - else if vl <: Mpqf.mone then Mpqf.to_string vl ^ var_str - else if vl >: Mpqf.one then Format.asprintf "+%s" (Mpqf.to_string vl) ^ var_str - else "" + let vars, _ = Environment.vars env in + let dim_to_str var = + let vl = Vector.nth vec (Environment.dim_of_var env var) in + let var_str = Var.to_string var in + if vl =: Mpqf.one then "+" ^ var_str + else if vl =: Mpqf.mone then "-" ^ var_str + else if vl <: Mpqf.mone then Mpqf.to_string vl ^ var_str + else if vl >: Mpqf.one then Format.asprintf "+%s" (Mpqf.to_string vl) ^ var_str + else "" in let c_to_str vl = - if vl >: Mpqf.zero then "-" ^ Mpqf.to_string vl - else if vl <: Mpqf.zero then "+" ^ Mpqf.to_string vl - else "" + if vl =: Mpqf.zero then + "" + else + let negated = vl *: Mpqf.mone in + if negated >: Mpqf.zero then "+" ^ Mpqf.to_string negated + else Mpqf.to_string negated in let res = (String.concat "" @@ Array.to_list @@ Array.map dim_to_str vars) - ^ (c_to_str @@ Vector.nth vec (Vector.length vec - 1)) ^ "=0" - in if String.starts_with res "+" then String.sub res 1 (String.length res - 1) else res + ^ (c_to_str @@ Vector.nth vec (Vector.length vec - 1)) ^ "=0" in + if String.starts_with res "+" then + String.sub res 1 (String.length res - 1) + else + res in match t.d with | None -> "Bottom Env" | Some m when Matrix.is_empty m -> "⊤" | Some m -> - let constraint_list = List.init (Matrix.num_rows m) (fun i -> vec_to_constraint (conv_to_ints @@ Matrix.get_row m i) t.env) - in Format.asprintf "%s" ("[|"^ (String.concat "; " constraint_list) ^"|]") + let constraint_list = List.init (Matrix.num_rows m) (fun i -> vec_to_constraint (conv_to_ints @@ Matrix.get_row m i) t.env) in + Format.asprintf "%s" ("[|"^ (String.concat "; " constraint_list) ^"|]") let pretty () (x:t) = text (show x) let printXml f x = BatPrintf.fprintf f "\n\n\nmatrix\n\n\n%s\n\nenv\n\n\n%s\n\n\n" (XmlUtil.escape (Format.asprintf "%s" (show x) )) (XmlUtil.escape (Format.asprintf "%a" (Environment.print: Format.formatter -> Environment.t -> unit) (x.env))) From 31065ed0addc6471416ae81b8b915f04dda9eb42 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 27 Dec 2023 19:47:11 +0100 Subject: [PATCH 1281/1312] Make computations in show directly on Z --- .../apron/affineEqualityDomain.apron.ml | 50 ++++++++++--------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index 4f47f6f494..a1653bb423 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -230,39 +230,41 @@ struct let show t = let conv_to_ints row = - let module BI = IntOps.BigIntOps in - let row = Array.copy @@ Vector.to_array row - in - for i = 0 to Array.length row -1 do - let val_i = Mpqf.of_mpz @@ Z_mlgmpidl.mpzf_of_z @@ Mpqf.get_den row.(i) - in Array.iteri(fun j x -> row.(j) <- val_i *: x) row - done; - let int_arr = Array.init (Array.length row) (fun i -> Mpqf.get_num row.(i)) - in let div = Mpqf.of_mpz @@ Z_mlgmpidl.mpzf_of_z @@ Array.fold_left BI.gcd int_arr.(0) int_arr - in Array.iteri (fun i x -> row.(i) <- x /: div) row; - Vector.of_array @@ row + let row = Array.copy @@ Vector.to_array row in + let mpqf_of_z x = Mpqf.of_mpz @@ Z_mlgmpidl.mpzf_of_z x in + let lcm = mpqf_of_z @@ Array.fold_left (fun x y -> Z.lcm x (Mpqf.get_den y)) Z.one row in + Array.modify (fun x -> x *: lcm) row; + let int_arr = Array.map (fun x -> Mpqf.get_num x) row in + let div = Array.fold_left Z.gcd int_arr.(0) int_arr in + Array.modify (fun x -> Z.div x div) int_arr; + int_arr in - let vec_to_constraint vec env = + let vec_to_constraint arr env = let vars, _ = Environment.vars env in let dim_to_str var = - let vl = Vector.nth vec (Environment.dim_of_var env var) in + let vl = arr.(Environment.dim_of_var env var) in let var_str = Var.to_string var in - if vl =: Mpqf.one then "+" ^ var_str - else if vl =: Mpqf.mone then "-" ^ var_str - else if vl <: Mpqf.mone then Mpqf.to_string vl ^ var_str - else if vl >: Mpqf.one then Format.asprintf "+%s" (Mpqf.to_string vl) ^ var_str - else "" + if Z.equal vl Z.zero then + "" + else if Z.equal vl Z.one then + "+" ^ var_str + else if Z.equal vl Z.minus_one then + "-" ^ var_str + else if Z.lt vl Z.minus_one then + Z.to_string vl ^ var_str + else + Format.asprintf "+%s" (Z.to_string vl) ^ var_str in - let c_to_str vl = - if vl =: Mpqf.zero then + let const_to_str vl = + if Z.equal vl Z.zero then "" else - let negated = vl *: Mpqf.mone in - if negated >: Mpqf.zero then "+" ^ Mpqf.to_string negated - else Mpqf.to_string negated + let negated = Z.mul vl Z.minus_one in + if Z.gt negated Z.zero then "+" ^ Z.to_string negated + else Z.to_string negated in let res = (String.concat "" @@ Array.to_list @@ Array.map dim_to_str vars) - ^ (c_to_str @@ Vector.nth vec (Vector.length vec - 1)) ^ "=0" in + ^ (const_to_str arr.(Array.length arr - 1)) ^ "=0" in if String.starts_with res "+" then String.sub res 1 (String.length res - 1) else From 29b8ca2f0d60e0654f83dc7b158f50064406879c Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 27 Dec 2023 20:00:52 +0100 Subject: [PATCH 1282/1312] A bit more refactoring --- .../apron/affineEqualityDomain.apron.ml | 85 ++++++++++--------- 1 file changed, 43 insertions(+), 42 deletions(-) diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index a1653bb423..6c5112c279 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -59,7 +59,9 @@ struct let dim_add ch m = timing_wrap "dim add" (dim_add ch) m let dim_remove (ch: Apron.Dim.change) m del = - if Array.length ch.dim = 0 || Matrix.is_empty m then m else ( + if Array.length ch.dim = 0 || Matrix.is_empty m then + m + else ( Array.iteri (fun i x-> ch.dim.(i) <- x + i) ch.dim; let m' = if not del then let m = Matrix.copy m in Array.fold_left (fun y x -> Matrix.reduce_col_with y x; y) m ch.dim else m in Matrix.remove_zero_rows @@ Matrix.del_cols m' ch.dim) @@ -146,47 +148,46 @@ struct let is_const_vec v = Vector.compare_length_with (Vector.filteri (fun i x -> (*Inefficient*) Vector.compare_length_with v (i + 1) > 0 && x <>: Mpqf.zero) v) 1 = 0 in - let rec convert_texpr texp = - begin match texp with - (*If x is a constant, replace it with its const. val. immediately*) - | Cst x -> let of_union union = - let open Coeff in - match union with - | Interval _ -> failwith "Not a constant" - | Scalar x -> (match x with - | Float x -> Mpqf.of_float x - | Mpqf x -> x - | Mpfrf x -> Mpfr.to_mpq x) in Vector.set_val zero_vec ((Vector.length zero_vec) - 1) (of_union x) - | Var x -> - let zero_vec_cp = Vector.copy zero_vec in - let entry_only v = Vector.set_val_with v (Environment.dim_of_var t.env x) Mpqf.one; v in - begin match t.d with - | Some m -> let row = Matrix.find_opt (fun r -> Vector.nth r (Environment.dim_of_var t.env x) =: Mpqf.one) m in - begin match row with - | Some v when is_const_vec v -> - Vector.set_val_with zero_vec_cp ((Vector.length zero_vec) - 1) (Vector.nth v (Vector.length v - 1)); zero_vec_cp - | _ -> entry_only zero_vec_cp end - | None -> entry_only zero_vec_cp end - | Unop (u, e, _, _) -> - begin match u with - | Neg -> neg @@ convert_texpr e - | Cast -> convert_texpr e (*Ignore since casts in apron are used for floating point nums and rounding in contrast to CIL casts*) - | Sqrt -> raise NotLinear end - | Binop (b, e1, e2, _, _) -> - begin match b with - | Add -> let v1 = convert_texpr e1 in Vector.map2_with (+:) v1 (convert_texpr e2); v1 - | Sub -> let v1 = convert_texpr e1 in Vector.map2_with (+:) v1 (neg @@ convert_texpr e2); v1 - | Mul -> - let x1, x2 = convert_texpr e1, convert_texpr e2 in - begin match get_c x1, get_c x2 with - | _, Some c -> Vector.apply_with_c_with ( *:) c x1; x1 - | Some c, _ -> Vector.apply_with_c_with ( *:) c x2; x2 - | _, _ -> raise NotLinear end - | _ -> raise NotLinear end - end - in match convert_texpr texp with - | exception NotLinear -> None - | x -> Some(x) + let rec convert_texpr = function + (*If x is a constant, replace it with its const. val. immediately*) + | Cst x -> + let of_union = function + | Coeff.Interval _ -> failwith "Not a constant" + | Scalar Float x -> Mpqf.of_float x + | Scalar Mpqf x -> x + | Scalar Mpfrf x -> Mpfr.to_mpq x + in + Vector.set_val zero_vec ((Vector.length zero_vec) - 1) (of_union x) + | Var x -> + let zero_vec_cp = Vector.copy zero_vec in + let entry_only v = Vector.set_val_with v (Environment.dim_of_var t.env x) Mpqf.one; v in + begin match t.d with + | Some m -> + let row = Matrix.find_opt (fun r -> Vector.nth r (Environment.dim_of_var t.env x) =: Mpqf.one) m in + begin match row with + | Some v when is_const_vec v -> + Vector.set_val_with zero_vec_cp ((Vector.length zero_vec) - 1) (Vector.nth v (Vector.length v - 1)); zero_vec_cp + | _ -> entry_only zero_vec_cp + end + | None -> entry_only zero_vec_cp end + | Unop (Neg, e, _, _) -> neg @@ convert_texpr e + | Unop (Cast, e, _, _) -> convert_texpr e (*Ignore since casts in apron are used for floating point nums and rounding in contrast to CIL casts*) + | Unop (Sqrt, e, _, _) -> raise NotLinear + | Binop (b, e1, e2, _, _) -> + begin match b with + | Add -> let v1 = convert_texpr e1 in Vector.map2_with (+:) v1 (convert_texpr e2); v1 + | Sub -> let v1 = convert_texpr e1 in Vector.map2_with (+:) v1 (neg @@ convert_texpr e2); v1 + | Mul -> + let x1, x2 = convert_texpr e1, convert_texpr e2 in + begin match get_c x1, get_c x2 with + | _, Some c -> Vector.apply_with_c_with ( *:) c x1; x1 + | Some c, _ -> Vector.apply_with_c_with ( *:) c x2; x2 + | _, _ -> raise NotLinear end + | _ -> raise NotLinear end + in + try + Some (convert_texpr texp) + with NotLinear -> None let get_coeff_vec t texp = timing_wrap "coeff_vec" (get_coeff_vec t) texp end From 4f113e1618883ca8a193c40e2481aab14726049e Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 27 Dec 2023 20:12:36 +0100 Subject: [PATCH 1283/1312] Use modifyi where appropriate --- src/cdomains/apron/affineEqualityDomain.apron.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index 6c5112c279..9febdb5778 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -53,7 +53,7 @@ struct let copy t = {t with d = Option.map Matrix.copy t.d} let dim_add (ch: Apron.Dim.change) m = - Array.iteri (fun i x -> ch.dim.(i) <- x + i) ch.dim; + Array.modifyi (fun i x -> x + i) ch.dim; (* could be written Array.modifyi (+) ch.dim; but that's too smart *) Matrix.add_empty_columns m ch.dim let dim_add ch m = timing_wrap "dim add" (dim_add ch) m @@ -62,7 +62,7 @@ struct if Array.length ch.dim = 0 || Matrix.is_empty m then m else ( - Array.iteri (fun i x-> ch.dim.(i) <- x + i) ch.dim; + Array.modifyi (fun i x -> x + i) ch.dim; let m' = if not del then let m = Matrix.copy m in Array.fold_left (fun y x -> Matrix.reduce_col_with y x; y) m ch.dim else m in Matrix.remove_zero_rows @@ Matrix.del_cols m' ch.dim) From e874d5de5aa6d65e26f1560c18bb3cb5e9c0d4f5 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 27 Dec 2023 20:26:10 +0100 Subject: [PATCH 1284/1312] Some formatting --- .../apron/affineEqualityDomain.apron.ml | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index 9febdb5778..ecd4bdc1d5 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -69,12 +69,19 @@ struct let dim_remove ch m del = timing_wrap "dim remove" (dim_remove ch m) del let change_d t new_env add del = - if Environment.equal t.env new_env then t else - let dim_change = if add then Environment.dimchange t.env new_env - else Environment.dimchange new_env t.env - in match t.d with + if Environment.equal t.env new_env then + t + else + match t.d with | None -> bot_env - | Some m -> {d = Some (if add then dim_add dim_change m else dim_remove dim_change m del); env = new_env} + | Some m -> + let dim_change = + if add then + Environment.dimchange t.env new_env + else + Environment.dimchange new_env t.env + in + {d = Some (if add then dim_add dim_change m else dim_remove dim_change m del); env = new_env} let change_d t new_env add del = timing_wrap "dimension change" (change_d t new_env add) del @@ -133,7 +140,8 @@ struct include ConvenienceOps(Mpqf) - let get_c v = match Vector.findi (fun x -> x <>: Mpqf.zero) v with + (** Get the constant from the vector if it is a constant *) + let get_c v = match Vector.findi ((<>:) Mpqf.zero) v with | exception Not_found -> Some Mpqf.zero | i when Vector.compare_length_with v (i + 1) = 0 -> Some (Vector.nth v i) | _ -> None @@ -202,8 +210,8 @@ struct match get_coeff_vec t texpr with | Some v -> begin match get_c v with | Some c when Mpqf.get_den c = IntOps.BigIntOps.one -> - let int_val = Mpqf.get_num c - in Some int_val, Some int_val + let int_val = Mpqf.get_num c in + Some int_val, Some int_val | _ -> None, None end | _ -> None, None From 059db8d83696c5883a09dae12026a1f4595bbbbb Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 28 Dec 2023 12:24:59 +0100 Subject: [PATCH 1285/1312] Inline Binop --- .../apron/affineEqualityDomain.apron.ml | 116 +++++++++--------- 1 file changed, 61 insertions(+), 55 deletions(-) diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index ecd4bdc1d5..fff6437882 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -59,8 +59,8 @@ struct let dim_add ch m = timing_wrap "dim add" (dim_add ch) m let dim_remove (ch: Apron.Dim.change) m del = - if Array.length ch.dim = 0 || Matrix.is_empty m then - m + if Array.length ch.dim = 0 || Matrix.is_empty m then + m else ( Array.modifyi (fun i x -> x + i) ch.dim; let m' = if not del then let m = Matrix.copy m in Array.fold_left (fun y x -> Matrix.reduce_col_with y x; y) m ch.dim else m in @@ -69,16 +69,16 @@ struct let dim_remove ch m del = timing_wrap "dim remove" (dim_remove ch m) del let change_d t new_env add del = - if Environment.equal t.env new_env then + if Environment.equal t.env new_env then t else match t.d with | None -> bot_env - | Some m -> - let dim_change = - if add then + | Some m -> + let dim_change = + if add then Environment.dimchange t.env new_env - else + else Environment.dimchange new_env t.env in {d = Some (if add then dim_add dim_change m else dim_remove dim_change m del); env = new_env} @@ -158,19 +158,19 @@ struct in let rec convert_texpr = function (*If x is a constant, replace it with its const. val. immediately*) - | Cst x -> + | Cst x -> let of_union = function | Coeff.Interval _ -> failwith "Not a constant" | Scalar Float x -> Mpqf.of_float x | Scalar Mpqf x -> x | Scalar Mpfrf x -> Mpfr.to_mpq x - in + in Vector.set_val zero_vec ((Vector.length zero_vec) - 1) (of_union x) | Var x -> let zero_vec_cp = Vector.copy zero_vec in let entry_only v = Vector.set_val_with v (Environment.dim_of_var t.env x) Mpqf.one; v in begin match t.d with - | Some m -> + | Some m -> let row = Matrix.find_opt (fun r -> Vector.nth r (Environment.dim_of_var t.env x) =: Mpqf.one) m in begin match row with | Some v when is_const_vec v -> @@ -181,18 +181,24 @@ struct | Unop (Neg, e, _, _) -> neg @@ convert_texpr e | Unop (Cast, e, _, _) -> convert_texpr e (*Ignore since casts in apron are used for floating point nums and rounding in contrast to CIL casts*) | Unop (Sqrt, e, _, _) -> raise NotLinear - | Binop (b, e1, e2, _, _) -> - begin match b with - | Add -> let v1 = convert_texpr e1 in Vector.map2_with (+:) v1 (convert_texpr e2); v1 - | Sub -> let v1 = convert_texpr e1 in Vector.map2_with (+:) v1 (neg @@ convert_texpr e2); v1 - | Mul -> - let x1, x2 = convert_texpr e1, convert_texpr e2 in - begin match get_c x1, get_c x2 with - | _, Some c -> Vector.apply_with_c_with ( *:) c x1; x1 - | Some c, _ -> Vector.apply_with_c_with ( *:) c x2; x2 - | _, _ -> raise NotLinear end - | _ -> raise NotLinear end - in + | Binop (Add, e1, e2, _, _) -> + let v1 = convert_texpr e1 in + let v2 = convert_texpr e2 in + Vector.map2_with (+:) v1 v2; v1 + | Binop (Sub, e1, e2, _, _) -> + let v1 = convert_texpr e1 in + let v2 = convert_texpr e2 in + Vector.map2_with (+:) v1 (neg @@ v2); v1 + | Binop (Mul, e1, e2, _, _) -> + let v1 = convert_texpr e1 in + let v2 = convert_texpr e2 in + begin match get_c v1, get_c v2 with + | _, Some c -> Vector.apply_with_c_with ( *:) c v1; v1 + | Some c, _ -> Vector.apply_with_c_with ( *:) c v2; v2 + | _, _ -> raise NotLinear + end + | Binop _ -> raise NotLinear + in try Some (convert_texpr texp) with NotLinear -> None @@ -210,7 +216,7 @@ struct match get_coeff_vec t texpr with | Some v -> begin match get_c v with | Some c when Mpqf.get_den c = IntOps.BigIntOps.one -> - let int_val = Mpqf.get_num c in + let int_val = Mpqf.get_num c in Some int_val, Some int_val | _ -> None, None end | _ -> None, None @@ -249,15 +255,15 @@ struct int_arr in let vec_to_constraint arr env = - let vars, _ = Environment.vars env in + let vars, _ = Environment.vars env in let dim_to_str var = - let vl = arr.(Environment.dim_of_var env var) in - let var_str = Var.to_string var in - if Z.equal vl Z.zero then + let vl = arr.(Environment.dim_of_var env var) in + let var_str = Var.to_string var in + if Z.equal vl Z.zero then "" - else if Z.equal vl Z.one then + else if Z.equal vl Z.one then "+" ^ var_str - else if Z.equal vl Z.minus_one then + else if Z.equal vl Z.minus_one then "-" ^ var_str else if Z.lt vl Z.minus_one then Z.to_string vl ^ var_str @@ -265,7 +271,7 @@ struct Format.asprintf "+%s" (Z.to_string vl) ^ var_str in let const_to_str vl = - if Z.equal vl Z.zero then + if Z.equal vl Z.zero then "" else let negated = Z.mul vl Z.minus_one in @@ -273,8 +279,8 @@ struct else Z.to_string negated in let res = (String.concat "" @@ Array.to_list @@ Array.map dim_to_str vars) - ^ (const_to_str arr.(Array.length arr - 1)) ^ "=0" in - if String.starts_with res "+" then + ^ (const_to_str arr.(Array.length arr - 1)) ^ "=0" in + if String.starts_with res "+" then String.sub res 1 (String.length res - 1) else res @@ -309,18 +315,18 @@ struct let meet t1 t2 = let sup_env = Environment.lce t1.env t2.env in let t1, t2 = change_d t1 sup_env true false, change_d t2 sup_env true false in - if is_bot t1 || is_bot t2 then + if is_bot t1 || is_bot t2 then bot () else (* TODO: Why can I be sure that m1 && m2 are all Some here? *) let m1, m2 = Option.get t1.d, Option.get t2.d in - if is_top_env t1 then - {d = Some (dim_add (Environment.dimchange t2.env sup_env) m2); env = sup_env} - else if is_top_env t2 then + if is_top_env t1 then + {d = Some (dim_add (Environment.dimchange t2.env sup_env) m2); env = sup_env} + else if is_top_env t2 then {d = Some (dim_add (Environment.dimchange t1.env sup_env) m1); env = sup_env} else let rref_matr = Matrix.rref_matrix_with (Matrix.copy m1) (Matrix.copy m2) in - if Option.is_none rref_matr then + if Option.is_none rref_matr then bot () else {d = rref_matr; env = sup_env} @@ -339,12 +345,12 @@ struct (* -2: environments are not compatible (a variable has different types in the 2 environements *) (* -1: if env1 is a subset of env2, (OK) *) (* 0: if equality, (OK) *) - (* +1: if env1 is a superset of env2, and +2 otherwise (the lce exists and is a strict superset of both) *) + (* +1: if env1 is a superset of env2, and +2 otherwise (the lce exists and is a strict superset of both) *) false - else if is_bot t1 || is_top_env t2 then + else if is_bot t1 || is_top_env t2 then true - else if is_bot t2 || is_top_env t1 then - false + else if is_bot t2 || is_top_env t1 then + false else let m1, m2 = Option.get t1.d, Option.get t2.d in let m1' = if env_comp = 0 then m1 else dim_add (Environment.dimchange t1.env t2.env) m1 in @@ -402,9 +408,9 @@ struct lin_disjunc new_r (s + 1) new_a new_b | _ -> failwith "Matrix not in rref form" end in - if is_bot a then + if is_bot a then b - else if is_bot b then + else if is_bot b then a else match Option.get a.d, Option.get b.d with @@ -437,15 +443,15 @@ struct let remove_rels_with_var x var env inplace = let j0 = Environment.dim_of_var env var in - if inplace then - (Matrix.reduce_col_with x j0; x) - else + if inplace then + (Matrix.reduce_col_with x j0; x) + else Matrix.reduce_col x j0 let remove_rels_with_var x var env inplace = timing_wrap "remove_rels_with_var" (remove_rels_with_var x var env) inplace let forget_vars t vars = - if is_bot t || is_top_env t || List.is_empty vars then + if is_bot t || is_top_env t || List.is_empty vars then t else let m = Option.get t.d in @@ -526,8 +532,8 @@ struct let t_primed = add_vars t primed_vars in let multi_t = List.fold_left2 (fun t' v_prime (_,v') -> assign_var t' v_prime v') t_primed primed_vars vv's in match multi_t.d with - | Some m when not @@ is_top_env multi_t -> - let replace_col m x y = + | Some m when not @@ is_top_env multi_t -> + let replace_col m x y = let dim_x, dim_y = Environment.dim_of_var multi_t.env x, Environment.dim_of_var multi_t.env y in let col_x = Matrix.get_col m dim_x in Matrix.set_col_with m col_x dim_y @@ -536,9 +542,9 @@ struct let switched_m = List.fold_left2 replace_col m_cp primed_vars assigned_vars in let res = drop_vars {d = Some switched_m; env = multi_t.env} primed_vars true in let x = Option.get res.d in - if Matrix.normalize_with x then - {d = Some x; env = res.env} - else + if Matrix.normalize_with x then + {d = Some x; env = res.env} + else bot () | _ -> t @@ -609,10 +615,10 @@ struct (*Flip the sign of the const. val in coeff vec*) Vector.mapi_with (fun i x -> if Vector.compare_length_with e (i + 1) = 0 then Mpqf.mone *: x else x) e; let res = - if is_bot t then - bot () + if is_bot t then + bot () else - let opt_m = Matrix.rref_vec_with (Matrix.copy @@ Option.get t.d) e in + let opt_m = Matrix.rref_vec_with (Matrix.copy @@ Option.get t.d) e in if Option.is_none opt_m then bot () else {d = opt_m; env = t.env} in meet_tcons_one_var_eq res expr From 54860e74b4fc96b4d6301621df7c3ac139efe20b Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 28 Dec 2023 12:27:54 +0100 Subject: [PATCH 1286/1312] Rename `get_c` to more obvious name --- src/cdomains/apron/affineEqualityDomain.apron.ml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index fff6437882..82fe8fe6b6 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -141,7 +141,7 @@ struct include ConvenienceOps(Mpqf) (** Get the constant from the vector if it is a constant *) - let get_c v = match Vector.findi ((<>:) Mpqf.zero) v with + let to_constant_opt v = match Vector.findi ((<>:) Mpqf.zero) v with | exception Not_found -> Some Mpqf.zero | i when Vector.compare_length_with v (i + 1) = 0 -> Some (Vector.nth v i) | _ -> None @@ -192,7 +192,7 @@ struct | Binop (Mul, e1, e2, _, _) -> let v1 = convert_texpr e1 in let v2 = convert_texpr e2 in - begin match get_c v1, get_c v2 with + begin match to_constant_opt v1, to_constant_opt v2 with | _, Some c -> Vector.apply_with_c_with ( *:) c v1; v1 | Some c, _ -> Vector.apply_with_c_with ( *:) c v2; v2 | _, _ -> raise NotLinear @@ -214,7 +214,7 @@ struct let bound_texpr t texpr = let texpr = Texpr1.to_expr texpr in match get_coeff_vec t texpr with - | Some v -> begin match get_c v with + | Some v -> begin match to_constant_opt v with | Some c when Mpqf.get_den c = IntOps.BigIntOps.one -> let int_val = Mpqf.get_num c in Some int_val, Some int_val @@ -625,7 +625,7 @@ struct in match get_coeff_vec t (Texpr1.to_expr @@ Tcons1.get_texpr1 tcons) with | Some v -> - begin match get_c v, Tcons1.get_typ tcons with + begin match to_constant_opt v, Tcons1.get_typ tcons with | Some c, DISEQ -> check_const (=:) c | Some c, SUP -> check_const (<=:) c | Some c, EQ -> check_const (<>:) c From f0f47987a6eb8f3d7543900610c00ab36375635c Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 28 Dec 2023 12:36:00 +0100 Subject: [PATCH 1287/1312] Simplify `bound_texpr` --- .../apron/affineEqualityDomain.apron.ml | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index 82fe8fe6b6..9ccf2294a3 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -213,20 +213,22 @@ struct let bound_texpr t texpr = let texpr = Texpr1.to_expr texpr in - match get_coeff_vec t texpr with - | Some v -> begin match to_constant_opt v with - | Some c when Mpqf.get_den c = IntOps.BigIntOps.one -> - let int_val = Mpqf.get_num c in - Some int_val, Some int_val - | _ -> None, None end + match Option.bind (get_coeff_vec t texpr) to_constant_opt with + | Some c when Mpqf.get_den c = IntOps.BigIntOps.one -> + let int_val = Mpqf.get_num c in + Some int_val, Some int_val | _ -> None, None let bound_texpr d texpr1 = let res = bound_texpr d texpr1 in - match res with - | Some min, Some max -> if M.tracing then M.tracel "bounds" "min: %s max: %s" (IntOps.BigIntOps.to_string min) (IntOps.BigIntOps.to_string max); res - | _ -> res + (if M.tracing then + match res with + | Some min, Some max -> M.tracel "bounds" "min: %s max: %s" (IntOps.BigIntOps.to_string min) (IntOps.BigIntOps.to_string max) + | _ -> () + ); + res + let bound_texpr d texpr1 = timing_wrap "bounds calculation" (bound_texpr d) texpr1 end From 44596eb2c63cb4ddfd2f3e9c86110978a2d5c361 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 28 Dec 2023 12:41:14 +0100 Subject: [PATCH 1288/1312] Shorten function --- src/cdomains/apron/affineEqualityDomain.apron.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index 9ccf2294a3..d2c74a82a2 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -528,7 +528,7 @@ struct res let assign_var_parallel t vv's = - let assigned_vars = List.map (function (v, _) -> v) vv's in + let assigned_vars = List.map fst vv's in let t = add_vars t assigned_vars in let primed_vars = List.init (List.length assigned_vars) (fun i -> Var.of_string (Int.to_string i ^"'")) in (* TODO: we use primed vars in analysis, conflict? *) let t_primed = add_vars t primed_vars in From b6b202ed759eb45d5ef02712a60d9685ab864509 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 28 Dec 2023 12:53:42 +0100 Subject: [PATCH 1289/1312] Some more making things concise --- src/cdomains/apron/affineEqualityDomain.apron.ml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index d2c74a82a2..0bb0e856c3 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -152,7 +152,7 @@ struct let open Apron.Texpr1 in let exception NotLinear in let zero_vec = Vector.zero_vec @@ Environment.size t.env + 1 in - let neg v = Vector.map_with (fun x -> Mpqf.mone *: x) v; v in + let neg v = Vector.map_with (( *:) Mpqf.mone) v; v in let is_const_vec v = Vector.compare_length_with (Vector.filteri (fun i x -> (*Inefficient*) Vector.compare_length_with v (i + 1) > 0 && x <>: Mpqf.zero) v) 1 = 0 in @@ -250,8 +250,8 @@ struct let row = Array.copy @@ Vector.to_array row in let mpqf_of_z x = Mpqf.of_mpz @@ Z_mlgmpidl.mpzf_of_z x in let lcm = mpqf_of_z @@ Array.fold_left (fun x y -> Z.lcm x (Mpqf.get_den y)) Z.one row in - Array.modify (fun x -> x *: lcm) row; - let int_arr = Array.map (fun x -> Mpqf.get_num x) row in + Array.modify (( *:) lcm) row; + let int_arr = Array.map Mpqf.get_num row in let div = Array.fold_left Z.gcd int_arr.(0) int_arr in Array.modify (fun x -> Z.div x div) int_arr; int_arr @@ -379,12 +379,12 @@ struct let col_a, col_b = Vector.keep_vals col_a max, Vector.keep_vals col_b max in if Vector.equal col_a col_b then (a, b, max) else let a_rev, b_rev = (Vector.rev_with col_a; col_a), (Vector.rev_with col_b; col_b) in - let i = Vector.find2i (fun x y -> x <>: y) a_rev b_rev in + let i = Vector.find2i (<>:) a_rev b_rev in let (x, y) = Vector.nth a_rev i, Vector.nth b_rev i in let r, diff = Vector.length a_rev - (i + 1), x -: y in let a_r, b_r = Matrix.get_row a r, Matrix.get_row b r in let sub_col = - Vector.map2_with (fun x y -> x -: y) a_rev b_rev; + Vector.map2_with (-:) a_rev b_rev; Vector.rev_with a_rev; a_rev in @@ -581,8 +581,8 @@ struct forget_vars res [var] let substitute_exp t var exp ov = - let res = substitute_exp t var exp ov - in if M.tracing then M.tracel "ops" "Substitute_expr t: \n %s \n var: %s \n exp: %a \n -> \n %s\n" (show t) (Var.to_string var) d_exp exp (show res); + let res = substitute_exp t var exp ov in + if M.tracing then M.tracel "ops" "Substitute_expr t: \n %s \n var: %s \n exp: %a \n -> \n %s\n" (show t) (Var.to_string var) d_exp exp (show res); res let substitute_exp t var exp ov = timing_wrap "substitution" (substitute_exp t var exp) ov From f0870881960a97a7d3afdd0f1b2f253f1c416794 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 28 Dec 2023 13:00:30 +0100 Subject: [PATCH 1290/1312] Rename var to `coeff` --- src/cdomains/apron/affineEqualityDomain.apron.ml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index 0bb0e856c3..5630888b59 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -259,18 +259,18 @@ struct let vec_to_constraint arr env = let vars, _ = Environment.vars env in let dim_to_str var = - let vl = arr.(Environment.dim_of_var env var) in + let coeff = arr.(Environment.dim_of_var env var) in let var_str = Var.to_string var in - if Z.equal vl Z.zero then + if Z.equal coeff Z.zero then "" - else if Z.equal vl Z.one then + else if Z.equal coeff Z.one then "+" ^ var_str - else if Z.equal vl Z.minus_one then + else if Z.equal coeff Z.minus_one then "-" ^ var_str - else if Z.lt vl Z.minus_one then - Z.to_string vl ^ var_str + else if Z.lt coeff Z.minus_one then + Z.to_string coeff ^ var_str else - Format.asprintf "+%s" (Z.to_string vl) ^ var_str + Format.asprintf "+%s" (Z.to_string coeff) ^ var_str in let const_to_str vl = if Z.equal vl Z.zero then From 0dcf8ce52ca8bd4992378ba2072a156ef68a415f Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 28 Dec 2023 13:04:50 +0100 Subject: [PATCH 1291/1312] Make show more concise --- .../apron/affineEqualityDomain.apron.ml | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index 5630888b59..7d01130480 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -260,23 +260,22 @@ struct let vars, _ = Environment.vars env in let dim_to_str var = let coeff = arr.(Environment.dim_of_var env var) in - let var_str = Var.to_string var in if Z.equal coeff Z.zero then "" - else if Z.equal coeff Z.one then - "+" ^ var_str - else if Z.equal coeff Z.minus_one then - "-" ^ var_str - else if Z.lt coeff Z.minus_one then - Z.to_string coeff ^ var_str else - Format.asprintf "+%s" (Z.to_string coeff) ^ var_str + let coeff_str = + if Z.equal coeff Z.one then "+" + else if Z.equal coeff Z.minus_one then "-" + else if Z.lt coeff Z.minus_one then Z.to_string coeff + else Format.asprintf "+%s" (Z.to_string coeff) + in + coeff_str ^ Var.to_string var in let const_to_str vl = if Z.equal vl Z.zero then "" else - let negated = Z.mul vl Z.minus_one in + let negated = Z.neg vl in if Z.gt negated Z.zero then "+" ^ Z.to_string negated else Z.to_string negated in From 399fb8c4c8fc31d91869e4851056ce0be9010b8f Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 28 Dec 2023 13:11:27 +0100 Subject: [PATCH 1292/1312] Remove code obscuring imperative nature --- .../apron/affineEqualityDomain.apron.ml | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index 7d01130480..2ec5dabf61 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -376,23 +376,25 @@ struct let case_three a b col_a col_b max = let col_a, col_b = Vector.copy col_a, Vector.copy col_b in let col_a, col_b = Vector.keep_vals col_a max, Vector.keep_vals col_b max in - if Vector.equal col_a col_b then (a, b, max) else - let a_rev, b_rev = (Vector.rev_with col_a; col_a), (Vector.rev_with col_b; col_b) in - let i = Vector.find2i (<>:) a_rev b_rev in - let (x, y) = Vector.nth a_rev i, Vector.nth b_rev i in - let r, diff = Vector.length a_rev - (i + 1), x -: y in - let a_r, b_r = Matrix.get_row a r, Matrix.get_row b r in - let sub_col = - Vector.map2_with (-:) a_rev b_rev; - Vector.rev_with a_rev; - a_rev - in - let multiply_by_t m t = - Matrix.map2i_with (fun i' x c -> if i' <= max then (let beta = c /: diff in - Vector.map2_with (fun u j -> u -: (beta *: j)) x t); x) m sub_col; - m - in - Matrix.remove_row (multiply_by_t a a_r) r, Matrix.remove_row (multiply_by_t b b_r) r, (max - 1) + if Vector.equal col_a col_b then + (a, b, max) + else + ( + Vector.rev_with col_a; + Vector.rev_with col_b; + let i = Vector.find2i (<>:) col_a col_b in + let (x, y) = Vector.nth col_a i, Vector.nth col_b i in + let r, diff = Vector.length col_a - (i + 1), x -: y in + let a_r, b_r = Matrix.get_row a r, Matrix.get_row b r in + Vector.map2_with (-:) col_a col_b; + Vector.rev_with col_a; + let multiply_by_t m t = + Matrix.map2i_with (fun i' x c -> if i' <= max then (let beta = c /: diff in + Vector.map2_with (fun u j -> u -: (beta *: j)) x t); x) m col_a; + m + in + Matrix.remove_row (multiply_by_t a a_r) r, Matrix.remove_row (multiply_by_t b b_r) r, (max - 1) + ) in let col_a, col_b = Matrix.get_col a s, Matrix.get_col b s in let nth_zero v i = match Vector.nth v i with From 63094e0e1fcee316a2cd8b299dd2699e3e27a193 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 28 Dec 2023 13:22:08 +0100 Subject: [PATCH 1293/1312] Make use of `uncurry` --- src/cdomains/vectorMatrix.ml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/cdomains/vectorMatrix.ml b/src/cdomains/vectorMatrix.ml index d652145032..1dd684a4c0 100644 --- a/src/cdomains/vectorMatrix.ml +++ b/src/cdomains/vectorMatrix.ml @@ -251,12 +251,14 @@ module ArrayVector: AbstractVector = let nth = Array.get - let map2i f v1 v2 = let f' i (v'1, v'2) = f i v'1 v'2 in Array.mapi f' (Array.combine v1 v2) (* TODO: iter2i? *) + let map2i f v1 v2 = + let f' i = uncurry (f i) in + Array.mapi f' (Array.combine v1 v2) (* TODO: iter2i? *) let map2i_with f v1 v2 = Array.iter2i (fun i x y -> v1.(i) <- f i x y) v1 v2 - let find2i f v1 v2 = let f' (v'1, v'2) = f v'1 v'2 in - Array.findi f' (Array.combine v1 v2) (* TODO: iter2i? *) + let find2i f v1 v2 = + Array.findi (uncurry f) (Array.combine v1 v2) (* TODO: iter2i? *) let to_array v = v From 79b79d841e17fdbd273a458a0c718c78fa11a1c3 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 28 Dec 2023 13:26:30 +0100 Subject: [PATCH 1294/1312] Simplify `meet_tcons_one_var_eq` --- .../apron/affineEqualityDomain.apron.ml | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index 2ec5dabf61..e05400e674 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -601,16 +601,21 @@ struct | None -> overflow_res res | Some v -> let ik = Cilfacade.get_ikind v.vtype in - match Bounds.bound_texpr res (Convert.texpr1_of_cil_exp res res.env (Lval (Cil.var v)) true) with - | Some _, Some _ when not (Cil.isSigned ik) -> raise NotRefinable (* TODO: unsigned w/o bounds handled differently? *) - | Some min, Some max -> - assert (Z.equal min max); (* other bounds impossible in affeq *) - let (min_ik, max_ik) = IntDomain.Size.range ik in - if Z.compare min min_ik < 0 || Z.compare max max_ik > 0 then - if IntDomain.should_ignore_overflow ik then bot () else raise NotRefinable - else res - | exception Convert.Unsupported_CilExp _ - | _, _ -> overflow_res res + if not (Cil.isSigned ik) then + raise NotRefinable (* TODO: unsigned w/o bounds handled differently? *) + else + match Bounds.bound_texpr res (Convert.texpr1_of_cil_exp res res.env (Lval (Cil.var v)) true) with + | Some min, Some max -> + assert (Z.equal min max); (* other bounds impossible in affeq *) + let (min_ik, max_ik) = IntDomain.Size.range ik in + if Z.compare min min_ik < 0 || Z.compare max max_ik > 0 then + if IntDomain.should_ignore_overflow ik then + bot () + else + raise NotRefinable + else res + | exception Convert.Unsupported_CilExp _ + | _ -> overflow_res res let meet_tcons t tcons expr = let check_const cmp c = if cmp c Mpqf.zero then bot_env else t in From 097156f2bac7560bd92c5d4aba6babbfb6c6c152 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 28 Dec 2023 13:27:51 +0100 Subject: [PATCH 1295/1312] Replace Z.compare with bespoke functions --- src/cdomains/apron/affineEqualityDomain.apron.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index e05400e674..87accda1b4 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -608,7 +608,7 @@ struct | Some min, Some max -> assert (Z.equal min max); (* other bounds impossible in affeq *) let (min_ik, max_ik) = IntDomain.Size.range ik in - if Z.compare min min_ik < 0 || Z.compare max max_ik > 0 then + if Z.lt min min_ik || Z.gt max max_ik then if IntDomain.should_ignore_overflow ik then bot () else From 0c0f3943c1d324e9e509e3edd4493592e17be8de Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 28 Dec 2023 13:40:00 +0100 Subject: [PATCH 1296/1312] Some reordering & make `get_coeff_vec` more efficient --- .../apron/affineEqualityDomain.apron.ml | 54 ++++++++++--------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index 87accda1b4..6f6f7c1280 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -152,7 +152,7 @@ struct let open Apron.Texpr1 in let exception NotLinear in let zero_vec = Vector.zero_vec @@ Environment.size t.env + 1 in - let neg v = Vector.map_with (( *:) Mpqf.mone) v; v in + let neg v = Vector.map_with Mpqf.neg v; v in let is_const_vec v = Vector.compare_length_with (Vector.filteri (fun i x -> (*Inefficient*) Vector.compare_length_with v (i + 1) > 0 && x <>: Mpqf.zero) v) 1 = 0 in @@ -485,7 +485,7 @@ struct let assign_invertible_rels x var b env = timing_wrap "assign_invertible" (assign_invertible_rels x var b) env in let assign_uninvertible_rel x var b env = let b_length = Vector.length b in - Vector.mapi_with (fun i z -> if i < b_length - 1 then Mpqf.mone *: z else z) b; + Vector.mapi_with (fun i z -> if i < b_length - 1 then Mpqf.neg z else z) b; Vector.set_val_with b (Environment.dim_of_var env var) Mpqf.one; let opt_m = Matrix.rref_vec_with x b in if Option.is_none opt_m then bot () else @@ -620,8 +620,9 @@ struct let meet_tcons t tcons expr = let check_const cmp c = if cmp c Mpqf.zero then bot_env else t in let meet_vec e = - (*Flip the sign of the const. val in coeff vec*) - Vector.mapi_with (fun i x -> if Vector.compare_length_with e (i + 1) = 0 then Mpqf.mone *: x else x) e; + (* Flip the sign of the const. val in coeff vec *) + let coeff = Vector.nth e (Vector.length e - 1) in + Vector.set_val_with e (Vector.length e - 1) (Mpqf.neg coeff); let res = if is_bot t then bot () @@ -631,27 +632,30 @@ struct in meet_tcons_one_var_eq res expr in - match get_coeff_vec t (Texpr1.to_expr @@ Tcons1.get_texpr1 tcons) with - | Some v -> - begin match to_constant_opt v, Tcons1.get_typ tcons with - | Some c, DISEQ -> check_const (=:) c - | Some c, SUP -> check_const (<=:) c - | Some c, EQ -> check_const (<>:) c - | Some c, SUPEQ -> check_const (<:) c - | None, DISEQ - | None, SUP -> - begin match meet_vec v with - | exception NotRefinable -> t - | res -> if equal res t then bot_env else t - end - | None, EQ -> - begin match meet_vec v with - | exception NotRefinable -> t - | res -> if is_bot res then bot_env else res - end - | _, _ -> t - end - | None -> t + try + match get_coeff_vec t (Texpr1.to_expr @@ Tcons1.get_texpr1 tcons) with + | Some v -> + begin match to_constant_opt v, Tcons1.get_typ tcons with + | Some c, DISEQ -> check_const (=:) c + | Some c, SUP -> check_const (<=:) c + | Some c, EQ -> check_const (<>:) c + | Some c, SUPEQ -> check_const (<:) c + | None, DISEQ + | None, SUP -> + if equal (meet_vec v) t then + bot_env + else + t + | None, EQ -> + let res = meet_vec v in + if is_bot res then + bot_env + else + res + | _ -> t + end + | None -> t + with NotRefinable -> t let meet_tcons t tcons expr = timing_wrap "meet_tcons" (meet_tcons t tcons) expr From 8ac5fadb08ec258021c8a98b00faca549c69a95b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 28 Dec 2023 14:41:48 +0200 Subject: [PATCH 1297/1312] Extract analysis results from Analyses module --- src/framework/analyses.ml | 186 ------------------------------- src/framework/analysisResult.ml | 191 ++++++++++++++++++++++++++++++++ src/framework/control.ml | 4 +- src/goblint_lib.ml | 1 + 4 files changed, 194 insertions(+), 188 deletions(-) create mode 100644 src/framework/analysisResult.ml diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 405df5b6a6..633eea1b39 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -162,183 +162,6 @@ struct end -module ResultNode: Printable.S with type t = MyCFG.node = -struct - include Printable.Std - - include Node - - let name () = "resultnode" - - let show a = - (* Not using Node.location here to have updated locations in incremental analysis. - See: https://github.com/goblint/analyzer/issues/290#issuecomment-881258091. *) - let x = UpdateCil.getLoc a in - let f = Node.find_fundec a in - CilType.Location.show x ^ "(" ^ f.svar.vname ^ ")" - - include Printable.SimpleShow ( - struct - type nonrec t = t - let show = show - end - ) -end - -module type ResultConf = -sig - val result_name: string -end - -module Result (Range: Printable.S) (C: ResultConf) = -struct - include Hashtbl.Make (ResultNode) - type nonrec t = Range.t t (* specialize polymorphic type for Range values *) - - let pretty () mapping = - let f key st dok = - dok ++ dprintf "%a ->@? @[%a@]\n" ResultNode.pretty key Range.pretty st - in - let content () = fold f mapping nil in - let defline () = dprintf "OTHERS -> Not available\n" in - dprintf "@[Mapping {\n @[%t%t@]}@]" content defline - - include C - - let printXml f xs = - let print_one n v = - (* Not using Node.location here to have updated locations in incremental analysis. - See: https://github.com/goblint/analyzer/issues/290#issuecomment-881258091. *) - let loc = UpdateCil.getLoc n in - BatPrintf.fprintf f "\n" (Node.show_id n) loc.file loc.line loc.byte loc.column; - BatPrintf.fprintf f "%a\n" Range.printXml v - in - iter print_one xs - - let printJson f xs = - let print_one n v = - (* Not using Node.location here to have updated locations in incremental analysis. - See: https://github.com/goblint/analyzer/issues/290#issuecomment-881258091. *) - let loc = UpdateCil.getLoc n in - BatPrintf.fprintf f "{\n\"id\": \"%s\", \"file\": \"%s\", \"line\": \"%d\", \"byte\": \"%d\", \"column\": \"%d\", \"states\": %s\n},\n" (Node.show_id n) loc.file loc.line loc.byte loc.column (Yojson.Safe.to_string (Range.to_yojson v)) - in - iter print_one xs - - let printXmlWarning f () = - let one_text f Messages.Piece.{loc; text = m; _} = - match loc with - | Some loc -> - let l = Messages.Location.to_cil loc in - BatPrintf.fprintf f "\n%s" l.file l.line l.column (XmlUtil.escape m) - | None -> - () (* TODO: not outputting warning without location *) - in - let one_w f (m: Messages.Message.t) = match m.multipiece with - | Single piece -> one_text f piece - | Group {group_text = n; pieces = e; group_loc} -> - let group_loc_text = match group_loc with - | None -> "" - | Some group_loc -> GobPretty.sprintf " (%a)" CilType.Location.pretty (Messages.Location.to_cil group_loc) - in - BatPrintf.fprintf f "%a\n" n group_loc_text (BatList.print ~first:"" ~last:"" ~sep:"" one_text) e - in - let one_w f x = BatPrintf.fprintf f "\n%a" one_w x in - List.iter (one_w f) !Messages.Table.messages_list - - let output table gtable gtfxml (file: file) = - let out = Messages.get_out result_name !Messages.out in - match get_string "result" with - | "pretty" -> ignore (fprintf out "%a\n" pretty (Lazy.force table)) - | "fast_xml" -> - let module SH = BatHashtbl.Make (Basetype.RawStrings) in - let file2funs = SH.create 100 in - let funs2node = SH.create 100 in - iter (fun n _ -> SH.add funs2node (Node.find_fundec n).svar.vname n) (Lazy.force table); - iterGlobals file (function - | GFun (fd,loc) -> SH.add file2funs loc.file fd.svar.vname - | _ -> () - ); - let p_node f n = BatPrintf.fprintf f "%s" (Node.show_id n) in - let p_nodes f xs = - List.iter (BatPrintf.fprintf f "\n" p_node) xs - in - let p_funs f xs = - let one_fun n = - BatPrintf.fprintf f "\n%a\n" n p_nodes (SH.find_all funs2node n) - in - List.iter one_fun xs - in - let write_file f fn = - Messages.xml_file_name := fn; - BatPrintf.printf "Writing xml to temp. file: %s\n%!" fn; - BatPrintf.fprintf f ""; - BatPrintf.fprintf f "%s" GobSys.command_line; - BatPrintf.fprintf f ""; - let timing_ppf = BatFormat.formatter_of_out_channel f in - Timing.Default.print timing_ppf; - Format.pp_print_flush timing_ppf (); - BatPrintf.fprintf f ""; - BatPrintf.fprintf f "\n"; - BatEnum.iter (fun b -> BatPrintf.fprintf f "\n%a\n" (Filename.basename b) b p_funs (SH.find_all file2funs b)) (BatEnum.uniq @@ SH.keys file2funs); - BatPrintf.fprintf f "%a" printXml (Lazy.force table); - gtfxml f gtable; - printXmlWarning f (); - BatPrintf.fprintf f "\n"; - BatPrintf.fprintf f "%!" - in - if get_bool "g2html" then - BatFile.with_temporary_out ~mode:[`create;`text;`delete_on_exit] write_file - else - let f = BatIO.output_channel out in - write_file f (get_string "outfile") - | "json" -> - let open BatPrintf in - let module SH = BatHashtbl.Make (Basetype.RawStrings) in - let file2funs = SH.create 100 in - let funs2node = SH.create 100 in - iter (fun n _ -> SH.add funs2node (Node.find_fundec n).svar.vname n) (Lazy.force table); - iterGlobals file (function - | GFun (fd,loc) -> SH.add file2funs loc.file fd.svar.vname - | _ -> () - ); - let p_enum p f xs = BatEnum.print ~first:"[\n " ~last:"\n]" ~sep:",\n " p f xs in - let p_list p f xs = BatList.print ~first:"[\n " ~last:"\n]" ~sep:",\n " p f xs in - (*let p_kv f (k,p,v) = fprintf f "\"%s\": %a" k p v in*) - (*let p_obj f xs = BatList.print ~first:"{\n " ~last:"\n}" ~sep:",\n " p_kv xs in*) - let p_node f n = BatPrintf.fprintf f "\"%s\"" (Node.show_id n) in - let p_fun f x = fprintf f "{\n \"name\": \"%s\",\n \"nodes\": %a\n}" x (p_list p_node) (SH.find_all funs2node x) in - (*let p_fun f x = p_obj f [ "name", BatString.print, x; "nodes", p_list p_node, SH.find_all funs2node x ] in*) - let p_file f x = fprintf f "{\n \"name\": \"%s\",\n \"path\": \"%s\",\n \"functions\": %a\n}" (Filename.basename x) x (p_list p_fun) (SH.find_all file2funs x) in - let write_file f fn = - printf "Writing json to temp. file: %s\n%!" fn; - fprintf f "{\n \"parameters\": \"%s\",\n " GobSys.command_line; - fprintf f "\"files\": %a,\n " (p_enum p_file) (SH.keys file2funs); - fprintf f "\"results\": [\n %a\n]\n" printJson (Lazy.force table); - (*gtfxml f gtable;*) - (*printXmlWarning f ();*) - fprintf f "}\n"; - in - if get_bool "g2html" then - BatFile.with_temporary_out ~mode:[`create;`text;`delete_on_exit] write_file - else - let f = BatIO.output_channel out in - write_file f (get_string "outfile") - | "sarif" -> - let open BatPrintf in - printf "Writing Sarif to file: %s\n%!" (get_string "outfile"); - Yojson.Safe.to_channel ~std:true out (Sarif.to_yojson (List.rev !Messages.Table.messages_list)); - | "json-messages" -> - let json = `Assoc [ - ("files", Preprocessor.dependencies_to_yojson ()); - ("messages", Messages.Table.to_yojson ()); - ] - in - Yojson.Safe.to_channel ~std:true out json - | "none" -> () - | s -> failwith @@ "Unsupported value for option `result`: "^s -end - - (* Experiment to reduce the number of arguments on transfer functions and allow sub-analyses. The list sub contains the current local states of analyses in the same order as written in the dependencies list (in MCP). @@ -598,15 +421,6 @@ module type GenericGlobSolver = val solve : (S.LVar.t*S.D.t) list -> (S.GVar.t*S.G.t) list -> S.LVar.t list -> marshal option -> (S.D.t LH.t * S.G.t GH.t) * marshal end -module ResultType2 (S:Spec) = -struct - open S - include Printable.Prod3 (C) (D) (CilType.Fundec) - let show (es,x,f:t) = D.show x - let pretty () (_,x,_) = D.pretty () x - let printXml f (c,d,fd) = - BatPrintf.fprintf f "\n%a\n%a" C.printXml c D.printXml d -end module StdV = struct diff --git a/src/framework/analysisResult.ml b/src/framework/analysisResult.ml new file mode 100644 index 0000000000..09ece868c1 --- /dev/null +++ b/src/framework/analysisResult.ml @@ -0,0 +1,191 @@ +(** Analysis result output. *) + +open GoblintCil +open Pretty +open GobConfig + +module ResultNode: Printable.S with type t = MyCFG.node = +struct + include Printable.Std + + include Node + + let name () = "resultnode" + + let show a = + (* Not using Node.location here to have updated locations in incremental analysis. + See: https://github.com/goblint/analyzer/issues/290#issuecomment-881258091. *) + let x = UpdateCil.getLoc a in + let f = Node.find_fundec a in + CilType.Location.show x ^ "(" ^ f.svar.vname ^ ")" + + include Printable.SimpleShow ( + struct + type nonrec t = t + let show = show + end + ) +end + +module type ResultConf = +sig + val result_name: string +end + +module Result (Range: Printable.S) (C: ResultConf) = +struct + include BatHashtbl.Make (ResultNode) + type nonrec t = Range.t t (* specialize polymorphic type for Range values *) + + let pretty () mapping = + let f key st dok = + dok ++ dprintf "%a ->@? @[%a@]\n" ResultNode.pretty key Range.pretty st + in + let content () = fold f mapping nil in + let defline () = dprintf "OTHERS -> Not available\n" in + dprintf "@[Mapping {\n @[%t%t@]}@]" content defline + + include C + + let printXml f xs = + let print_one n v = + (* Not using Node.location here to have updated locations in incremental analysis. + See: https://github.com/goblint/analyzer/issues/290#issuecomment-881258091. *) + let loc = UpdateCil.getLoc n in + BatPrintf.fprintf f "\n" (Node.show_id n) loc.file loc.line loc.byte loc.column; + BatPrintf.fprintf f "%a\n" Range.printXml v + in + iter print_one xs + + let printJson f xs = + let print_one n v = + (* Not using Node.location here to have updated locations in incremental analysis. + See: https://github.com/goblint/analyzer/issues/290#issuecomment-881258091. *) + let loc = UpdateCil.getLoc n in + BatPrintf.fprintf f "{\n\"id\": \"%s\", \"file\": \"%s\", \"line\": \"%d\", \"byte\": \"%d\", \"column\": \"%d\", \"states\": %s\n},\n" (Node.show_id n) loc.file loc.line loc.byte loc.column (Yojson.Safe.to_string (Range.to_yojson v)) + in + iter print_one xs + + let printXmlWarning f () = + let one_text f Messages.Piece.{loc; text = m; _} = + match loc with + | Some loc -> + let l = Messages.Location.to_cil loc in + BatPrintf.fprintf f "\n%s" l.file l.line l.column (XmlUtil.escape m) + | None -> + () (* TODO: not outputting warning without location *) + in + let one_w f (m: Messages.Message.t) = match m.multipiece with + | Single piece -> one_text f piece + | Group {group_text = n; pieces = e; group_loc} -> + let group_loc_text = match group_loc with + | None -> "" + | Some group_loc -> GobPretty.sprintf " (%a)" CilType.Location.pretty (Messages.Location.to_cil group_loc) + in + BatPrintf.fprintf f "%a\n" n group_loc_text (BatList.print ~first:"" ~last:"" ~sep:"" one_text) e + in + let one_w f x = BatPrintf.fprintf f "\n%a" one_w x in + List.iter (one_w f) !Messages.Table.messages_list + + let output table gtable gtfxml (file: file) = + let out = Messages.get_out result_name !Messages.out in + match get_string "result" with + | "pretty" -> ignore (fprintf out "%a\n" pretty (Lazy.force table)) + | "fast_xml" -> + let module SH = BatHashtbl.Make (Basetype.RawStrings) in + let file2funs = SH.create 100 in + let funs2node = SH.create 100 in + iter (fun n _ -> SH.add funs2node (Node.find_fundec n).svar.vname n) (Lazy.force table); + iterGlobals file (function + | GFun (fd,loc) -> SH.add file2funs loc.file fd.svar.vname + | _ -> () + ); + let p_node f n = BatPrintf.fprintf f "%s" (Node.show_id n) in + let p_nodes f xs = + List.iter (BatPrintf.fprintf f "\n" p_node) xs + in + let p_funs f xs = + let one_fun n = + BatPrintf.fprintf f "\n%a\n" n p_nodes (SH.find_all funs2node n) + in + List.iter one_fun xs + in + let write_file f fn = + Messages.xml_file_name := fn; + BatPrintf.printf "Writing xml to temp. file: %s\n%!" fn; + BatPrintf.fprintf f ""; + BatPrintf.fprintf f "%s" GobSys.command_line; + BatPrintf.fprintf f ""; + let timing_ppf = BatFormat.formatter_of_out_channel f in + Timing.Default.print timing_ppf; + Format.pp_print_flush timing_ppf (); + BatPrintf.fprintf f ""; + BatPrintf.fprintf f "\n"; + BatEnum.iter (fun b -> BatPrintf.fprintf f "\n%a\n" (Filename.basename b) b p_funs (SH.find_all file2funs b)) (BatEnum.uniq @@ SH.keys file2funs); + BatPrintf.fprintf f "%a" printXml (Lazy.force table); + gtfxml f gtable; + printXmlWarning f (); + BatPrintf.fprintf f "\n"; + BatPrintf.fprintf f "%!" + in + if get_bool "g2html" then + BatFile.with_temporary_out ~mode:[`create;`text;`delete_on_exit] write_file + else + let f = BatIO.output_channel out in + write_file f (get_string "outfile") + | "json" -> + let open BatPrintf in + let module SH = BatHashtbl.Make (Basetype.RawStrings) in + let file2funs = SH.create 100 in + let funs2node = SH.create 100 in + iter (fun n _ -> SH.add funs2node (Node.find_fundec n).svar.vname n) (Lazy.force table); + iterGlobals file (function + | GFun (fd,loc) -> SH.add file2funs loc.file fd.svar.vname + | _ -> () + ); + let p_enum p f xs = BatEnum.print ~first:"[\n " ~last:"\n]" ~sep:",\n " p f xs in + let p_list p f xs = BatList.print ~first:"[\n " ~last:"\n]" ~sep:",\n " p f xs in + (*let p_kv f (k,p,v) = fprintf f "\"%s\": %a" k p v in*) + (*let p_obj f xs = BatList.print ~first:"{\n " ~last:"\n}" ~sep:",\n " p_kv xs in*) + let p_node f n = BatPrintf.fprintf f "\"%s\"" (Node.show_id n) in + let p_fun f x = fprintf f "{\n \"name\": \"%s\",\n \"nodes\": %a\n}" x (p_list p_node) (SH.find_all funs2node x) in + (*let p_fun f x = p_obj f [ "name", BatString.print, x; "nodes", p_list p_node, SH.find_all funs2node x ] in*) + let p_file f x = fprintf f "{\n \"name\": \"%s\",\n \"path\": \"%s\",\n \"functions\": %a\n}" (Filename.basename x) x (p_list p_fun) (SH.find_all file2funs x) in + let write_file f fn = + printf "Writing json to temp. file: %s\n%!" fn; + fprintf f "{\n \"parameters\": \"%s\",\n " GobSys.command_line; + fprintf f "\"files\": %a,\n " (p_enum p_file) (SH.keys file2funs); + fprintf f "\"results\": [\n %a\n]\n" printJson (Lazy.force table); + (*gtfxml f gtable;*) + (*printXmlWarning f ();*) + fprintf f "}\n"; + in + if get_bool "g2html" then + BatFile.with_temporary_out ~mode:[`create;`text;`delete_on_exit] write_file + else + let f = BatIO.output_channel out in + write_file f (get_string "outfile") + | "sarif" -> + let open BatPrintf in + printf "Writing Sarif to file: %s\n%!" (get_string "outfile"); + Yojson.Safe.to_channel ~std:true out (Sarif.to_yojson (List.rev !Messages.Table.messages_list)); + | "json-messages" -> + let json = `Assoc [ + ("files", Preprocessor.dependencies_to_yojson ()); + ("messages", Messages.Table.to_yojson ()); + ] + in + Yojson.Safe.to_channel ~std:true out json + | "none" -> () + | s -> failwith @@ "Unsupported value for option `result`: "^s +end + +module ResultType2 (S: Analyses.Spec) = +struct + open S + include Printable.Prod3 (C) (D) (CilType.Fundec) + let show (es,x,f:t) = D.show x + let pretty () (_,x,_) = D.pretty () x + let printXml f (c,d,fd) = + BatPrintf.fprintf f "\n%a\n%a" C.printXml c D.printXml d +end diff --git a/src/framework/control.ml b/src/framework/control.ml index 00a6034e27..54fd1d7774 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -89,11 +89,11 @@ struct module CompareGlobSys = Constraints.CompareGlobSys (SpecSys) (* Triple of the function, context, and the local value. *) - module RT = Analyses.ResultType2 (Spec) + module RT = AnalysisResult.ResultType2 (Spec) (* Set of triples [RT] *) module LT = SetDomain.HeadlessSet (RT) (* Analysis result structure---a hashtable from program points to [LT] *) - module Result = Analyses.Result (LT) (struct let result_name = "analysis" end) + module Result = AnalysisResult.Result (LT) (struct let result_name = "analysis" end) module Query = ResultQuery.Query (SpecSys) diff --git a/src/goblint_lib.ml b/src/goblint_lib.ml index e402cc33fe..2cbe737079 100644 --- a/src/goblint_lib.ml +++ b/src/goblint_lib.ml @@ -45,6 +45,7 @@ module Events = Events The following modules help query the constraint system solution using semantic information. *) +module AnalysisResult = AnalysisResult module ResultQuery = ResultQuery module VarQuery = VarQuery From afdbcf5466015344db929ba982b49d608e2ae68e Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 28 Dec 2023 13:49:06 +0100 Subject: [PATCH 1298/1312] Remove `OldDomainFacade` --- src/cdomains/intDomain.ml | 93 -------------------------------------- src/cdomains/intDomain.mli | 2 - 2 files changed, 95 deletions(-) diff --git a/src/cdomains/intDomain.ml b/src/cdomains/intDomain.ml index 376dab71c2..986634066c 100644 --- a/src/cdomains/intDomain.ml +++ b/src/cdomains/intDomain.ml @@ -282,99 +282,6 @@ end module type Z = Y with type int_t = BI.t -module OldDomainFacade (Old : IkindUnawareS with type int_t = int64) : S with type int_t = BI.t and type t = Old.t = -struct - include Old - type int_t = BI.t - let neg ?no_ov _ik = Old.neg - let add ?no_ov _ik = Old.add - let sub ?no_ov _ik = Old.sub - let mul ?no_ov _ik = Old.mul - let div ?no_ov _ik = Old.div - let rem _ik = Old.rem - - let lt _ik = Old.lt - let gt _ik = Old.gt - let le _ik = Old.le - let ge _ik = Old.ge - let eq _ik = Old.eq - let ne _ik = Old.ne - - let bitnot _ik = bitnot - let bitand _ik = bitand - let bitor _ik = bitor - let bitxor _ik = bitxor - - let shift_left _ik = shift_left - let shift_right _ik = shift_right - - let lognot _ik = lognot - let logand _ik = logand - let logor _ik = logor - - - let to_int a = Option.map BI.of_int64 (Old.to_int a) - - let equal_to (x: int_t) (a: t)= - try - Old.equal_to (BI.to_int64 x) a - with Z.Overflow | Failure _ -> `Top - - let to_excl_list a = Option.map (BatTuple.Tuple2.map1 (List.map BI.of_int64)) (Old.to_excl_list a) - let of_excl_list ik xs = - let xs' = List.map BI.to_int64 xs in - Old.of_excl_list ik xs' - - let to_incl_list a = Option.map (List.map BI.of_int64) (Old.to_incl_list a) - - let maximal a = Option.map BI.of_int64 (Old.maximal a) - let minimal a = Option.map BI.of_int64 (Old.minimal a) - - let of_int ik x = - (* If we cannot convert x to int64, we have to represent it with top in the underlying domain*) - try - Old.of_int (BI.to_int64 x) - with - Failure _ -> top_of ik - - let of_bool ik b = Old.of_bool b - let of_interval ?(suppress_ovwarn=false) ik (l, u) = - try - Old.of_interval ~suppress_ovwarn ik (BI.to_int64 l, BI.to_int64 u) - with - Failure _ -> top_of ik - let of_congruence ik (c, m) = - try - Old.of_congruence ik (BI.to_int64 c, BI.to_int64 m) - with - Failure _ -> top_of ik - - let starting ?(suppress_ovwarn=false) ik x = - try Old.starting ~suppress_ovwarn ik (BI.to_int64 x) with Failure _ -> top_of ik - let ending ?(suppress_ovwarn=false) ik x = - try Old.ending ~suppress_ovwarn ik (BI.to_int64 x) with Failure _ -> top_of ik - - let join _ik = Old.join - let meet _ik = Old.meet - let narrow _ik = Old.narrow - let widen _ik = Old.widen - - let is_top_of _ik = Old.is_top - - let invariant_ikind e ik t = Old.invariant e t - - let cast_to ?torg ?no_ov = Old.cast_to ?torg - - let refine_with_congruence ik a b = a - let refine_with_interval ik a b = a - let refine_with_excl_list ik a b = a - let refine_with_incl_list ik a b = a - - let project ik p t = t - - let arbitrary _ik = Old.arbitrary () -end - module IntDomLifter (I : S) = struct diff --git a/src/cdomains/intDomain.mli b/src/cdomains/intDomain.mli index a853c8acca..4b14aeec72 100644 --- a/src/cdomains/intDomain.mli +++ b/src/cdomains/intDomain.mli @@ -308,8 +308,6 @@ end module SOverflowUnlifter (D : SOverflow) : S with type int_t = D.int_t and type t = D.t -module OldDomainFacade (Old : IkindUnawareS with type int_t = int64) : S with type int_t = IntOps.BigIntOps.t and type t = Old.t -(** Facade for IntDomain implementations that do not implement the interface where arithmetic functions take an ikind parameter. *) module type Y = sig From 0602af056a32170f090cf54c9555b7366a4c2fee Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 28 Dec 2023 14:56:31 +0200 Subject: [PATCH 1299/1312] Extract constraint systems from Analyses module --- src/framework/analyses.ml | 126 +----------------------- src/framework/constrSys.ml | 125 +++++++++++++++++++++++ src/framework/constraints.ml | 1 + src/framework/control.ml | 1 + src/goblint_lib.ml | 1 + src/solvers/effectWConEq.ml | 2 +- src/solvers/generic.ml | 2 +- src/solvers/postSolver.ml | 3 +- src/solvers/sLR.ml | 2 +- src/solvers/sLRphased.ml | 2 +- src/solvers/sLRterm.ml | 2 +- src/solvers/selector.ml | 2 +- src/solvers/td3.ml | 4 +- src/solvers/topDown.ml | 2 +- src/solvers/topDown_deprecated.ml | 2 +- src/solvers/topDown_space_cache_term.ml | 2 +- src/solvers/topDown_term.ml | 2 +- src/solvers/worklist.ml | 2 +- 18 files changed, 146 insertions(+), 137 deletions(-) create mode 100644 src/framework/constrSys.ml diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 633eea1b39..ca6cb9fd51 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -11,24 +11,6 @@ module M = Messages * other functions. *) type fundecs = fundec list * fundec list * fundec list -module type SysVar = -sig - type t - val is_write_only: t -> bool -end - -module type VarType = -sig - include Hashtbl.HashedType - include SysVar with type t := t - val pretty_trace: unit -> t -> doc - val compare : t -> t -> int - - val printXml : 'a BatInnerIO.output -> t -> unit - val var_id : t -> string - val node : t -> MyCFG.node - val relift : t -> t (* needed only for incremental+hashcons to re-hashcons contexts after loading *) -end module Var = struct @@ -69,7 +51,7 @@ end module type SpecSysVar = sig include Printable.S - include SysVar with type t := t + include ConstrSys.SysVar with type t := t end module GVarF (V: SpecSysVar) = @@ -318,110 +300,6 @@ type increment_data = { restarting: VarQuery.t list; } -(** Abstract incremental change to constraint system. - @param 'v constrain system variable type *) -type 'v sys_change_info = { - obsolete: 'v list; (** Variables to destabilize. *) - delete: 'v list; (** Variables to delete. *) - reluctant: 'v list; (** Variables to solve reluctantly. *) - restart: 'v list; (** Variables to restart. *) -} - -(** A side-effecting system. *) -module type MonSystem = -sig - type v (* variables *) - type d (* values *) - type 'a m (* basically a monad carrier *) - - (** Variables must be hashable, comparable, etc. *) - module Var : VarType with type t = v - - (** Values must form a lattice. *) - module Dom : Lattice.S with type t = d - - (** The system in functional form. *) - val system : v -> ((v -> d) -> (v -> d -> unit) -> d) m - - val sys_change: (v -> d) -> v sys_change_info - (** Compute incremental constraint system change from old solution. *) -end - -(** Any system of side-effecting equations over lattices. *) -module type EqConstrSys = MonSystem with type 'a m := 'a option - -(** A side-effecting system with globals. *) -module type GlobConstrSys = -sig - module LVar : VarType - module GVar : VarType - - module D : Lattice.S - module G : Lattice.S - val system : LVar.t -> ((LVar.t -> D.t) -> (LVar.t -> D.t -> unit) -> (GVar.t -> G.t) -> (GVar.t -> G.t -> unit) -> D.t) option - val iter_vars: (LVar.t -> D.t) -> (GVar.t -> G.t) -> VarQuery.t -> LVar.t VarQuery.f -> GVar.t VarQuery.f -> unit - val sys_change: (LVar.t -> D.t) -> (GVar.t -> G.t) -> [`L of LVar.t | `G of GVar.t] sys_change_info -end - -(** A solver is something that can translate a system into a solution (hash-table). - Incremental solver has data to be marshaled. *) -module type GenericEqIncrSolverBase = - functor (S:EqConstrSys) -> - functor (H:Hashtbl.S with type key=S.v) -> - sig - type marshal - - val copy_marshal: marshal -> marshal - val relift_marshal: marshal -> marshal - - (** The hash-map that is the first component of [solve xs vs] is a local solution for interesting variables [vs], - reached from starting values [xs]. - As a second component the solver returns data structures for incremental serialization. *) - val solve : (S.v*S.d) list -> S.v list -> marshal option -> S.d H.t * marshal - end - -(** (Incremental) solver argument, indicating which postsolving should be performed by the solver. *) -module type IncrSolverArg = -sig - val should_prune: bool - val should_verify: bool - val should_warn: bool - val should_save_run: bool -end - -(** An incremental solver takes the argument about postsolving. *) -module type GenericEqIncrSolver = - functor (Arg: IncrSolverArg) -> - GenericEqIncrSolverBase - -(** A solver is something that can translate a system into a solution (hash-table) *) -module type GenericEqSolver = - functor (S:EqConstrSys) -> - functor (H:Hashtbl.S with type key=S.v) -> - sig - (** The hash-map that is the first component of [solve xs vs] is a local solution for interesting variables [vs], - reached from starting values [xs]. *) - val solve : (S.v*S.d) list -> S.v list -> S.d H.t - end - -(** A solver is something that can translate a system into a solution (hash-table) *) -module type GenericGlobSolver = - functor (S:GlobConstrSys) -> - functor (LH:Hashtbl.S with type key=S.LVar.t) -> - functor (GH:Hashtbl.S with type key=S.GVar.t) -> - sig - type marshal - - val copy_marshal: marshal -> marshal - val relift_marshal: marshal -> marshal - - (** The hash-map that is the first component of [solve xs vs] is a local solution for interesting variables [vs], - reached from starting values [xs]. - As a second component the solver returns data structures for incremental serialization. *) - val solve : (S.LVar.t*S.D.t) list -> (S.GVar.t*S.G.t) list -> S.LVar.t list -> marshal option -> (S.D.t LH.t * S.G.t GH.t) * marshal - end - - module StdV = struct let is_write_only _ = false @@ -542,7 +420,7 @@ end module type SpecSys = sig module Spec: Spec - module EQSys: GlobConstrSys with module LVar = VarF (Spec.C) + module EQSys: ConstrSys.GlobConstrSys with module LVar = VarF (Spec.C) and module GVar = GVarF (Spec.V) and module D = Spec.D and module G = GVarG (Spec.G) (Spec.C) diff --git a/src/framework/constrSys.ml b/src/framework/constrSys.ml new file mode 100644 index 0000000000..936e03355c --- /dev/null +++ b/src/framework/constrSys.ml @@ -0,0 +1,125 @@ +(** {{!MonSystem} constraint system} signatures. *) + +open Batteries + +module type SysVar = +sig + type t + val is_write_only: t -> bool +end + +module type VarType = +sig + include Hashtbl.HashedType + include SysVar with type t := t + val pretty_trace: unit -> t -> GoblintCil.Pretty.doc + val compare : t -> t -> int + + val printXml : 'a BatInnerIO.output -> t -> unit + val var_id : t -> string + val node : t -> MyCFG.node + val relift : t -> t (* needed only for incremental+hashcons to re-hashcons contexts after loading *) +end + +(** Abstract incremental change to constraint system. + @param 'v constrain system variable type *) +type 'v sys_change_info = { + obsolete: 'v list; (** Variables to destabilize. *) + delete: 'v list; (** Variables to delete. *) + reluctant: 'v list; (** Variables to solve reluctantly. *) + restart: 'v list; (** Variables to restart. *) +} + +(** A side-effecting system. *) +module type MonSystem = +sig + type v (* variables *) + type d (* values *) + type 'a m (* basically a monad carrier *) + + (** Variables must be hashable, comparable, etc. *) + module Var : VarType with type t = v + + (** Values must form a lattice. *) + module Dom : Lattice.S with type t = d + + (** The system in functional form. *) + val system : v -> ((v -> d) -> (v -> d -> unit) -> d) m + + val sys_change: (v -> d) -> v sys_change_info + (** Compute incremental constraint system change from old solution. *) +end + +(** Any system of side-effecting equations over lattices. *) +module type EqConstrSys = MonSystem with type 'a m := 'a option + +(** A side-effecting system with globals. *) +module type GlobConstrSys = +sig + module LVar : VarType + module GVar : VarType + + module D : Lattice.S + module G : Lattice.S + val system : LVar.t -> ((LVar.t -> D.t) -> (LVar.t -> D.t -> unit) -> (GVar.t -> G.t) -> (GVar.t -> G.t -> unit) -> D.t) option + val iter_vars: (LVar.t -> D.t) -> (GVar.t -> G.t) -> VarQuery.t -> LVar.t VarQuery.f -> GVar.t VarQuery.f -> unit + val sys_change: (LVar.t -> D.t) -> (GVar.t -> G.t) -> [`L of LVar.t | `G of GVar.t] sys_change_info +end + +(** A solver is something that can translate a system into a solution (hash-table). + Incremental solver has data to be marshaled. *) +module type GenericEqIncrSolverBase = + functor (S:EqConstrSys) -> + functor (H:Hashtbl.S with type key=S.v) -> + sig + type marshal + + val copy_marshal: marshal -> marshal + val relift_marshal: marshal -> marshal + + (** The hash-map that is the first component of [solve xs vs] is a local solution for interesting variables [vs], + reached from starting values [xs]. + As a second component the solver returns data structures for incremental serialization. *) + val solve : (S.v*S.d) list -> S.v list -> marshal option -> S.d H.t * marshal + end + +(** (Incremental) solver argument, indicating which postsolving should be performed by the solver. *) +module type IncrSolverArg = +sig + val should_prune: bool + val should_verify: bool + val should_warn: bool + val should_save_run: bool +end + +(** An incremental solver takes the argument about postsolving. *) +module type GenericEqIncrSolver = + functor (Arg: IncrSolverArg) -> + GenericEqIncrSolverBase + +(** A solver is something that can translate a system into a solution (hash-table) *) +module type GenericEqSolver = + functor (S:EqConstrSys) -> + functor (H:Hashtbl.S with type key=S.v) -> + sig + (** The hash-map that is the first component of [solve xs vs] is a local solution for interesting variables [vs], + reached from starting values [xs]. *) + val solve : (S.v*S.d) list -> S.v list -> S.d H.t + end + +(** A solver is something that can translate a system into a solution (hash-table) *) +module type GenericGlobSolver = + functor (S:GlobConstrSys) -> + functor (LH:Hashtbl.S with type key=S.LVar.t) -> + functor (GH:Hashtbl.S with type key=S.GVar.t) -> + sig + type marshal + + val copy_marshal: marshal -> marshal + val relift_marshal: marshal -> marshal + + (** The hash-map that is the first component of [solve xs vs] is a local solution for interesting variables [vs], + reached from starting values [xs]. + As a second component the solver returns data structures for incremental serialization. *) + val solve : (S.LVar.t*S.D.t) list -> (S.GVar.t*S.G.t) list -> S.LVar.t list -> marshal option -> (S.D.t LH.t * S.G.t GH.t) * marshal + end \ No newline at end of file diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 8039a867d8..28e6f2f287 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -5,6 +5,7 @@ open Batteries open GoblintCil open MyCFG open Analyses +open ConstrSys open GobConfig module M = Messages diff --git a/src/framework/control.ml b/src/framework/control.ml index 54fd1d7774..26ef8bbda0 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -6,6 +6,7 @@ open Batteries open GoblintCil open MyCFG open Analyses +open ConstrSys open GobConfig open Constraints diff --git a/src/goblint_lib.ml b/src/goblint_lib.ml index 2cbe737079..a340cb085f 100644 --- a/src/goblint_lib.ml +++ b/src/goblint_lib.ml @@ -21,6 +21,7 @@ module CfgTools = CfgTools A dynamic composition of analyses is combined with CFGs to produce a constraint system. *) module Analyses = Analyses +module ConstrSys = ConstrSys module Constraints = Constraints module AnalysisState = AnalysisState module AnalysisStateUtil = AnalysisStateUtil diff --git a/src/solvers/effectWConEq.ml b/src/solvers/effectWConEq.ml index c6dcf8f0e9..2455dc10f2 100644 --- a/src/solvers/effectWConEq.ml +++ b/src/solvers/effectWConEq.ml @@ -1,7 +1,7 @@ (** ([effectWConEq]). *) open Batteries -open Analyses +open ConstrSys open Constraints module Make = diff --git a/src/solvers/generic.ml b/src/solvers/generic.ml index 2569341dd1..025074c149 100644 --- a/src/solvers/generic.ml +++ b/src/solvers/generic.ml @@ -2,7 +2,7 @@ open Batteries open GobConfig -open Analyses +open ConstrSys module LoadRunSolver: GenericEqSolver = functor (S: EqConstrSys) (VH: Hashtbl.S with type key = S.v) -> diff --git a/src/solvers/postSolver.ml b/src/solvers/postSolver.ml index e01560c752..ebfa17063a 100644 --- a/src/solvers/postSolver.ml +++ b/src/solvers/postSolver.ml @@ -1,9 +1,10 @@ (** Extra constraint system evaluation pass for warning generation, verification, pruning, etc. *) open Batteries -open Analyses +open ConstrSys open GobConfig module Pretty = GoblintCil.Pretty +module M = Messages (** Postsolver with hooks. *) module type S = diff --git a/src/solvers/sLR.ml b/src/solvers/sLR.ml index 4904731b61..d6bc2a56a5 100644 --- a/src/solvers/sLR.ml +++ b/src/solvers/sLR.ml @@ -3,7 +3,7 @@ @see Apinis, K. Frameworks for analyzing multi-threaded C. *) open Batteries -open Analyses +open ConstrSys open Constraints open Messages diff --git a/src/solvers/sLRphased.ml b/src/solvers/sLRphased.ml index c120a7bc6c..5f48669b14 100644 --- a/src/solvers/sLRphased.ml +++ b/src/solvers/sLRphased.ml @@ -1,7 +1,7 @@ (** Two-phased terminating SLR3 solver ([slr3tp]). *) open Batteries -open Analyses +open ConstrSys open Constraints open Messages open SLR diff --git a/src/solvers/sLRterm.ml b/src/solvers/sLRterm.ml index eb11447d11..b90e195ec4 100644 --- a/src/solvers/sLRterm.ml +++ b/src/solvers/sLRterm.ml @@ -2,7 +2,7 @@ Simpler version of {!SLRphased} without phases. *) open Batteries -open Analyses +open ConstrSys open Constraints open Messages open SLR diff --git a/src/solvers/selector.ml b/src/solvers/selector.ml index 664cbe0513..854b8e1036 100644 --- a/src/solvers/selector.ml +++ b/src/solvers/selector.ml @@ -1,7 +1,7 @@ (** Solver, which delegates at runtime to the configured solver. *) open Batteries -open Analyses +open ConstrSys open GobConfig (* Registered solvers. *) diff --git a/src/solvers/td3.ml b/src/solvers/td3.ml index 07edc632c7..b2696787e6 100644 --- a/src/solvers/td3.ml +++ b/src/solvers/td3.ml @@ -15,9 +15,11 @@ *) open Batteries -open Analyses +open ConstrSys open Messages +module M = Messages + module type Hooks = sig module S: EqConstrSys diff --git a/src/solvers/topDown.ml b/src/solvers/topDown.ml index c6b20d28db..fe6aaf53da 100644 --- a/src/solvers/topDown.ml +++ b/src/solvers/topDown.ml @@ -2,7 +2,7 @@ Simpler version of {!Td3} without terminating, space-efficiency and incremental. *) open Batteries -open Analyses +open ConstrSys open Constraints open Messages diff --git a/src/solvers/topDown_deprecated.ml b/src/solvers/topDown_deprecated.ml index 1f51244458..3e1329aa19 100644 --- a/src/solvers/topDown_deprecated.ml +++ b/src/solvers/topDown_deprecated.ml @@ -1,7 +1,7 @@ (** Deprecated top-down solver ([topdown_deprecated]). *) open Batteries -open Analyses +open ConstrSys open Constraints open Messages diff --git a/src/solvers/topDown_space_cache_term.ml b/src/solvers/topDown_space_cache_term.ml index a78d90559d..1bf8127fb9 100644 --- a/src/solvers/topDown_space_cache_term.ml +++ b/src/solvers/topDown_space_cache_term.ml @@ -2,7 +2,7 @@ Simpler version of {!Td3} without incremental. *) open Batteries -open Analyses +open ConstrSys open Constraints open Messages diff --git a/src/solvers/topDown_term.ml b/src/solvers/topDown_term.ml index ec07995586..f62aa74a5c 100644 --- a/src/solvers/topDown_term.ml +++ b/src/solvers/topDown_term.ml @@ -2,7 +2,7 @@ Simpler version of {!Td3} without space-efficiency and incremental. *) open Batteries -open Analyses +open ConstrSys open Constraints open Messages diff --git a/src/solvers/worklist.ml b/src/solvers/worklist.ml index b525764c74..2954928a23 100644 --- a/src/solvers/worklist.ml +++ b/src/solvers/worklist.ml @@ -1,7 +1,7 @@ (** Worklist solver ([WL]). *) open Batteries -open Analyses +open ConstrSys open Constraints module Make = From f97869c3aa7e655bdb8fe5bebf445b5959cbe63e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 28 Dec 2023 15:09:59 +0200 Subject: [PATCH 1300/1312] Extract constraint systems from Constraints module --- src/framework/constrSys.ml | 176 +++++++++++++++++++++- src/framework/constraints.ml | 189 ------------------------ src/solvers/effectWConEq.ml | 3 +- src/solvers/generic.ml | 2 +- src/solvers/postSolver.ml | 19 +++ src/solvers/sLR.ml | 29 ++-- src/solvers/sLRphased.ml | 3 +- src/solvers/sLRterm.ml | 3 +- src/solvers/td3.ml | 2 +- src/solvers/topDown.ml | 3 +- src/solvers/topDown_deprecated.ml | 3 +- src/solvers/topDown_space_cache_term.ml | 3 +- src/solvers/topDown_term.ml | 3 +- src/solvers/worklist.ml | 3 +- 14 files changed, 218 insertions(+), 223 deletions(-) diff --git a/src/framework/constrSys.ml b/src/framework/constrSys.ml index 936e03355c..1698d5f214 100644 --- a/src/framework/constrSys.ml +++ b/src/framework/constrSys.ml @@ -122,4 +122,178 @@ module type GenericGlobSolver = reached from starting values [xs]. As a second component the solver returns data structures for incremental serialization. *) val solve : (S.LVar.t*S.D.t) list -> (S.GVar.t*S.G.t) list -> S.LVar.t list -> marshal option -> (S.D.t LH.t * S.G.t GH.t) * marshal - end \ No newline at end of file + end + + +(** Combined variables so that we can also use the more common [EqConstrSys] + that uses only one kind of a variable. *) +module Var2 (LV:VarType) (GV:VarType) + : VarType + with type t = [ `L of LV.t | `G of GV.t ] += +struct + type t = [ `L of LV.t | `G of GV.t ] [@@deriving eq, ord, hash] + let relift = function + | `L x -> `L (LV.relift x) + | `G x -> `G (GV.relift x) + + let pretty_trace () = function + | `L a -> GoblintCil.Pretty.dprintf "L:%a" LV.pretty_trace a + | `G a -> GoblintCil.Pretty.dprintf "G:%a" GV.pretty_trace a + + let printXml f = function + | `L a -> LV.printXml f a + | `G a -> GV.printXml f a + + let var_id = function + | `L a -> LV.var_id a + | `G a -> GV.var_id a + + let node = function + | `L a -> LV.node a + | `G a -> GV.node a + + let is_write_only = function + | `L a -> LV.is_write_only a + | `G a -> GV.is_write_only a +end + + +(** Translate a [GlobConstrSys] into a [EqConstrSys] *) +module EqConstrSysFromGlobConstrSys (S:GlobConstrSys) + : EqConstrSys with type v = Var2(S.LVar)(S.GVar).t + and type d = Lattice.Lift2(S.G)(S.D).t + and module Var = Var2(S.LVar)(S.GVar) + and module Dom = Lattice.Lift2(S.G)(S.D) += +struct + module Var = Var2(S.LVar)(S.GVar) + module Dom = + struct + include Lattice.Lift2 (S.G) (S.D) + let printXml f = function + | `Lifted1 a -> S.G.printXml f a + | `Lifted2 a -> S.D.printXml f a + | (`Bot | `Top) as x -> printXml f x + end + type v = Var.t + type d = Dom.t + + let getG = function + | `Lifted1 x -> x + | `Bot -> S.G.bot () + | `Top -> failwith "EqConstrSysFromGlobConstrSys.getG: global variable has top value" + | `Lifted2 _ -> failwith "EqConstrSysFromGlobConstrSys.getG: global variable has local value" + + let getL = function + | `Lifted2 x -> x + | `Bot -> S.D.bot () + | `Top -> failwith "EqConstrSysFromGlobConstrSys.getL: local variable has top value" + | `Lifted1 _ -> failwith "EqConstrSysFromGlobConstrSys.getL: local variable has global value" + + let l, g = (fun x -> `L x), (fun x -> `G x) + let lD, gD = (fun x -> `Lifted2 x), (fun x -> `Lifted1 x) + + let conv f get set = + f (getL % get % l) (fun x v -> set (l x) (lD v)) + (getG % get % g) (fun x v -> set (g x) (gD v)) + |> lD + + let system = function + | `G _ -> None + | `L x -> Option.map conv (S.system x) + + let sys_change get = + S.sys_change (getL % get % l) (getG % get % g) +end + +(** Splits a [EqConstrSys] solution into a [GlobConstrSys] solution with given [Hashtbl.S] for the [EqConstrSys]. *) +module GlobConstrSolFromEqConstrSolBase (S: GlobConstrSys) (LH: Hashtbl.S with type key = S.LVar.t) (GH: Hashtbl.S with type key = S.GVar.t) (VH: Hashtbl.S with type key = Var2 (S.LVar) (S.GVar).t) = +struct + let split_solution hm = + let l' = LH.create 113 in + let g' = GH.create 113 in + let split_vars x d = match x with + | `L x -> + begin match d with + | `Lifted2 d -> LH.replace l' x d + (* | `Bot -> () *) + (* Since Verify2 is broken and only checks existing keys, add it with local bottom value. + This works around some cases, where Verify2 would not detect a problem due to completely missing variable. *) + | `Bot -> LH.replace l' x (S.D.bot ()) + | `Top -> failwith "GlobConstrSolFromEqConstrSolBase.split_vars: local variable has top value" + | `Lifted1 _ -> failwith "GlobConstrSolFromEqConstrSolBase.split_vars: local variable has global value" + end + | `G x -> + begin match d with + | `Lifted1 d -> GH.replace g' x d + | `Bot -> () + | `Top -> failwith "GlobConstrSolFromEqConstrSolBase.split_vars: global variable has top value" + | `Lifted2 _ -> failwith "GlobConstrSolFromEqConstrSolBase.split_vars: global variable has local value" + end + in + VH.iter split_vars hm; + (l', g') +end + +(** Splits a [EqConstrSys] solution into a [GlobConstrSys] solution. *) +module GlobConstrSolFromEqConstrSol (S: GlobConstrSys) (LH: Hashtbl.S with type key = S.LVar.t) (GH: Hashtbl.S with type key = S.GVar.t) = +struct + module S2 = EqConstrSysFromGlobConstrSys (S) + module VH = Hashtbl.Make (S2.Var) + + include GlobConstrSolFromEqConstrSolBase (S) (LH) (GH) (VH) +end + +(** Transforms a [GenericEqIncrSolver] into a [GenericGlobSolver]. *) +module GlobSolverFromEqSolver (Sol:GenericEqIncrSolverBase) + = functor (S:GlobConstrSys) -> + functor (LH:Hashtbl.S with type key=S.LVar.t) -> + functor (GH:Hashtbl.S with type key=S.GVar.t) -> + struct + module EqSys = EqConstrSysFromGlobConstrSys (S) + + module VH : Hashtbl.S with type key=EqSys.v = Hashtbl.Make(EqSys.Var) + module Sol' = Sol (EqSys) (VH) + + module Splitter = GlobConstrSolFromEqConstrSolBase (S) (LH) (GH) (VH) (* reuse EqSys and VH *) + + type marshal = Sol'.marshal + + let copy_marshal = Sol'.copy_marshal + let relift_marshal = Sol'.relift_marshal + + let solve ls gs l old_data = + let vs = List.map (fun (x,v) -> `L x, `Lifted2 v) ls + @ List.map (fun (x,v) -> `G x, `Lifted1 v) gs in + let sv = List.map (fun x -> `L x) l in + let hm, solver_data = Sol'.solve vs sv old_data in + Splitter.split_solution hm, solver_data + end + + +(** [EqConstrSys] where [current_var] indicates the variable whose right-hand side is currently being evaluated. *) +module CurrentVarEqConstrSys (S: EqConstrSys) = +struct + let current_var = ref None + + module S = + struct + include S + + let system x = + match S.system x with + | None -> None + | Some f -> + let f' get set = + let old_current_var = !current_var in + current_var := Some x; + Fun.protect ~finally:(fun () -> + current_var := old_current_var + ) (fun () -> + f get set + ) + in + Some f' + end +end diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 28e6f2f287..f5c024c24f 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -502,38 +502,6 @@ sig val increment: increment_data option end -(** Combined variables so that we can also use the more common [EqConstrSys] - that uses only one kind of a variable. *) -module Var2 (LV:VarType) (GV:VarType) - : VarType - with type t = [ `L of LV.t | `G of GV.t ] -= -struct - type t = [ `L of LV.t | `G of GV.t ] [@@deriving eq, ord, hash] - let relift = function - | `L x -> `L (LV.relift x) - | `G x -> `G (GV.relift x) - - let pretty_trace () = function - | `L a -> Pretty.dprintf "L:%a" LV.pretty_trace a - | `G a -> Pretty.dprintf "G:%a" GV.pretty_trace a - - let printXml f = function - | `L a -> LV.printXml f a - | `G a -> GV.printXml f a - - let var_id = function - | `L a -> LV.var_id a - | `G a -> GV.var_id a - - let node = function - | `L a -> LV.node a - | `G a -> GV.node a - - let is_write_only = function - | `L a -> LV.is_write_only a - | `G a -> GV.is_write_only a -end (** The main point of this file---generating a [GlobConstrSys] from a [Spec]. *) module FromSpec (S:Spec) (Cfg:CfgBackward) (I: Increment) @@ -1054,137 +1022,6 @@ struct {obsolete; delete; reluctant; restart} end -(** Convert a non-incremental solver into an "incremental" solver. - It will solve from scratch, perform standard postsolving and have no marshal data. *) -module EqIncrSolverFromEqSolver (Sol: GenericEqSolver): GenericEqIncrSolver = - functor (Arg: IncrSolverArg) (S: EqConstrSys) (VH: Hashtbl.S with type key = S.v) -> - struct - module Sol = Sol (S) (VH) - module Post = PostSolver.MakeList (PostSolver.ListArgFromStdArg (S) (VH) (Arg)) - - type marshal = unit - let copy_marshal () = () - let relift_marshal () = () - - let solve xs vs _ = - let vh = Sol.solve xs vs in - Post.post xs vs vh; - (vh, ()) - end - - -(** Translate a [GlobConstrSys] into a [EqConstrSys] *) -module EqConstrSysFromGlobConstrSys (S:GlobConstrSys) - : EqConstrSys with type v = Var2(S.LVar)(S.GVar).t - and type d = Lattice.Lift2(S.G)(S.D).t - and module Var = Var2(S.LVar)(S.GVar) - and module Dom = Lattice.Lift2(S.G)(S.D) -= -struct - module Var = Var2(S.LVar)(S.GVar) - module Dom = - struct - include Lattice.Lift2 (S.G) (S.D) - let printXml f = function - | `Lifted1 a -> S.G.printXml f a - | `Lifted2 a -> S.D.printXml f a - | (`Bot | `Top) as x -> printXml f x - end - type v = Var.t - type d = Dom.t - - let getG = function - | `Lifted1 x -> x - | `Bot -> S.G.bot () - | `Top -> failwith "EqConstrSysFromGlobConstrSys.getG: global variable has top value" - | `Lifted2 _ -> failwith "EqConstrSysFromGlobConstrSys.getG: global variable has local value" - - let getL = function - | `Lifted2 x -> x - | `Bot -> S.D.bot () - | `Top -> failwith "EqConstrSysFromGlobConstrSys.getL: local variable has top value" - | `Lifted1 _ -> failwith "EqConstrSysFromGlobConstrSys.getL: local variable has global value" - - let l, g = (fun x -> `L x), (fun x -> `G x) - let lD, gD = (fun x -> `Lifted2 x), (fun x -> `Lifted1 x) - - let conv f get set = - f (getL % get % l) (fun x v -> set (l x) (lD v)) - (getG % get % g) (fun x v -> set (g x) (gD v)) - |> lD - - let system = function - | `G _ -> None - | `L x -> Option.map conv (S.system x) - - let sys_change get = - S.sys_change (getL % get % l) (getG % get % g) -end - -(** Splits a [EqConstrSys] solution into a [GlobConstrSys] solution with given [Hashtbl.S] for the [EqConstrSys]. *) -module GlobConstrSolFromEqConstrSolBase (S: GlobConstrSys) (LH: Hashtbl.S with type key = S.LVar.t) (GH: Hashtbl.S with type key = S.GVar.t) (VH: Hashtbl.S with type key = Var2 (S.LVar) (S.GVar).t) = -struct - let split_solution hm = - let l' = LH.create 113 in - let g' = GH.create 113 in - let split_vars x d = match x with - | `L x -> - begin match d with - | `Lifted2 d -> LH.replace l' x d - (* | `Bot -> () *) - (* Since Verify2 is broken and only checks existing keys, add it with local bottom value. - This works around some cases, where Verify2 would not detect a problem due to completely missing variable. *) - | `Bot -> LH.replace l' x (S.D.bot ()) - | `Top -> failwith "GlobConstrSolFromEqConstrSolBase.split_vars: local variable has top value" - | `Lifted1 _ -> failwith "GlobConstrSolFromEqConstrSolBase.split_vars: local variable has global value" - end - | `G x -> - begin match d with - | `Lifted1 d -> GH.replace g' x d - | `Bot -> () - | `Top -> failwith "GlobConstrSolFromEqConstrSolBase.split_vars: global variable has top value" - | `Lifted2 _ -> failwith "GlobConstrSolFromEqConstrSolBase.split_vars: global variable has local value" - end - in - VH.iter split_vars hm; - (l', g') -end - -(** Splits a [EqConstrSys] solution into a [GlobConstrSys] solution. *) -module GlobConstrSolFromEqConstrSol (S: GlobConstrSys) (LH: Hashtbl.S with type key = S.LVar.t) (GH: Hashtbl.S with type key = S.GVar.t) = -struct - module S2 = EqConstrSysFromGlobConstrSys (S) - module VH = Hashtbl.Make (S2.Var) - - include GlobConstrSolFromEqConstrSolBase (S) (LH) (GH) (VH) -end - -(** Transforms a [GenericEqIncrSolver] into a [GenericGlobSolver]. *) -module GlobSolverFromEqSolver (Sol:GenericEqIncrSolverBase) - = functor (S:GlobConstrSys) -> - functor (LH:Hashtbl.S with type key=S.LVar.t) -> - functor (GH:Hashtbl.S with type key=S.GVar.t) -> - struct - module EqSys = EqConstrSysFromGlobConstrSys (S) - - module VH : Hashtbl.S with type key=EqSys.v = Hashtbl.Make(EqSys.Var) - module Sol' = Sol (EqSys) (VH) - - module Splitter = GlobConstrSolFromEqConstrSolBase (S) (LH) (GH) (VH) (* reuse EqSys and VH *) - - type marshal = Sol'.marshal - - let copy_marshal = Sol'.copy_marshal - let relift_marshal = Sol'.relift_marshal - - let solve ls gs l old_data = - let vs = List.map (fun (x,v) -> `L x, `Lifted2 v) ls - @ List.map (fun (x,v) -> `G x, `Lifted1 v) gs in - let sv = List.map (fun x -> `L x) l in - let hm, solver_data = Sol'.solve vs sv old_data in - Splitter.split_solution hm, solver_data - end - (** Add path sensitivity to a analysis *) module PathSensitive2 (Spec:Spec) @@ -2057,29 +1894,3 @@ struct ignore (Pretty.printf "Nodes comparison summary: %t\n" (fun () -> msg)); print_newline (); end - -(** [EqConstrSys] where [current_var] indicates the variable whose right-hand side is currently being evaluated. *) -module CurrentVarEqConstrSys (S: EqConstrSys) = -struct - let current_var = ref None - - module S = - struct - include S - - let system x = - match S.system x with - | None -> None - | Some f -> - let f' get set = - let old_current_var = !current_var in - current_var := Some x; - Fun.protect ~finally:(fun () -> - current_var := old_current_var - ) (fun () -> - f get set - ) - in - Some f' - end -end diff --git a/src/solvers/effectWConEq.ml b/src/solvers/effectWConEq.ml index 2455dc10f2..3cca6361b4 100644 --- a/src/solvers/effectWConEq.ml +++ b/src/solvers/effectWConEq.ml @@ -2,7 +2,6 @@ open Batteries open ConstrSys -open Constraints module Make = functor (S:EqConstrSys) -> @@ -88,4 +87,4 @@ module Make = end let _ = - Selector.add_solver ("effectWConEq", (module EqIncrSolverFromEqSolver (Make))); + Selector.add_solver ("effectWConEq", (module PostSolver.EqIncrSolverFromEqSolver (Make))); diff --git a/src/solvers/generic.ml b/src/solvers/generic.ml index 025074c149..636aed8831 100644 --- a/src/solvers/generic.ml +++ b/src/solvers/generic.ml @@ -30,7 +30,7 @@ module LoadRunSolver: GenericEqSolver = end module LoadRunIncrSolver: GenericEqIncrSolver = - Constraints.EqIncrSolverFromEqSolver (LoadRunSolver) + PostSolver.EqIncrSolverFromEqSolver (LoadRunSolver) module SolverStats (S:EqConstrSys) (HM:Hashtbl.S with type key = S.v) = struct diff --git a/src/solvers/postSolver.ml b/src/solvers/postSolver.ml index ebfa17063a..7f4f9c2b1f 100644 --- a/src/solvers/postSolver.ml +++ b/src/solvers/postSolver.ml @@ -316,3 +316,22 @@ struct |> List.map snd |> List.map (fun (module F: F) -> (module F (S) (VH): M)) end + +(* Here to avoid module cycle between ConstrSys and PostSolver. *) +(** Convert a non-incremental solver into an "incremental" solver. + It will solve from scratch, perform standard postsolving and have no marshal data. *) +module EqIncrSolverFromEqSolver (Sol: GenericEqSolver): GenericEqIncrSolver = + functor (Arg: IncrSolverArg) (S: EqConstrSys) (VH: Hashtbl.S with type key = S.v) -> + struct + module Sol = Sol (S) (VH) + module Post = MakeList (ListArgFromStdArg (S) (VH) (Arg)) + + type marshal = unit + let copy_marshal () = () + let relift_marshal () = () + + let solve xs vs _ = + let vh = Sol.solve xs vs in + Post.post xs vs vh; + (vh, ()) + end diff --git a/src/solvers/sLR.ml b/src/solvers/sLR.ml index d6bc2a56a5..d05d87c4f3 100644 --- a/src/solvers/sLR.ml +++ b/src/solvers/sLR.ml @@ -4,7 +4,6 @@ open Batteries open ConstrSys -open Constraints open Messages let narrow f = if GobConfig.get_bool "exp.no-narrow" then (fun a b -> a) else f @@ -522,29 +521,29 @@ let _ = let module W1 = JustWiden (struct let ver = 1 end) in let module W2 = JustWiden (struct let ver = 2 end) in let module W3 = JustWiden (struct let ver = 3 end) in - Selector.add_solver ("widen1", (module EqIncrSolverFromEqSolver (W1))); - Selector.add_solver ("widen2", (module EqIncrSolverFromEqSolver (W2))); - Selector.add_solver ("widen3", (module EqIncrSolverFromEqSolver (W3))); + Selector.add_solver ("widen1", (module PostSolver.EqIncrSolverFromEqSolver (W1))); + Selector.add_solver ("widen2", (module PostSolver.EqIncrSolverFromEqSolver (W2))); + Selector.add_solver ("widen3", (module PostSolver.EqIncrSolverFromEqSolver (W3))); let module S2 = TwoPhased (struct let ver = 1 end) in - Selector.add_solver ("two", (module EqIncrSolverFromEqSolver (S2))); + Selector.add_solver ("two", (module PostSolver.EqIncrSolverFromEqSolver (S2))); let module S1 = Make (struct let ver = 1 end) in - Selector.add_solver ("new", (module EqIncrSolverFromEqSolver (S1))); - Selector.add_solver ("slr+", (module EqIncrSolverFromEqSolver (S1))) + Selector.add_solver ("new", (module PostSolver.EqIncrSolverFromEqSolver (S1))); + Selector.add_solver ("slr+", (module PostSolver.EqIncrSolverFromEqSolver (S1))) let _ = let module S1 = Make (struct let ver = 1 end) in let module S2 = Make (struct let ver = 2 end) in let module S3 = SLR3 in let module S4 = Make (struct let ver = 4 end) in - Selector.add_solver ("slr1", (module EqIncrSolverFromEqSolver (S1))); (* W&N at every program point *) - Selector.add_solver ("slr2", (module EqIncrSolverFromEqSolver (S2))); (* W&N dynamic at certain points, growing number of W-points *) - Selector.add_solver ("slr3", (module EqIncrSolverFromEqSolver (S3))); (* same as S2 but number of W-points may also shrink *) - Selector.add_solver ("slr4", (module EqIncrSolverFromEqSolver (S4))); (* restarting: set influenced variables to bot and start up-iteration instead of narrowing *) + Selector.add_solver ("slr1", (module PostSolver.EqIncrSolverFromEqSolver (S1))); (* W&N at every program point *) + Selector.add_solver ("slr2", (module PostSolver.EqIncrSolverFromEqSolver (S2))); (* W&N dynamic at certain points, growing number of W-points *) + Selector.add_solver ("slr3", (module PostSolver.EqIncrSolverFromEqSolver (S3))); (* same as S2 but number of W-points may also shrink *) + Selector.add_solver ("slr4", (module PostSolver.EqIncrSolverFromEqSolver (S4))); (* restarting: set influenced variables to bot and start up-iteration instead of narrowing *) let module S1p = PrintInfluence (Make (struct let ver = 1 end)) in let module S2p = PrintInfluence (Make (struct let ver = 2 end)) in let module S3p = PrintInfluence (Make (struct let ver = 3 end)) in let module S4p = PrintInfluence (Make (struct let ver = 4 end)) in - Selector.add_solver ("slr1p", (module EqIncrSolverFromEqSolver (S1p))); (* same as S1-4 above but with side-effects *) - Selector.add_solver ("slr2p", (module EqIncrSolverFromEqSolver (S2p))); - Selector.add_solver ("slr3p", (module EqIncrSolverFromEqSolver (S3p))); - Selector.add_solver ("slr4p", (module EqIncrSolverFromEqSolver (S4p))); + Selector.add_solver ("slr1p", (module PostSolver.EqIncrSolverFromEqSolver (S1p))); (* same as S1-4 above but with side-effects *) + Selector.add_solver ("slr2p", (module PostSolver.EqIncrSolverFromEqSolver (S2p))); + Selector.add_solver ("slr3p", (module PostSolver.EqIncrSolverFromEqSolver (S3p))); + Selector.add_solver ("slr4p", (module PostSolver.EqIncrSolverFromEqSolver (S4p))); diff --git a/src/solvers/sLRphased.ml b/src/solvers/sLRphased.ml index 5f48669b14..17571f0138 100644 --- a/src/solvers/sLRphased.ml +++ b/src/solvers/sLRphased.ml @@ -2,7 +2,6 @@ open Batteries open ConstrSys -open Constraints open Messages open SLR @@ -205,4 +204,4 @@ module Make = end let _ = - Selector.add_solver ("slr3tp", (module EqIncrSolverFromEqSolver (Make))); (* two-phased slr3t *) + Selector.add_solver ("slr3tp", (module PostSolver.EqIncrSolverFromEqSolver (Make))); (* two-phased slr3t *) diff --git a/src/solvers/sLRterm.ml b/src/solvers/sLRterm.ml index b90e195ec4..8ec34c7dc2 100644 --- a/src/solvers/sLRterm.ml +++ b/src/solvers/sLRterm.ml @@ -3,7 +3,6 @@ open Batteries open ConstrSys -open Constraints open Messages open SLR @@ -224,4 +223,4 @@ module SLR3term = end let _ = - Selector.add_solver ("slr3t", (module EqIncrSolverFromEqSolver (SLR3term))); (* same as S2 but number of W-points may also shrink + terminating? *) + Selector.add_solver ("slr3t", (module PostSolver.EqIncrSolverFromEqSolver (SLR3term))); (* same as S2 but number of W-points may also shrink + terminating? *) diff --git a/src/solvers/td3.ml b/src/solvers/td3.ml index b2696787e6..54b7520cd6 100644 --- a/src/solvers/td3.ml +++ b/src/solvers/td3.ml @@ -194,7 +194,7 @@ module Base = type phase = Widen | Narrow [@@deriving show] (* used in inner solve *) - module CurrentVarS = Constraints.CurrentVarEqConstrSys (S) + module CurrentVarS = ConstrSys.CurrentVarEqConstrSys (S) module S = CurrentVarS.S let solve st vs marshal = diff --git a/src/solvers/topDown.ml b/src/solvers/topDown.ml index fe6aaf53da..f7da560057 100644 --- a/src/solvers/topDown.ml +++ b/src/solvers/topDown.ml @@ -3,7 +3,6 @@ open Batteries open ConstrSys -open Constraints open Messages module WP = @@ -155,4 +154,4 @@ module WP = end let _ = - Selector.add_solver ("topdown", (module EqIncrSolverFromEqSolver (WP))); + Selector.add_solver ("topdown", (module PostSolver.EqIncrSolverFromEqSolver (WP))); diff --git a/src/solvers/topDown_deprecated.ml b/src/solvers/topDown_deprecated.ml index 3e1329aa19..4e9799cf78 100644 --- a/src/solvers/topDown_deprecated.ml +++ b/src/solvers/topDown_deprecated.ml @@ -2,7 +2,6 @@ open Batteries open ConstrSys -open Constraints open Messages exception SolverCannotDoGlobals @@ -164,4 +163,4 @@ module TD3 = end let _ = - Selector.add_solver ("topdown_deprecated", (module EqIncrSolverFromEqSolver (TD3))); + Selector.add_solver ("topdown_deprecated", (module PostSolver.EqIncrSolverFromEqSolver (TD3))); diff --git a/src/solvers/topDown_space_cache_term.ml b/src/solvers/topDown_space_cache_term.ml index 1bf8127fb9..f6c256517c 100644 --- a/src/solvers/topDown_space_cache_term.ml +++ b/src/solvers/topDown_space_cache_term.ml @@ -3,7 +3,6 @@ open Batteries open ConstrSys -open Constraints open Messages module WP = @@ -197,4 +196,4 @@ module WP = end let _ = - Selector.add_solver ("topdown_space_cache_term", (module EqIncrSolverFromEqSolver (WP))); + Selector.add_solver ("topdown_space_cache_term", (module PostSolver.EqIncrSolverFromEqSolver (WP))); diff --git a/src/solvers/topDown_term.ml b/src/solvers/topDown_term.ml index f62aa74a5c..d15493b5a1 100644 --- a/src/solvers/topDown_term.ml +++ b/src/solvers/topDown_term.ml @@ -3,7 +3,6 @@ open Batteries open ConstrSys -open Constraints open Messages module WP = @@ -134,4 +133,4 @@ module WP = end let _ = - Selector.add_solver ("topdown_term", (module EqIncrSolverFromEqSolver (WP))); + Selector.add_solver ("topdown_term", (module PostSolver.EqIncrSolverFromEqSolver (WP))); diff --git a/src/solvers/worklist.ml b/src/solvers/worklist.ml index 2954928a23..b1a5d7e834 100644 --- a/src/solvers/worklist.ml +++ b/src/solvers/worklist.ml @@ -2,7 +2,6 @@ open Batteries open ConstrSys -open Constraints module Make = functor (S:EqConstrSys) -> @@ -63,4 +62,4 @@ module Make = let _ = - Selector.add_solver ("WL", (module EqIncrSolverFromEqSolver (Make))); + Selector.add_solver ("WL", (module PostSolver.EqIncrSolverFromEqSolver (Make))); From 07009f020e7874a688f8517e8417ff7b29836e25 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 28 Dec 2023 15:21:21 +0200 Subject: [PATCH 1301/1312] Extract constraint system to goblint_constraint dune library --- src/{framework => constraint}/constrSys.ml | 0 src/constraint/constraint.mld | 16 ++++++++++++++++ src/constraint/dune | 21 +++++++++++++++++++++ src/{framework => constraint}/varQuery.ml | 0 src/{framework => constraint}/varQuery.mli | 0 src/dune | 2 +- src/index.mld | 3 +++ 7 files changed, 41 insertions(+), 1 deletion(-) rename src/{framework => constraint}/constrSys.ml (100%) create mode 100644 src/constraint/constraint.mld create mode 100644 src/constraint/dune rename src/{framework => constraint}/varQuery.ml (100%) rename src/{framework => constraint}/varQuery.mli (100%) diff --git a/src/framework/constrSys.ml b/src/constraint/constrSys.ml similarity index 100% rename from src/framework/constrSys.ml rename to src/constraint/constrSys.ml diff --git a/src/constraint/constraint.mld b/src/constraint/constraint.mld new file mode 100644 index 0000000000..695e7bfa0d --- /dev/null +++ b/src/constraint/constraint.mld @@ -0,0 +1,16 @@ +{0 Library goblint.constraint} +This library is unwrapped and provides the following top-level modules. +For better context, see {!Goblint_lib} which also documents these modules. + + +{1 Framework} + +{2 Specification} +{!modules: +ConstrSys +} + +{2 Results} +{!modules: +VarQuery +} diff --git a/src/constraint/dune b/src/constraint/dune new file mode 100644 index 0000000000..2d11b9010f --- /dev/null +++ b/src/constraint/dune @@ -0,0 +1,21 @@ +(include_subdirs no) + +(library + (name goblint_constraint) + (public_name goblint.constraint) + (wrapped false) ; TODO: wrap + (libraries + batteries.unthreaded + goblint_std + goblint_common + goblint_domain + goblint-cil) + (flags :standard -open Goblint_std) + (preprocess + (pps + ppx_deriving.std + ppx_deriving_hash + ppx_deriving_yojson)) + (instrumentation (backend bisect_ppx))) + +(documentation) diff --git a/src/framework/varQuery.ml b/src/constraint/varQuery.ml similarity index 100% rename from src/framework/varQuery.ml rename to src/constraint/varQuery.ml diff --git a/src/framework/varQuery.mli b/src/constraint/varQuery.mli similarity index 100% rename from src/framework/varQuery.mli rename to src/constraint/varQuery.mli diff --git a/src/dune b/src/dune index eac6640451..59845b8e03 100644 --- a/src/dune +++ b/src/dune @@ -7,7 +7,7 @@ (name goblint_lib) (public_name goblint.lib) (modules :standard \ goblint privPrecCompare apronPrecCompare messagesCompare) - (libraries goblint.sites goblint.build-info goblint-cil.all-features batteries.unthreaded qcheck-core.runner sha json-data-encoding jsonrpc cpu arg-complete fpath yaml yaml.unix uuidm goblint_timing catapult goblint_backtrace fileutils goblint_std goblint_config goblint_common goblint_domain goblint_library goblint_incremental goblint_tracing + (libraries goblint.sites goblint.build-info goblint-cil.all-features batteries.unthreaded qcheck-core.runner sha json-data-encoding jsonrpc cpu arg-complete fpath yaml yaml.unix uuidm goblint_timing catapult goblint_backtrace fileutils goblint_std goblint_config goblint_common goblint_domain goblint_constraint goblint_library goblint_incremental goblint_tracing ; Conditionally compile based on whether apron optional dependency is installed or not. ; Alternative dependencies seem like the only way to optionally depend on optional dependencies. ; See: https://dune.readthedocs.io/en/stable/concepts.html#alternative-dependencies. diff --git a/src/index.mld b/src/index.mld index 76b9d230dd..3ed2b8079f 100644 --- a/src/index.mld +++ b/src/index.mld @@ -16,6 +16,9 @@ This {{!page-common}unwrapped library} contains various common modules extracted {2 Library goblint.domain} This {{!page-domain}unwrapped library} contains various domain modules extracted from {!Goblint_lib}. +{2 Library goblint.constraint} +This {{!page-constraint}unwrapped library} contains various constraint system modules extracted from {!Goblint_lib}. + {2 Library goblint.library} This {{!page-library}unwrapped library} contains various library specification modules extracted from {!Goblint_lib}. From 834df31e5821a5a38c956abe7f029c4e0a4b122c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 28 Dec 2023 15:51:06 +0200 Subject: [PATCH 1302/1312] Extract solvers to goblint_solver dune library --- scripts/goblint-lib-modules.py | 2 ++ src/analyses/base.ml | 2 +- src/dune | 2 +- src/framework/control.ml | 6 +++--- src/goblint_lib.ml | 35 ---------------------------------- src/index.mld | 3 +++ src/maingoblint.ml | 4 ++-- src/solvers/dune | 22 +++++++++++++++++++++ src/solvers/goblint_solver.ml | 31 ++++++++++++++++++++++++++++++ 9 files changed, 65 insertions(+), 42 deletions(-) create mode 100644 src/solvers/dune create mode 100644 src/solvers/goblint_solver.ml diff --git a/scripts/goblint-lib-modules.py b/scripts/goblint-lib-modules.py index ec0e78e440..95ac9b268e 100755 --- a/scripts/goblint-lib-modules.py +++ b/scripts/goblint-lib-modules.py @@ -8,6 +8,7 @@ goblint_lib_paths = [ src_root_path / "goblint_lib.ml", + src_root_path / "solvers" / "goblint_solver.ml", src_root_path / "util" / "std" / "goblint_std.ml", ] goblint_lib_modules = set() @@ -33,6 +34,7 @@ # libraries "Goblint_std", + "Goblint_solver", "Goblint_timing", "Goblint_backtrace", "Goblint_tracing", diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 912d1f3bff..2b8ca4d429 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2871,7 +2871,7 @@ struct | "once" -> f (D.bot ()) | "fixpoint" -> - let module DFP = LocalFixpoint.Make (D) in + let module DFP = Goblint_solver.LocalFixpoint.Make (D) in DFP.lfp f | _ -> assert false diff --git a/src/dune b/src/dune index 59845b8e03..2ea9155b9b 100644 --- a/src/dune +++ b/src/dune @@ -7,7 +7,7 @@ (name goblint_lib) (public_name goblint.lib) (modules :standard \ goblint privPrecCompare apronPrecCompare messagesCompare) - (libraries goblint.sites goblint.build-info goblint-cil.all-features batteries.unthreaded qcheck-core.runner sha json-data-encoding jsonrpc cpu arg-complete fpath yaml yaml.unix uuidm goblint_timing catapult goblint_backtrace fileutils goblint_std goblint_config goblint_common goblint_domain goblint_constraint goblint_library goblint_incremental goblint_tracing + (libraries goblint.sites goblint.build-info goblint-cil.all-features batteries.unthreaded qcheck-core.runner sha json-data-encoding jsonrpc cpu arg-complete fpath yaml yaml.unix uuidm goblint_timing catapult goblint_backtrace fileutils goblint_std goblint_config goblint_common goblint_domain goblint_constraint goblint_solver goblint_library goblint_incremental goblint_tracing ; Conditionally compile based on whether apron optional dependency is installed or not. ; Alternative dependencies seem like the only way to optionally depend on optional dependencies. ; See: https://dune.readthedocs.io/en/stable/concepts.html#alternative-dependencies. diff --git a/src/framework/control.ml b/src/framework/control.ml index 26ef8bbda0..391c766feb 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -85,7 +85,7 @@ struct let save_run = let o = get_string "save_run" in if o = "" then (if gobview then "run" else "") else o in save_run <> "" end - module Slvr = (GlobSolverFromEqSolver (Selector.Make (PostSolverArg))) (EQSys) (LHT) (GHT) + module Slvr = (GlobSolverFromEqSolver (Goblint_solver.Selector.Make (PostSolverArg))) (EQSys) (LHT) (GHT) (* The comparator *) module CompareGlobSys = Constraints.CompareGlobSys (SpecSys) @@ -476,7 +476,7 @@ struct let save_run_str = let o = get_string "save_run" in if o = "" then (if gobview then "run" else "") else o in let lh, gh = if load_run <> "" then ( - let module S2' = (GlobSolverFromEqSolver (Generic.LoadRunIncrSolver (PostSolverArg))) (EQSys) (LHT) (GHT) in + let module S2' = (GlobSolverFromEqSolver (Goblint_solver.Generic.LoadRunIncrSolver (PostSolverArg))) (EQSys) (LHT) (GHT) in let (r2, _) = S2'.solve entrystates entrystates_global startvars' None in (* TODO: has incremental data? *) r2 ) else if compare_runs <> [] then ( @@ -582,7 +582,7 @@ struct let (r2, _) = S2'.solve entrystates entrystates_global startvars' None in (* TODO: has incremental data? *) CompareGlobSys.compare (get_string "solver", get_string "comparesolver") (lh,gh) (r2) in - compare_with (Selector.choose_solver (get_string "comparesolver")) + compare_with (Goblint_solver.Selector.choose_solver (get_string "comparesolver")) ); (* Most warnings happen before during postsolver, but some happen later (e.g. in finalize), so enable this for the rest (if required by option). *) diff --git a/src/goblint_lib.ml b/src/goblint_lib.ml index a340cb085f..1bc70f3f52 100644 --- a/src/goblint_lib.ml +++ b/src/goblint_lib.ml @@ -288,41 +288,6 @@ module Serialize = Serialize module CilMaps = CilMaps -(** {1 Solvers} - - Generic solvers are used to solve {{!Analyses.MonSystem} (side-effecting) constraint systems}. *) - -(** {2 Top-down} - - The top-down solver family. *) - -module Td3 = Td3 -module TopDown = TopDown -module TopDown_term = TopDown_term -module TopDown_space_cache_term = TopDown_space_cache_term -module TopDown_deprecated = TopDown_deprecated - -(** {2 SLR} - - The SLR solver family. *) - -module SLRphased = SLRphased -module SLRterm = SLRterm -module SLR = SLR - -(** {2 Other} *) - -module EffectWConEq = EffectWConEq -module Worklist = Worklist -module Generic = Generic -module Selector = Selector - -module PostSolver = PostSolver -module LocalFixpoint = LocalFixpoint -module SolverStats = SolverStats -module SolverBox = SolverBox - - (** {1 I/O} Various input/output interfaces and formats. *) diff --git a/src/index.mld b/src/index.mld index 3ed2b8079f..0763284c15 100644 --- a/src/index.mld +++ b/src/index.mld @@ -19,6 +19,9 @@ This {{!page-domain}unwrapped library} contains various domain modules extracted {2 Library goblint.constraint} This {{!page-constraint}unwrapped library} contains various constraint system modules extracted from {!Goblint_lib}. +{2 Library goblint.solver} +{!modules:Goblint_solver} + {2 Library goblint.library} This {{!page-library}unwrapped library} contains various library specification modules extracted from {!Goblint_lib}. diff --git a/src/maingoblint.ml b/src/maingoblint.ml index 2c7d353594..f1d2793d2e 100644 --- a/src/maingoblint.ml +++ b/src/maingoblint.ml @@ -513,7 +513,7 @@ let preprocess_parse_merge () = let do_stats () = if get_bool "dbg.timing.enabled" then ( print_newline (); - SolverStats.print (); + Goblint_solver.SolverStats.print (); print_newline (); print_string "Timings:\n"; Timing.Default.print (Stdlib.Format.formatter_of_out_channel @@ Messages.get_out "timing" Legacy.stderr); @@ -521,7 +521,7 @@ let do_stats () = ) let reset_stats () = - SolverStats.reset (); + Goblint_solver.SolverStats.reset (); Timing.Default.reset (); Timing.Program.reset () diff --git a/src/solvers/dune b/src/solvers/dune new file mode 100644 index 0000000000..907d082089 --- /dev/null +++ b/src/solvers/dune @@ -0,0 +1,22 @@ +(include_subdirs no) + +(library + (name goblint_solver) + (public_name goblint.solver) + (libraries + batteries.unthreaded + goblint_std + goblint_common + goblint_domain + goblint_constraint + goblint_incremental + goblint-cil) + (flags :standard -open Goblint_std) + (preprocess + (pps + ppx_deriving.std + ppx_deriving_hash + ppx_deriving_yojson)) + (instrumentation (backend bisect_ppx))) + +(documentation) diff --git a/src/solvers/goblint_solver.ml b/src/solvers/goblint_solver.ml new file mode 100644 index 0000000000..0a264d7dea --- /dev/null +++ b/src/solvers/goblint_solver.ml @@ -0,0 +1,31 @@ +(** Generic solvers for {{!ConstrSys.MonSystem} (side-effecting) constraint systems}. *) + +(** {1 Top-down} + + The top-down solver family. *) + +module Td3 = Td3 +module TopDown = TopDown +module TopDown_term = TopDown_term +module TopDown_space_cache_term = TopDown_space_cache_term +module TopDown_deprecated = TopDown_deprecated + +(** {1 SLR} + + The SLR solver family. *) + +module SLRphased = SLRphased +module SLRterm = SLRterm +module SLR = SLR + +(** {1 Other} *) + +module EffectWConEq = EffectWConEq +module Worklist = Worklist +module Generic = Generic +module Selector = Selector + +module PostSolver = PostSolver +module LocalFixpoint = LocalFixpoint +module SolverStats = SolverStats +module SolverBox = SolverBox From e9c0cc3b757e1f5904c4c1f2de70d2baba02c8e8 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 28 Dec 2023 15:54:47 +0200 Subject: [PATCH 1303/1312] Rename src/solvers -> src/solver --- scripts/goblint-lib-modules.py | 2 +- src/{solvers => solver}/dune | 0 src/{solvers => solver}/effectWConEq.ml | 0 src/{solvers => solver}/generic.ml | 0 src/{solvers => solver}/goblint_solver.ml | 0 src/{solvers => solver}/localFixpoint.ml | 0 src/{solvers => solver}/postSolver.ml | 0 src/{solvers => solver}/sLR.ml | 0 src/{solvers => solver}/sLRphased.ml | 0 src/{solvers => solver}/sLRterm.ml | 0 src/{solvers => solver}/selector.ml | 0 src/{solvers => solver}/solverBox.ml | 0 src/{solvers => solver}/solverStats.ml | 0 src/{solvers => solver}/td3.ml | 0 src/{solvers => solver}/topDown.ml | 0 src/{solvers => solver}/topDown_deprecated.ml | 0 src/{solvers => solver}/topDown_space_cache_term.ml | 0 src/{solvers => solver}/topDown_term.ml | 0 src/{solvers => solver}/worklist.ml | 0 19 files changed, 1 insertion(+), 1 deletion(-) rename src/{solvers => solver}/dune (100%) rename src/{solvers => solver}/effectWConEq.ml (100%) rename src/{solvers => solver}/generic.ml (100%) rename src/{solvers => solver}/goblint_solver.ml (100%) rename src/{solvers => solver}/localFixpoint.ml (100%) rename src/{solvers => solver}/postSolver.ml (100%) rename src/{solvers => solver}/sLR.ml (100%) rename src/{solvers => solver}/sLRphased.ml (100%) rename src/{solvers => solver}/sLRterm.ml (100%) rename src/{solvers => solver}/selector.ml (100%) rename src/{solvers => solver}/solverBox.ml (100%) rename src/{solvers => solver}/solverStats.ml (100%) rename src/{solvers => solver}/td3.ml (100%) rename src/{solvers => solver}/topDown.ml (100%) rename src/{solvers => solver}/topDown_deprecated.ml (100%) rename src/{solvers => solver}/topDown_space_cache_term.ml (100%) rename src/{solvers => solver}/topDown_term.ml (100%) rename src/{solvers => solver}/worklist.ml (100%) diff --git a/scripts/goblint-lib-modules.py b/scripts/goblint-lib-modules.py index 95ac9b268e..8ae3b4b3eb 100755 --- a/scripts/goblint-lib-modules.py +++ b/scripts/goblint-lib-modules.py @@ -8,7 +8,7 @@ goblint_lib_paths = [ src_root_path / "goblint_lib.ml", - src_root_path / "solvers" / "goblint_solver.ml", + src_root_path / "solver" / "goblint_solver.ml", src_root_path / "util" / "std" / "goblint_std.ml", ] goblint_lib_modules = set() diff --git a/src/solvers/dune b/src/solver/dune similarity index 100% rename from src/solvers/dune rename to src/solver/dune diff --git a/src/solvers/effectWConEq.ml b/src/solver/effectWConEq.ml similarity index 100% rename from src/solvers/effectWConEq.ml rename to src/solver/effectWConEq.ml diff --git a/src/solvers/generic.ml b/src/solver/generic.ml similarity index 100% rename from src/solvers/generic.ml rename to src/solver/generic.ml diff --git a/src/solvers/goblint_solver.ml b/src/solver/goblint_solver.ml similarity index 100% rename from src/solvers/goblint_solver.ml rename to src/solver/goblint_solver.ml diff --git a/src/solvers/localFixpoint.ml b/src/solver/localFixpoint.ml similarity index 100% rename from src/solvers/localFixpoint.ml rename to src/solver/localFixpoint.ml diff --git a/src/solvers/postSolver.ml b/src/solver/postSolver.ml similarity index 100% rename from src/solvers/postSolver.ml rename to src/solver/postSolver.ml diff --git a/src/solvers/sLR.ml b/src/solver/sLR.ml similarity index 100% rename from src/solvers/sLR.ml rename to src/solver/sLR.ml diff --git a/src/solvers/sLRphased.ml b/src/solver/sLRphased.ml similarity index 100% rename from src/solvers/sLRphased.ml rename to src/solver/sLRphased.ml diff --git a/src/solvers/sLRterm.ml b/src/solver/sLRterm.ml similarity index 100% rename from src/solvers/sLRterm.ml rename to src/solver/sLRterm.ml diff --git a/src/solvers/selector.ml b/src/solver/selector.ml similarity index 100% rename from src/solvers/selector.ml rename to src/solver/selector.ml diff --git a/src/solvers/solverBox.ml b/src/solver/solverBox.ml similarity index 100% rename from src/solvers/solverBox.ml rename to src/solver/solverBox.ml diff --git a/src/solvers/solverStats.ml b/src/solver/solverStats.ml similarity index 100% rename from src/solvers/solverStats.ml rename to src/solver/solverStats.ml diff --git a/src/solvers/td3.ml b/src/solver/td3.ml similarity index 100% rename from src/solvers/td3.ml rename to src/solver/td3.ml diff --git a/src/solvers/topDown.ml b/src/solver/topDown.ml similarity index 100% rename from src/solvers/topDown.ml rename to src/solver/topDown.ml diff --git a/src/solvers/topDown_deprecated.ml b/src/solver/topDown_deprecated.ml similarity index 100% rename from src/solvers/topDown_deprecated.ml rename to src/solver/topDown_deprecated.ml diff --git a/src/solvers/topDown_space_cache_term.ml b/src/solver/topDown_space_cache_term.ml similarity index 100% rename from src/solvers/topDown_space_cache_term.ml rename to src/solver/topDown_space_cache_term.ml diff --git a/src/solvers/topDown_term.ml b/src/solver/topDown_term.ml similarity index 100% rename from src/solvers/topDown_term.ml rename to src/solver/topDown_term.ml diff --git a/src/solvers/worklist.ml b/src/solver/worklist.ml similarity index 100% rename from src/solvers/worklist.ml rename to src/solver/worklist.ml From 27295d709fd74facf3ca0789c2a769594ec41919 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 28 Dec 2023 16:02:03 +0200 Subject: [PATCH 1304/1312] Fix SolverTest compilation --- unittest/dune | 2 +- unittest/solver/solverTest.ml | 6 ++++-- unittest/util/intOpsTest.ml | 1 - 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/unittest/dune b/unittest/dune index a08a4b2323..cb8dd668be 100644 --- a/unittest/dune +++ b/unittest/dune @@ -2,7 +2,7 @@ (test (name mainTest) - (libraries ounit2 qcheck-ounit goblint.std goblint.lib goblint.sites.dune goblint.build-info.dune) + (libraries ounit2 qcheck-ounit goblint.std goblint.lib goblint.constraint goblint.solver goblint.sites.dune goblint.build-info.dune) (preprocess (pps ppx_deriving.std ppx_deriving_hash ppx_deriving_yojson)) (flags :standard -linkall)) diff --git a/unittest/solver/solverTest.ml b/unittest/solver/solverTest.ml index 47ec5443ca..4e96266262 100644 --- a/unittest/solver/solverTest.ml +++ b/unittest/solver/solverTest.ml @@ -2,6 +2,8 @@ open Goblint_lib open OUnit2 open GoblintCil open Pretty +open ConstrSys +open Goblint_solver (* variables are strings *) module StringVar = @@ -43,7 +45,7 @@ module ConstrSys = struct | _ -> None let iter_vars _ _ _ _ _ = () - let sys_change _ _ = {Analyses.obsolete = []; delete = []; reluctant = []; restart = []} + let sys_change _ _ = {obsolete = []; delete = []; reluctant = []; restart = []} end module LH = BatHashtbl.Make (ConstrSys.LVar) @@ -55,7 +57,7 @@ struct let should_warn = false let should_save_run = false end -module Solver = Constraints.GlobSolverFromEqSolver (Constraints.EqIncrSolverFromEqSolver (EffectWConEq.Make) (PostSolverArg)) (ConstrSys) (LH) (GH) +module Solver = GlobSolverFromEqSolver (PostSolver.EqIncrSolverFromEqSolver (EffectWConEq.Make) (PostSolverArg)) (ConstrSys) (LH) (GH) let test1 _ = let id x = x in diff --git a/unittest/util/intOpsTest.ml b/unittest/util/intOpsTest.ml index 307d9e84b0..b0cb4dc984 100644 --- a/unittest/util/intOpsTest.ml +++ b/unittest/util/intOpsTest.ml @@ -1,6 +1,5 @@ open OUnit2 open Goblint_std -open Goblint_lib (* If the first operand of a div is negative, Zarith rounds the result away from zero. We thus always transform this into a division with a non-negative first operand. *) From 41499319b4e4463e8a0b044d2d98873ab7480b3e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 28 Dec 2023 16:11:42 +0200 Subject: [PATCH 1305/1312] Add goblint_config dependency to goblint_solver --- src/solver/dune | 1 + 1 file changed, 1 insertion(+) diff --git a/src/solver/dune b/src/solver/dune index 907d082089..bd6d7a4d0a 100644 --- a/src/solver/dune +++ b/src/solver/dune @@ -7,6 +7,7 @@ batteries.unthreaded goblint_std goblint_common + goblint_config goblint_domain goblint_constraint goblint_incremental From 580e5dce0e7d5c9e3269fd2df0581370aaa3fc14 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 28 Dec 2023 16:24:52 +0200 Subject: [PATCH 1306/1312] Update Gobview with goblint.constraint and goblint.solver dependencies --- gobview | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gobview b/gobview index 3de13d7412..c8fcb09e9a 160000 --- a/gobview +++ b/gobview @@ -1 +1 @@ -Subproject commit 3de13d74124ab7bc30d8be299f02570d8f498b84 +Subproject commit c8fcb09e9a3e27de22d4803606d5784f667a542a From c4292c3d84284f6d26825f23c77fbfeabd423677 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 28 Dec 2023 17:09:27 +0200 Subject: [PATCH 1307/1312] Move some modules from goblint_lib to goblint_common --- src/{ => common}/cdomains/floatOps/floatOps.ml | 0 src/{ => common}/cdomains/floatOps/floatOps.mli | 0 src/{ => common}/cdomains/floatOps/stubs.c | 0 src/common/common.mld | 7 +++++++ src/common/dune | 2 ++ src/{ => common}/util/analysisStateUtil.ml | 0 src/{ => common}/util/contextUtil.ml | 0 src/{ => common}/util/intOps.ml | 0 src/dune | 1 - 9 files changed, 9 insertions(+), 1 deletion(-) rename src/{ => common}/cdomains/floatOps/floatOps.ml (100%) rename src/{ => common}/cdomains/floatOps/floatOps.mli (100%) rename src/{ => common}/cdomains/floatOps/stubs.c (100%) rename src/{ => common}/util/analysisStateUtil.ml (100%) rename src/{ => common}/util/contextUtil.ml (100%) rename src/{ => common}/util/intOps.ml (100%) diff --git a/src/cdomains/floatOps/floatOps.ml b/src/common/cdomains/floatOps/floatOps.ml similarity index 100% rename from src/cdomains/floatOps/floatOps.ml rename to src/common/cdomains/floatOps/floatOps.ml diff --git a/src/cdomains/floatOps/floatOps.mli b/src/common/cdomains/floatOps/floatOps.mli similarity index 100% rename from src/cdomains/floatOps/floatOps.mli rename to src/common/cdomains/floatOps/floatOps.mli diff --git a/src/cdomains/floatOps/stubs.c b/src/common/cdomains/floatOps/stubs.c similarity index 100% rename from src/cdomains/floatOps/stubs.c rename to src/common/cdomains/floatOps/stubs.c diff --git a/src/common/common.mld b/src/common/common.mld index 2ad88c3758..2176a95b8a 100644 --- a/src/common/common.mld +++ b/src/common/common.mld @@ -16,6 +16,7 @@ CfgTools {2 Specification} {!modules: AnalysisState +AnalysisStateUtil ControlSpecC } @@ -42,6 +43,7 @@ Messages {2 General} {!modules: +IntOps LazyEval ResettableLazy MessageUtil @@ -55,6 +57,11 @@ Cilfacade RichVarinfo } +{2 Analysis-specific} +{!modules: +ContextUtil +} + {1 Library extensions} diff --git a/src/common/dune b/src/common/dune index 458ef02dcb..8576970900 100644 --- a/src/common/dune +++ b/src/common/dune @@ -16,6 +16,8 @@ goblint_timing qcheck-core.runner) (flags :standard -open Goblint_std) + (foreign_stubs (language c) (names stubs)) + (ocamlopt_flags :standard -no-float-const-prop) (preprocess (pps ppx_deriving.std diff --git a/src/util/analysisStateUtil.ml b/src/common/util/analysisStateUtil.ml similarity index 100% rename from src/util/analysisStateUtil.ml rename to src/common/util/analysisStateUtil.ml diff --git a/src/util/contextUtil.ml b/src/common/util/contextUtil.ml similarity index 100% rename from src/util/contextUtil.ml rename to src/common/util/contextUtil.ml diff --git a/src/util/intOps.ml b/src/common/util/intOps.ml similarity index 100% rename from src/util/intOps.ml rename to src/common/util/intOps.ml diff --git a/src/dune b/src/dune index 2ea9155b9b..d65acfc856 100644 --- a/src/dune +++ b/src/dune @@ -61,7 +61,6 @@ ) ) (flags :standard -open Goblint_std) - (foreign_stubs (language c) (names stubs)) (ocamlopt_flags :standard -no-float-const-prop) (preprocess (pps ppx_deriving.std ppx_deriving_hash ppx_deriving_yojson ppx_blob)) From 7ee115aa429a90e0c5a61f44ddb2c85503a12e93 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 28 Dec 2023 17:50:56 +0200 Subject: [PATCH 1308/1312] Extract value domain to goblint_cdomain_value dune library --- .../analyses/wrapperFunctionAnalysis0.ml | 0 src/cdomain/value/cdomain_value.mld | 71 +++++++++++++++++++ .../value}/cdomains/addressDomain.ml | 0 .../value}/cdomains/addressDomain.mli | 0 .../value}/cdomains/addressDomain_intf.ml | 0 .../value}/cdomains/arrayDomain.ml | 0 .../value}/cdomains/arrayDomain.mli | 0 .../value}/cdomains/concDomain.ml | 0 .../value}/cdomains/floatDomain.ml | 0 .../value}/cdomains/floatDomain.mli | 0 src/{ => cdomain/value}/cdomains/intDomain.ml | 0 .../value}/cdomains/intDomain.mli | 0 .../value}/cdomains/jmpBufDomain.ml | 0 src/{ => cdomain/value}/cdomains/lval.ml | 0 .../value}/cdomains/mutexAttrDomain.ml | 0 src/{ => cdomain/value}/cdomains/mval.ml | 0 src/{ => cdomain/value}/cdomains/mval.mli | 0 src/{ => cdomain/value}/cdomains/mval_intf.ml | 0 .../value}/cdomains/nullByteSet.ml | 0 src/{ => cdomain/value}/cdomains/offset.ml | 0 src/{ => cdomain/value}/cdomains/offset.mli | 0 .../value}/cdomains/offset_intf.ml | 0 .../value}/cdomains/preValueDomain.ml | 0 .../value}/cdomains/stringDomain.ml | 0 .../value}/cdomains/stringDomain.mli | 0 .../value}/cdomains/structDomain.ml | 0 .../value}/cdomains/structDomain.mli | 0 .../value}/cdomains/threadIdDomain.ml | 0 .../value}/cdomains/unionDomain.ml | 0 .../value}/cdomains/valueDomain.ml | 0 src/{ => cdomain/value}/domains/invariant.ml | 0 .../value}/domains/invariantCil.ml | 0 .../value}/domains/valueDomainQueries.ml | 0 src/cdomain/value/dune | 24 +++++++ src/{ => cdomain/value}/util/precisionUtil.ml | 0 .../value}/util/wideningThresholds.ml | 0 .../value}/util/wideningThresholds.mli | 0 src/dune | 2 +- src/index.mld | 3 + unittest/dune | 2 +- 40 files changed, 100 insertions(+), 2 deletions(-) rename src/{ => cdomain/value}/analyses/wrapperFunctionAnalysis0.ml (100%) create mode 100644 src/cdomain/value/cdomain_value.mld rename src/{ => cdomain/value}/cdomains/addressDomain.ml (100%) rename src/{ => cdomain/value}/cdomains/addressDomain.mli (100%) rename src/{ => cdomain/value}/cdomains/addressDomain_intf.ml (100%) rename src/{ => cdomain/value}/cdomains/arrayDomain.ml (100%) rename src/{ => cdomain/value}/cdomains/arrayDomain.mli (100%) rename src/{ => cdomain/value}/cdomains/concDomain.ml (100%) rename src/{ => cdomain/value}/cdomains/floatDomain.ml (100%) rename src/{ => cdomain/value}/cdomains/floatDomain.mli (100%) rename src/{ => cdomain/value}/cdomains/intDomain.ml (100%) rename src/{ => cdomain/value}/cdomains/intDomain.mli (100%) rename src/{ => cdomain/value}/cdomains/jmpBufDomain.ml (100%) rename src/{ => cdomain/value}/cdomains/lval.ml (100%) rename src/{ => cdomain/value}/cdomains/mutexAttrDomain.ml (100%) rename src/{ => cdomain/value}/cdomains/mval.ml (100%) rename src/{ => cdomain/value}/cdomains/mval.mli (100%) rename src/{ => cdomain/value}/cdomains/mval_intf.ml (100%) rename src/{ => cdomain/value}/cdomains/nullByteSet.ml (100%) rename src/{ => cdomain/value}/cdomains/offset.ml (100%) rename src/{ => cdomain/value}/cdomains/offset.mli (100%) rename src/{ => cdomain/value}/cdomains/offset_intf.ml (100%) rename src/{ => cdomain/value}/cdomains/preValueDomain.ml (100%) rename src/{ => cdomain/value}/cdomains/stringDomain.ml (100%) rename src/{ => cdomain/value}/cdomains/stringDomain.mli (100%) rename src/{ => cdomain/value}/cdomains/structDomain.ml (100%) rename src/{ => cdomain/value}/cdomains/structDomain.mli (100%) rename src/{ => cdomain/value}/cdomains/threadIdDomain.ml (100%) rename src/{ => cdomain/value}/cdomains/unionDomain.ml (100%) rename src/{ => cdomain/value}/cdomains/valueDomain.ml (100%) rename src/{ => cdomain/value}/domains/invariant.ml (100%) rename src/{ => cdomain/value}/domains/invariantCil.ml (100%) rename src/{ => cdomain/value}/domains/valueDomainQueries.ml (100%) create mode 100644 src/cdomain/value/dune rename src/{ => cdomain/value}/util/precisionUtil.ml (100%) rename src/{ => cdomain/value}/util/wideningThresholds.ml (100%) rename src/{ => cdomain/value}/util/wideningThresholds.mli (100%) diff --git a/src/analyses/wrapperFunctionAnalysis0.ml b/src/cdomain/value/analyses/wrapperFunctionAnalysis0.ml similarity index 100% rename from src/analyses/wrapperFunctionAnalysis0.ml rename to src/cdomain/value/analyses/wrapperFunctionAnalysis0.ml diff --git a/src/cdomain/value/cdomain_value.mld b/src/cdomain/value/cdomain_value.mld new file mode 100644 index 0000000000..668bbfa0ca --- /dev/null +++ b/src/cdomain/value/cdomain_value.mld @@ -0,0 +1,71 @@ +{0 Library goblint.cdomain.value} +This library is unwrapped and provides the following top-level modules. +For better context, see {!Goblint_lib} which also documents these modules. + + +{1 Domains} + +{2 Analysis-specific} + +{3 Value} + +{4 Non-relational} + +{5 Numeric} +{!modules: +IntDomain +FloatDomain +} + +{5 Addresses} +{!modules: +Mval +Offset +StringDomain +AddressDomain +} + +{5 Complex} +{!modules: +StructDomain +UnionDomain +ArrayDomain +NullByteSet +JmpBufDomain +} + +{5 Combined} +{!modules: +ValueDomain +ValueDomainQueries +} + +{3 Concurrency} +{!modules: +MutexAttrDomain +ThreadIdDomain +ConcDomain +} + +{3 Other} +{!modules: +Lval +} + + +{1 I/O} + +{2 Witnesses} +{!modules: +Invariant +InvariantCil +} + + +{1 Utilities} + +{2 Analysis-specific} +{!modules: +PrecisionUtil +WideningThresholds +} diff --git a/src/cdomains/addressDomain.ml b/src/cdomain/value/cdomains/addressDomain.ml similarity index 100% rename from src/cdomains/addressDomain.ml rename to src/cdomain/value/cdomains/addressDomain.ml diff --git a/src/cdomains/addressDomain.mli b/src/cdomain/value/cdomains/addressDomain.mli similarity index 100% rename from src/cdomains/addressDomain.mli rename to src/cdomain/value/cdomains/addressDomain.mli diff --git a/src/cdomains/addressDomain_intf.ml b/src/cdomain/value/cdomains/addressDomain_intf.ml similarity index 100% rename from src/cdomains/addressDomain_intf.ml rename to src/cdomain/value/cdomains/addressDomain_intf.ml diff --git a/src/cdomains/arrayDomain.ml b/src/cdomain/value/cdomains/arrayDomain.ml similarity index 100% rename from src/cdomains/arrayDomain.ml rename to src/cdomain/value/cdomains/arrayDomain.ml diff --git a/src/cdomains/arrayDomain.mli b/src/cdomain/value/cdomains/arrayDomain.mli similarity index 100% rename from src/cdomains/arrayDomain.mli rename to src/cdomain/value/cdomains/arrayDomain.mli diff --git a/src/cdomains/concDomain.ml b/src/cdomain/value/cdomains/concDomain.ml similarity index 100% rename from src/cdomains/concDomain.ml rename to src/cdomain/value/cdomains/concDomain.ml diff --git a/src/cdomains/floatDomain.ml b/src/cdomain/value/cdomains/floatDomain.ml similarity index 100% rename from src/cdomains/floatDomain.ml rename to src/cdomain/value/cdomains/floatDomain.ml diff --git a/src/cdomains/floatDomain.mli b/src/cdomain/value/cdomains/floatDomain.mli similarity index 100% rename from src/cdomains/floatDomain.mli rename to src/cdomain/value/cdomains/floatDomain.mli diff --git a/src/cdomains/intDomain.ml b/src/cdomain/value/cdomains/intDomain.ml similarity index 100% rename from src/cdomains/intDomain.ml rename to src/cdomain/value/cdomains/intDomain.ml diff --git a/src/cdomains/intDomain.mli b/src/cdomain/value/cdomains/intDomain.mli similarity index 100% rename from src/cdomains/intDomain.mli rename to src/cdomain/value/cdomains/intDomain.mli diff --git a/src/cdomains/jmpBufDomain.ml b/src/cdomain/value/cdomains/jmpBufDomain.ml similarity index 100% rename from src/cdomains/jmpBufDomain.ml rename to src/cdomain/value/cdomains/jmpBufDomain.ml diff --git a/src/cdomains/lval.ml b/src/cdomain/value/cdomains/lval.ml similarity index 100% rename from src/cdomains/lval.ml rename to src/cdomain/value/cdomains/lval.ml diff --git a/src/cdomains/mutexAttrDomain.ml b/src/cdomain/value/cdomains/mutexAttrDomain.ml similarity index 100% rename from src/cdomains/mutexAttrDomain.ml rename to src/cdomain/value/cdomains/mutexAttrDomain.ml diff --git a/src/cdomains/mval.ml b/src/cdomain/value/cdomains/mval.ml similarity index 100% rename from src/cdomains/mval.ml rename to src/cdomain/value/cdomains/mval.ml diff --git a/src/cdomains/mval.mli b/src/cdomain/value/cdomains/mval.mli similarity index 100% rename from src/cdomains/mval.mli rename to src/cdomain/value/cdomains/mval.mli diff --git a/src/cdomains/mval_intf.ml b/src/cdomain/value/cdomains/mval_intf.ml similarity index 100% rename from src/cdomains/mval_intf.ml rename to src/cdomain/value/cdomains/mval_intf.ml diff --git a/src/cdomains/nullByteSet.ml b/src/cdomain/value/cdomains/nullByteSet.ml similarity index 100% rename from src/cdomains/nullByteSet.ml rename to src/cdomain/value/cdomains/nullByteSet.ml diff --git a/src/cdomains/offset.ml b/src/cdomain/value/cdomains/offset.ml similarity index 100% rename from src/cdomains/offset.ml rename to src/cdomain/value/cdomains/offset.ml diff --git a/src/cdomains/offset.mli b/src/cdomain/value/cdomains/offset.mli similarity index 100% rename from src/cdomains/offset.mli rename to src/cdomain/value/cdomains/offset.mli diff --git a/src/cdomains/offset_intf.ml b/src/cdomain/value/cdomains/offset_intf.ml similarity index 100% rename from src/cdomains/offset_intf.ml rename to src/cdomain/value/cdomains/offset_intf.ml diff --git a/src/cdomains/preValueDomain.ml b/src/cdomain/value/cdomains/preValueDomain.ml similarity index 100% rename from src/cdomains/preValueDomain.ml rename to src/cdomain/value/cdomains/preValueDomain.ml diff --git a/src/cdomains/stringDomain.ml b/src/cdomain/value/cdomains/stringDomain.ml similarity index 100% rename from src/cdomains/stringDomain.ml rename to src/cdomain/value/cdomains/stringDomain.ml diff --git a/src/cdomains/stringDomain.mli b/src/cdomain/value/cdomains/stringDomain.mli similarity index 100% rename from src/cdomains/stringDomain.mli rename to src/cdomain/value/cdomains/stringDomain.mli diff --git a/src/cdomains/structDomain.ml b/src/cdomain/value/cdomains/structDomain.ml similarity index 100% rename from src/cdomains/structDomain.ml rename to src/cdomain/value/cdomains/structDomain.ml diff --git a/src/cdomains/structDomain.mli b/src/cdomain/value/cdomains/structDomain.mli similarity index 100% rename from src/cdomains/structDomain.mli rename to src/cdomain/value/cdomains/structDomain.mli diff --git a/src/cdomains/threadIdDomain.ml b/src/cdomain/value/cdomains/threadIdDomain.ml similarity index 100% rename from src/cdomains/threadIdDomain.ml rename to src/cdomain/value/cdomains/threadIdDomain.ml diff --git a/src/cdomains/unionDomain.ml b/src/cdomain/value/cdomains/unionDomain.ml similarity index 100% rename from src/cdomains/unionDomain.ml rename to src/cdomain/value/cdomains/unionDomain.ml diff --git a/src/cdomains/valueDomain.ml b/src/cdomain/value/cdomains/valueDomain.ml similarity index 100% rename from src/cdomains/valueDomain.ml rename to src/cdomain/value/cdomains/valueDomain.ml diff --git a/src/domains/invariant.ml b/src/cdomain/value/domains/invariant.ml similarity index 100% rename from src/domains/invariant.ml rename to src/cdomain/value/domains/invariant.ml diff --git a/src/domains/invariantCil.ml b/src/cdomain/value/domains/invariantCil.ml similarity index 100% rename from src/domains/invariantCil.ml rename to src/cdomain/value/domains/invariantCil.ml diff --git a/src/domains/valueDomainQueries.ml b/src/cdomain/value/domains/valueDomainQueries.ml similarity index 100% rename from src/domains/valueDomainQueries.ml rename to src/cdomain/value/domains/valueDomainQueries.ml diff --git a/src/cdomain/value/dune b/src/cdomain/value/dune new file mode 100644 index 0000000000..c89d5be04d --- /dev/null +++ b/src/cdomain/value/dune @@ -0,0 +1,24 @@ +(include_subdirs unqualified) + +(library + (name goblint_cdomain_value) + (public_name goblint.cdomain.value) + (wrapped false) ; TODO: wrap + (libraries + batteries.unthreaded + goblint_std + goblint_common + goblint_config + goblint_library + goblint_domain + goblint_incremental + goblint-cil) + (flags :standard -open Goblint_std) + (preprocess + (pps + ppx_deriving.std + ppx_deriving_hash + ppx_deriving_yojson)) + (instrumentation (backend bisect_ppx))) + +(documentation) diff --git a/src/util/precisionUtil.ml b/src/cdomain/value/util/precisionUtil.ml similarity index 100% rename from src/util/precisionUtil.ml rename to src/cdomain/value/util/precisionUtil.ml diff --git a/src/util/wideningThresholds.ml b/src/cdomain/value/util/wideningThresholds.ml similarity index 100% rename from src/util/wideningThresholds.ml rename to src/cdomain/value/util/wideningThresholds.ml diff --git a/src/util/wideningThresholds.mli b/src/cdomain/value/util/wideningThresholds.mli similarity index 100% rename from src/util/wideningThresholds.mli rename to src/cdomain/value/util/wideningThresholds.mli diff --git a/src/dune b/src/dune index d65acfc856..d7c6d28026 100644 --- a/src/dune +++ b/src/dune @@ -7,7 +7,7 @@ (name goblint_lib) (public_name goblint.lib) (modules :standard \ goblint privPrecCompare apronPrecCompare messagesCompare) - (libraries goblint.sites goblint.build-info goblint-cil.all-features batteries.unthreaded qcheck-core.runner sha json-data-encoding jsonrpc cpu arg-complete fpath yaml yaml.unix uuidm goblint_timing catapult goblint_backtrace fileutils goblint_std goblint_config goblint_common goblint_domain goblint_constraint goblint_solver goblint_library goblint_incremental goblint_tracing + (libraries goblint.sites goblint.build-info goblint-cil.all-features batteries.unthreaded qcheck-core.runner sha json-data-encoding jsonrpc cpu arg-complete fpath yaml yaml.unix uuidm goblint_timing catapult goblint_backtrace fileutils goblint_std goblint_config goblint_common goblint_domain goblint_constraint goblint_solver goblint_library goblint_cdomain_value goblint_incremental goblint_tracing ; Conditionally compile based on whether apron optional dependency is installed or not. ; Alternative dependencies seem like the only way to optionally depend on optional dependencies. ; See: https://dune.readthedocs.io/en/stable/concepts.html#alternative-dependencies. diff --git a/src/index.mld b/src/index.mld index 0763284c15..0f6b1c3e69 100644 --- a/src/index.mld +++ b/src/index.mld @@ -16,6 +16,9 @@ This {{!page-common}unwrapped library} contains various common modules extracted {2 Library goblint.domain} This {{!page-domain}unwrapped library} contains various domain modules extracted from {!Goblint_lib}. +{2 Library goblint.cdomain.value} +This {{!page-cdomain_value}unwrapped library} contains various value domain modules extracted from {!Goblint_lib}. + {2 Library goblint.constraint} This {{!page-constraint}unwrapped library} contains various constraint system modules extracted from {!Goblint_lib}. diff --git a/unittest/dune b/unittest/dune index cb8dd668be..036c8d8013 100644 --- a/unittest/dune +++ b/unittest/dune @@ -2,7 +2,7 @@ (test (name mainTest) - (libraries ounit2 qcheck-ounit goblint.std goblint.lib goblint.constraint goblint.solver goblint.sites.dune goblint.build-info.dune) + (libraries ounit2 qcheck-ounit goblint.std goblint.lib goblint.constraint goblint.solver goblint.cdomain.value goblint.sites.dune goblint.build-info.dune) (preprocess (pps ppx_deriving.std ppx_deriving_hash ppx_deriving_yojson)) (flags :standard -linkall)) From 3422110111c6621a85caac18db9d4412a5a01cd0 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 9 Jan 2024 18:38:02 +0100 Subject: [PATCH 1309/1312] Rm outdated comment --- src/cdomains/apron/affineEqualityDomain.apron.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index 6f6f7c1280..ab24515c28 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -602,7 +602,7 @@ struct | Some v -> let ik = Cilfacade.get_ikind v.vtype in if not (Cil.isSigned ik) then - raise NotRefinable (* TODO: unsigned w/o bounds handled differently? *) + raise NotRefinable else match Bounds.bound_texpr res (Convert.texpr1_of_cil_exp res res.env (Lval (Cil.var v)) true) with | Some min, Some max -> From 9452d0881aad4d95e8c6b5eda63d3053c34a91af Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 10 Jan 2024 11:45:57 +0200 Subject: [PATCH 1310/1312] Add unsound minimal conf with no analyses --- conf/min-unsound.json | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 conf/min-unsound.json diff --git a/conf/min-unsound.json b/conf/min-unsound.json new file mode 100644 index 0000000000..5195909ffb --- /dev/null +++ b/conf/min-unsound.json @@ -0,0 +1,6 @@ +{ + "ana": { + "activated": [ + ] + } +} \ No newline at end of file From 3b0e0c598e47f9e011d1b9a56904708e78ac079a Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Wed, 10 Jan 2024 11:48:50 +0200 Subject: [PATCH 1311/1312] Add Karoliine as a maintainer to relevant files #1315 --- dune-project | 2 +- goblint.opam | 1 + goblint.opam.locked | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/dune-project b/dune-project index 37e81f4199..de6e955e60 100644 --- a/dune-project +++ b/dune-project @@ -16,7 +16,7 @@ (homepage "https://goblint.in.tum.de") (documentation "https://goblint.readthedocs.io/en/latest/") (authors "Simmo Saan" "Michael Schwarz" "Julian Erhard" "Sarah Tilscher" "Ralf Vogler" "Kalmer Apinis" "Vesal Vojdani" ) ; same authors as in .zenodo.json and CITATION.cff -(maintainers "Simmo Saan " "Michael Schwarz ") +(maintainers "Simmo Saan " "Michael Schwarz " "Karoliine Holter") (license MIT) (package diff --git a/goblint.opam b/goblint.opam index b5f1f360dc..7a75a1fb45 100644 --- a/goblint.opam +++ b/goblint.opam @@ -4,6 +4,7 @@ synopsis: "Static analysis framework for C" maintainer: [ "Simmo Saan " "Michael Schwarz " + "Karoliine Holter" ] authors: [ "Simmo Saan" diff --git a/goblint.opam.locked b/goblint.opam.locked index 02eac0bb75..b0a1c9ef20 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -5,6 +5,7 @@ synopsis: "Static analysis framework for C" maintainer: [ "Simmo Saan " "Michael Schwarz " + "Karoliine Holter" ] authors: [ "Simmo Saan" From 910b152226b7f28d20eabdda2c5578c526ceba49 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 10 Jan 2024 13:45:40 +0200 Subject: [PATCH 1312/1312] Update extension in debugging documentation --- docs/developer-guide/debugging.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/developer-guide/debugging.md b/docs/developer-guide/debugging.md index 5c1db12227..d218e1a8b8 100644 --- a/docs/developer-guide/debugging.md +++ b/docs/developer-guide/debugging.md @@ -60,14 +60,14 @@ This will create a file called `goblint.byte`. ### Debugging Goblint with VS Code To debug OCaml programs, you can use the command line interface of `ocamldebug` or make use of the Visual Studio Code -integration provided by `hackwaly.ocamlearlybird`. +integration provided by `ocamllabs.ocaml-platform`. In the following, we describe the steps necessary to set up this VS Code extension to debug Goblint. ### Setting-up Earlybird -Install the [`hackwaly.ocamlearlybird` extension](https://marketplace.visualstudio.com/items?itemName=hackwaly.ocamlearlybird) in your installation of Visual Studio Code. -To be able to use this extension, you additionally need to install `ocamlearlybird` on the opam switch you use for Goblint. +Install the [`ocamllabs.ocaml-platform` extension](https://marketplace.visualstudio.com/items?itemName=ocamllabs.ocaml-platform) in your installation of Visual Studio Code. +To be able to use this extension, you additionally need to install `earlybird` on the opam switch you use for Goblint. To do so, run the following command in the `analyzer` directory: ```console @@ -76,7 +76,7 @@ opam install earlybird ### Providing a Launch Configuration -To let the `hackwaly.ocamlearlybird` extension know which executable it should debug, and which arguments it should pass, we have to provide a configuration file. +To let the `ocamllabs.ocaml-platform` extension know which executable it should debug, and which arguments it should pass, we have to provide a configuration file. The configuration file has to be named `launch.json` and must reside in the `./.vscode` directory. Here is an example `launch.json`: ```JSON @@ -92,6 +92,9 @@ The configuration file has to be named `launch.json` and must reside in the `./. "tests/regression/00-sanity/01-assert.c", "--enable", "ana.int.interval", ], + "env": { + "LD_LIBRARY_PATH": "$LD_LIBRARY_PATH:_build/default/src/common" + }, "stopOnEntry": false, } ]