Skip to content

Commit

Permalink
Merge branch 'master' into svcomp24-conf
Browse files Browse the repository at this point in the history
  • Loading branch information
sim642 committed Nov 1, 2023
2 parents 35f9323 + 6131273 commit ebc8cf8
Show file tree
Hide file tree
Showing 200 changed files with 1,508 additions and 460 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/locked.yml
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ jobs:
ocaml-compiler: ${{ matrix.ocaml-compiler }}

- name: Set up Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/metadata.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
uses: actions/checkout@v4

- name: Set up Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}

Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/options.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,18 @@ jobs:
uses: actions/checkout@v4

- name: Set up Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}

- name: Install ajv-cli
run: npm install -g ajv-cli

- name: Migrate schema # https://github.com/ajv-validator/ajv-cli/issues/199
run: ajv migrate -s src/util/options.schema.json
run: ajv migrate -s src/common/util/options.schema.json

- name: Validate conf
run: ajv validate -s src/util/options.schema.json -d "conf/**/*.json"
run: ajv validate -s src/common/util/options.schema.json -d "conf/**/*.json"

- name: Validate incremental tests
run: ajv validate -s src/util/options.schema.json -d "tests/incremental/*/*.json"
run: ajv validate -s src/common/util/options.schema.json -d "tests/incremental/*/*.json"
2 changes: 1 addition & 1 deletion .readthedocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ build:
- pip install json-schema-for-humans
post_build:
- mkdir _readthedocs/html/jsfh/
- generate-schema-doc --config-file jsfh.yml src/util/options.schema.json _readthedocs/html/jsfh/
- generate-schema-doc --config-file jsfh.yml src/common/util/options.schema.json _readthedocs/html/jsfh/
3 changes: 2 additions & 1 deletion conf/svcomp.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@
"congruence",
"octagon",
"wideningThresholds",
"loopUnrollHeuristic"
"loopUnrollHeuristic",
"memsafetySpecification"
]
}
},
Expand Down
2 changes: 1 addition & 1 deletion docs/user-guide/configuring.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ In `.vscode/settings.json` add the following:
"/conf/*.json",
"/tests/incremental/*/*.json"
],
"url": "/src/util/options.schema.json"
"url": "/src/common/util/options.schema.json"
}
]
}
Expand Down
2 changes: 1 addition & 1 deletion goblint.opam
Original file line number Diff line number Diff line change
Expand Up @@ -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" ]
]
Expand Down
2 changes: 1 addition & 1 deletion goblint.opam.locked
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion goblint.opam.template
Original file line number Diff line number Diff line change
Expand Up @@ -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" ]
]
Expand Down
2 changes: 1 addition & 1 deletion gobview
2 changes: 1 addition & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ nav:
- 👶 Your first analysis: developer-guide/firstanalysis.md
- 🏫 Extending library: developer-guide/extending-library.md
- 📢 Messaging: developer-guide/messaging.md
- 🗃️ API reference: https://goblint.github.io/analyzer/
- 🗃️ API reference: https://goblint.github.io/analyzer/goblint/
- 🚨 Testing: developer-guide/testing.md
- 🪲 Debugging: developer-guide/debugging.md
- 📉 Profiling: developer-guide/profiling.md
Expand Down
26 changes: 18 additions & 8 deletions scripts/goblint-lib-modules.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,20 @@

src_root_path = Path("./src")

goblint_lib_path = src_root_path / "goblint_lib.ml"
goblint_lib_paths = [
src_root_path / "goblint_lib.ml",
src_root_path / "util" / "std" / "goblint_std.ml",
]
goblint_lib_modules = set()

with goblint_lib_path.open() as goblint_lib_file:
for line in goblint_lib_file:
line = line.strip()
m = re.match(r"module (.*) = .*", line)
if m is not None:
module_name = m.group(1)
goblint_lib_modules.add(module_name)
for goblint_lib_path in goblint_lib_paths:
with goblint_lib_path.open() as goblint_lib_file:
for line in goblint_lib_file:
line = line.strip()
m = re.match(r"module (.*) = .*", line)
if m is not None:
module_name = m.group(1)
goblint_lib_modules.add(module_name)

src_vendor_path = src_root_path / "vendor"
exclude_module_names = set([
Expand All @@ -29,15 +33,21 @@
"Mainspec",

# libraries
"Goblint_std",
"Goblint_timing",
"Goblint_backtrace",
"Goblint_sites",
"Goblint_build_info",
"Dune_build_info",

"MessageCategory", # included in Messages
"PreValueDomain", # included in ValueDomain
"SpecCore", # spec stuff
"SpecUtil", # spec stuff

"ConfigVersion",
"ConfigProfile",
"ConfigOcaml",
])

src_modules = set()
Expand Down
133 changes: 118 additions & 15 deletions src/analyses/base.ml
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,8 @@ struct
Int (if AD.is_bot (AD.meet p1 p2) then ID.of_int ik BI.one else match eq p1 p2 with Some x when x -> ID.of_int ik BI.zero | _ -> bool_top ik)
| IndexPI when AD.to_string p2 = ["all_index"] ->
addToAddrOp p1 (ID.top_of (Cilfacade.ptrdiff_ikind ()))
| IndexPI | PlusPI ->
addToAddrOp p1 (AD.to_int p2) (* sometimes index is AD for some reason... *)
| _ -> VD.top ()
end
(* For other values, we just give up! *)
Expand Down Expand Up @@ -761,7 +763,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) -> Float (FD.of_const fkind num)
| 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* *)
Expand Down Expand Up @@ -1042,10 +1045,24 @@ struct
| Mem n, ofs -> begin
match (eval_rv a gs st n) with
| Address adr ->
(if AD.is_null adr
then M.error ~category:M.Category.Behavior.Undefined.nullpointer_dereference ~tags:[CWE 476] "Must dereference NULL pointer"
else if AD.may_be_null adr
then M.warn ~category:M.Category.Behavior.Undefined.nullpointer_dereference ~tags:[CWE 476] "May dereference NULL pointer");
(
if AD.is_null adr then (
AnalysisStateUtil.set_mem_safety_flag InvalidDeref;
M.error ~category:M.Category.Behavior.Undefined.nullpointer_dereference ~tags:[CWE 476] "Must dereference NULL pointer"
)
else if AD.may_be_null adr then (
AnalysisStateUtil.set_mem_safety_flag InvalidDeref;
M.warn ~category:M.Category.Behavior.Undefined.nullpointer_dereference ~tags:[CWE 476] "May dereference NULL pointer"
);
(* Warn if any of the addresses contains a non-local and non-global variable *)
if AD.exists (function
| AD.Addr.Addr (v, _) -> not (CPA.mem v st.cpa) && not (is_global a v)
| _ -> false
) adr then (
AnalysisStateUtil.set_mem_safety_flag InvalidDeref;
M.warn "lval %a points to a non-local variable. Invalid pointer dereference may occur" d_lval lval
)
);
AD.map (add_offset_varinfo (convert_offset a gs st ofs)) adr
| _ ->
M.debug ~category:Analyzer "Failed evaluating %a to lvalue" d_lval lval;
Expand Down Expand Up @@ -1216,9 +1233,16 @@ struct
if copied then
M.warn ~category:(Behavior (Undefined Other)) "The jump buffer %a contains values that were copied here instead of being set by setjmp. This is Undefined Behavior." d_exp e;
x
| y -> failwith (GobPretty.sprintf "problem?! is %a %a:\n state is %a" CilType.Exp.pretty e VD.pretty y D.pretty ctx.local)
| Top
| Bot ->
JmpBufDomain.JmpBufSet.top ()
| y ->
M.debug ~category:Imprecise "EvalJmpBuf %a is %a, not JmpBuf." CilType.Exp.pretty e VD.pretty y;
JmpBufDomain.JmpBufSet.top ()
end
| _ -> failwith "problem?!"
| _ ->
M.debug ~category:Imprecise "EvalJmpBuf is not Address";
JmpBufDomain.JmpBufSet.top ()
end
| Q.EvalInt e ->
query_evalint (Analyses.ask_of_ctx ctx) ctx.global ctx.local e
Expand Down Expand Up @@ -2022,14 +2046,78 @@ struct
in
match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local ptr with
| Address a ->
if AD.is_top a then
if AD.is_top a then (
AnalysisStateUtil.set_mem_safety_flag InvalidFree;
M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Points-to set for pointer %a in function %s is top. Potentially invalid memory deallocation may occur" d_exp ptr special_fn.vname
else if has_non_heap_var a then
) else if has_non_heap_var a then (
AnalysisStateUtil.set_mem_safety_flag InvalidFree;
M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Free of non-dynamically allocated memory in function %s for pointer %a" special_fn.vname d_exp ptr
else if has_non_zero_offset a then
) else if has_non_zero_offset a then (
AnalysisStateUtil.set_mem_safety_flag InvalidFree;
M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 761] "Free of memory not at start of buffer in function %s for pointer %a" special_fn.vname d_exp ptr
| _ -> M.warn ~category:MessageCategory.Analyzer "Pointer %a in function %s doesn't evaluate to a valid address." d_exp ptr special_fn.vname
)
| _ ->
AnalysisStateUtil.set_mem_safety_flag InvalidFree;
M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Pointer %a in function %s doesn't evaluate to a valid address. Invalid memory deallocation may occur" d_exp ptr special_fn.vname

let points_to_heap_only ctx ptr =
match ctx.ask (Queries.MayPointTo ptr) with
| a when not (Queries.AD.is_top a)->
Queries.AD.for_all (function
| Addr (v, _) -> ctx.ask (Queries.IsHeapVar v)
| _ -> false
) a
| _ -> false

let get_size_of_ptr_target ctx ptr =
let intdom_of_int x =
ID.of_int (Cilfacade.ptrdiff_ikind ()) (Z.of_int x)
in
let size_of_type_in_bytes typ =
let typ_size_in_bytes = (bitsSizeOf typ) / 8 in
intdom_of_int typ_size_in_bytes
in
if points_to_heap_only ctx ptr then
(* Ask for BlobSize from the base address (the second component being set to true) in order to avoid BlobSize giving us bot *)
ctx.ask (Queries.BlobSize {exp = ptr; base_address = true})
else
match ctx.ask (Queries.MayPointTo ptr) with
| a when not (Queries.AD.is_top a) ->
let pts_list = Queries.AD.elements a in
let pts_elems_to_sizes (addr: Queries.AD.elt) =
begin match addr with
| Addr (v, _) ->
begin match v.vtype with
| TArray (item_typ, _, _) ->
let item_typ_size_in_bytes = size_of_type_in_bytes item_typ in
begin match ctx.ask (Queries.EvalLength ptr) with
| `Lifted arr_len ->
let arr_len_casted = ID.cast_to (Cilfacade.ptrdiff_ikind ()) arr_len in
begin
try `Lifted (ID.mul item_typ_size_in_bytes arr_len_casted)
with IntDomain.ArithmeticOnIntegerBot _ -> `Bot
end
| `Bot -> `Bot
| `Top -> `Top
end
| _ ->
let type_size_in_bytes = size_of_type_in_bytes v.vtype in
`Lifted type_size_in_bytes
end
| _ -> `Top
end
in
(* Map each points-to-set element to its size *)
let pts_sizes = List.map pts_elems_to_sizes pts_list in
(* Take the smallest of all sizes that ptr's contents may have *)
begin match pts_sizes with
| [] -> `Bot
| [x] -> x
| x::xs -> List.fold_left ValueDomainQueries.ID.join x xs
end
| _ ->
(M.warn "Pointer %a has a points-to-set of top. An invalid memory access might occur" d_exp ptr;
`Top)

let special ctx (lv:lval option) (f: varinfo) (args: exp list) =
let invalidate_ret_lv st = match lv with
Expand All @@ -2049,13 +2137,28 @@ struct
let st: store = ctx.local in
let gs = ctx.global in
let desc = LF.find f in
let memory_copying dst src =
let memory_copying dst src n =
let dest_size = get_size_of_ptr_target ctx dst in
let n_intdom = Option.map_default (fun exp -> ctx.ask (Queries.EvalInt exp)) `Bot n in
let dest_size_equal_n =
match dest_size, n_intdom with
| `Lifted ds, `Lifted n ->
let casted_ds = ID.cast_to (Cilfacade.ptrdiff_ikind ()) ds in
let casted_n = ID.cast_to (Cilfacade.ptrdiff_ikind ()) n in
let ds_eq_n =
begin try ID.eq casted_ds casted_n
with IntDomain.ArithmeticOnIntegerBot _ -> ID.top_of @@ Cilfacade.ptrdiff_ikind ()
end
in
Option.default false (ID.to_bool ds_eq_n)
| _ -> false
in
let dest_a, dest_typ = addr_type_of_exp dst in
let src_lval = mkMem ~addr:(Cil.stripCasts src) ~off:NoOffset in
let src_typ = eval_lv (Analyses.ask_of_ctx ctx) gs st src_lval
|> AD.type_of in
(* when src and destination type coincide, take value from the source, otherwise use top *)
let value = if typeSig dest_typ = typeSig src_typ then
let value = if (typeSig dest_typ = typeSig src_typ) && dest_size_equal_n then
let src_cast_lval = mkMem ~addr:(Cilfacade.mkCast ~e:src ~newt:(TPtr (dest_typ, []))) ~off:NoOffset in
eval_rv (Analyses.ask_of_ctx ctx) gs st (Lval src_cast_lval)
else
Expand Down Expand Up @@ -2116,13 +2219,13 @@ struct
let value = VD.zero_init_value dest_typ in
set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value
| Memcpy { dest = dst; src; n; }, _ -> (* TODO: use n *)
memory_copying dst src
memory_copying dst src (Some n)
(* strcpy(dest, src); *)
| Strcpy { dest = dst; src; n = None }, _ ->
let dest_a, dest_typ = addr_type_of_exp dst in
(* when dest surely isn't a string literal, try copying src to dest *)
if AD.string_writing_defined dest_a then
memory_copying dst src
memory_copying dst src None
else
(* else return top (after a warning was issued) *)
set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ (VD.top_value (unrollType dest_typ))
Expand Down
6 changes: 3 additions & 3 deletions src/analyses/baseInvariant.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion src/analyses/commonPriv.ml
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ struct
module V =
struct
(* TODO: Either3? *)
include Printable.Either (Printable.Either (VMutex) (VMutexInits)) (VGlobal)
include Printable.Either (struct include Printable.Either (VMutex) (VMutexInits) let name () = "mutex" end) (VGlobal)
let name () = "MutexGlobals"
let mutex x: t = `Left (`Left x)
let mutex_inits: t = `Left (`Right ())
Expand Down
5 changes: 5 additions & 0 deletions src/analyses/libraryDesc.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand Down
Loading

0 comments on commit ebc8cf8

Please sign in to comment.