From 7bb5697b51950921c51e638aaebefe4b80ce3a4c Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 27 Jun 2023 16:23:52 +0200 Subject: [PATCH 001/178] 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 002/178] 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 f3507455563fa0b9e23f25fe7dfae605d53c04ca Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 1 Jul 2023 18:33:43 +0200 Subject: [PATCH 003/178] 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 4d5e0dcabe77afa11102f07c50971b12f18b1037 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 8 Jul 2023 16:26:07 +0200 Subject: [PATCH 004/178] 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 005/178] 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 006/178] 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 007/178] 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 008/178] 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 009/178] 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 4c093726b52ba1e3e0966ec5cded850d36bc87b6 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 26 Jul 2023 16:46:50 +0200 Subject: [PATCH 010/178] 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 a57112b0316c9000c35ee639198a299e5059fb7c Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 26 Jul 2023 16:54:03 +0200 Subject: [PATCH 011/178] 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 012/178] 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 013/178] 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 cfef9bd7d3f08fb578bc44608bd6af549785d48e Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 30 Jul 2023 14:13:32 +0200 Subject: [PATCH 014/178] 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 015/178] 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 016/178] 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 017/178] 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 b02d0a743f51fd0afd79a6ba86eb46556bfc1e1f Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 2 Aug 2023 08:53:23 +0200 Subject: [PATCH 018/178] 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 019/178] 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 c90fb0f11a3070c9c92534232a06008b0c2beb32 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 2 Aug 2023 14:27:40 +0200 Subject: [PATCH 020/178] 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 021/178] 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 503fb2416b4669aedbf03f531e0eb405e85c2cd3 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 12 Aug 2023 08:11:06 +0200 Subject: [PATCH 022/178] 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 023/178] 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 024/178] 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 434a4c1202382e0a0965a0792948423181bf3813 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 20 Aug 2023 18:39:19 +0200 Subject: [PATCH 025/178] 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 026/178] 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 027/178] 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 028/178] 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 029/178] 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 6ed476e3216d808fb6e6a16a6b15ca19c93efbfd Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Mon, 21 Aug 2023 18:17:46 +0200 Subject: [PATCH 030/178] 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 4e7b21b7d4c411eaf3576438ac599df91a1a2af9 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 24 Aug 2023 12:20:21 +0200 Subject: [PATCH 031/178] 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 291d60cd20d2a5ed29c3d76cd1e4cc05b07a562f Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 25 Aug 2023 18:40:20 +0200 Subject: [PATCH 032/178] 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 033/178] 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 034/178] 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 035/178] 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 036/178] 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 037/178] 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 038/178] 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 039/178] 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 040/178] 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 041/178] 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 cd95e8d45dbcaf68adacd3508cc5938eec4d500c Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Mon, 28 Aug 2023 22:17:59 +0200 Subject: [PATCH 042/178] 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 043/178] 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 044/178] 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 045/178] 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 046/178] 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 047/178] 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 048/178] 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 049/178] 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 050/178] 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 051/178] 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 052/178] 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 053/178] 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 054/178] 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 055/178] 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 056/178] 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 057/178] 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 058/178] 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 059/178] 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 060/178] 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 061/178] 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 aceffa897f7294261db65e2531661c64239a0376 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 31 Aug 2023 16:48:35 +0200 Subject: [PATCH 062/178] 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 063/178] 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 064/178] 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 065/178] 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 066/178] 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 2b4549905c80bbca10d82e04a7d2f21b9978143e Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 6 Sep 2023 12:01:45 +0200 Subject: [PATCH 067/178] 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 068/178] 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 069/178] 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 cb08d337d202fe2ac6154390f8973f9195c5a7ab Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 15 Sep 2023 11:03:07 +0300 Subject: [PATCH 070/178] 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 a32be380cd024523408b4e1348eab0d65da2842c Mon Sep 17 00:00:00 2001 From: karoliineh Date: Mon, 18 Sep 2023 15:06:56 +0300 Subject: [PATCH 071/178] 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 072/178] 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 073/178] 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 074/178] 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 075/178] 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 076/178] 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 077/178] 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 078/178] 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 c5136d1cdf945b9990376476bbd0881ee2f817ad Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 19 Sep 2023 14:28:13 +0300 Subject: [PATCH 079/178] 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 080/178] 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 081/178] 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 082/178] 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 083/178] 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 f7851e89bdc9438007ecd4cd4405d9584559ea6d Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 20 Sep 2023 10:05:46 +0300 Subject: [PATCH 084/178] 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 085/178] 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 086/178] 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 087/178] 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 088/178] 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 089/178] 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 090/178] 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 091/178] 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 092/178] 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 093/178] 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 094/178] 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 70f267b1bb50c66a0e0332982506de963c6f619e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 25 Sep 2023 10:36:12 +0300 Subject: [PATCH 095/178] 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 096/178] 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 097/178] 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 098/178] 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 23a5d83ab8a1af6431a9c00934139ff056758642 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 25 Sep 2023 16:30:25 +0300 Subject: [PATCH 099/178] 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 100/178] 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 101/178] 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 102/178] 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 103/178] 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 104/178] 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 105/178] 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 106/178] 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 107/178] 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 684a74cd39ab8fd4e1a8514beec44cd879a88fe9 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 26 Sep 2023 15:20:34 +0300 Subject: [PATCH 108/178] 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 109/178] 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 110/178] 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 111/178] 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 112/178] 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 113/178] 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 114/178] 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 115/178] 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 116/178] 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 117/178] 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 e780a83125a973e7b4df4d17e372a5a692e4bd37 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 28 Sep 2023 15:41:25 +0300 Subject: [PATCH 118/178] 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 119/178] 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 120/178] 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 121/178] 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 122/178] 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 123/178] 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 124/178] 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 125/178] 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 126/178] 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 127/178] 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 128/178] 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 129/178] 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 130/178] 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 131/178] 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 132/178] 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 133/178] 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 134/178] 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 135/178] 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 136/178] 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 137/178] 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 138/178] 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 139/178] 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 140/178] 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 141/178] 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 142/178] 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 143/178] 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 144/178] 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 145/178] 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 146/178] 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 147/178] 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 148/178] 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 149/178] 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 150/178] 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 151/178] 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 152/178] 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 153/178] 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 154/178] 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 155/178] 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 156/178] 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 157/178] 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 158/178] 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 159/178] 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 160/178] 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 161/178] 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 162/178] 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 163/178] 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 164/178] 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 15e6c3d636bde630a5df20d31af2c812355b68a2 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 2 Oct 2023 10:37:21 +0300 Subject: [PATCH 165/178] 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 7857a683163de4cc6e1df31f4b25dd80a99b2189 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 2 Oct 2023 16:34:03 +0300 Subject: [PATCH 166/178] 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 167/178] 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 168/178] 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 738ebfad83dbd3645b1a4dc76165391284c9267b Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 3 Oct 2023 14:08:14 +0200 Subject: [PATCH 169/178] 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 170/178] 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 171/178] 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 172/178] `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 173/178] 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 a9f2bae279a99bedd1a83a10e87f0296d1e24068 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 4 Oct 2023 11:17:43 +0200 Subject: [PATCH 174/178] 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 058990eb5a9413b08997fbc2d6ebdf52203290e3 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 4 Oct 2023 12:38:27 +0200 Subject: [PATCH 175/178] 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 093eb5ef686078baf906cc0d932a6a0c64c24053 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 4 Oct 2023 15:52:00 +0300 Subject: [PATCH 176/178] 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 8468a5a676fae48a82e0284ea13170d0cefa935c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 9 Oct 2023 12:12:58 +0300 Subject: [PATCH 177/178] 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 5aa420441c248a582b4484df170666b75fee5377 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 9 Oct 2023 16:20:50 +0200 Subject: [PATCH 178/178] 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*)