Skip to content

Commit

Permalink
Merge branch 'witness-invariant-int' into yaml-witness-ghost-eval
Browse files Browse the repository at this point in the history
  • Loading branch information
sim642 committed Jun 19, 2024
2 parents 7fcb10c + 02ef2f9 commit d3594c9
Show file tree
Hide file tree
Showing 49 changed files with 1,262 additions and 449 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ jobs:
- name: Build Docker image
id: build
uses: docker/build-push-action@v5
uses: docker/build-push-action@v6
with:
context: .
load: true # load into docker instead of immediately pushing
Expand All @@ -72,7 +72,7 @@ jobs:
run: docker run --rm -v $(pwd):/data ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }} /data/tests/regression/04-mutex/01-simple_rc.c # run image by version in case multiple tags

- name: Push Docker image
uses: docker/build-push-action@v5
uses: docker/build-push-action@v6
with:
context: .
push: true
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/unlocked.yml
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ jobs:

- name: Build dev Docker image
id: build
uses: docker/build-push-action@v5
uses: docker/build-push-action@v6
with:
context: .
target: dev
Expand Down
6 changes: 3 additions & 3 deletions docs/developer-guide/firstanalysis.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ This program is in the Goblint repository: `tests/regression/99-tutorials/01-fir
But if you run Goblint out of the box on this example, it will not work:

```console
./goblint --enable warn.debug tests/regression/99-tutorials/01-first.c
./goblint tests/regression/99-tutorials/01-first.c
```

This will claim that the assertion in unknown.
Expand Down Expand Up @@ -74,10 +74,10 @@ For more information on the signature of the individual transfer functions, plea
## Extending the domain

You could now enrich the lattice to also have a representation for non-negative (i.e., zero or positive) values.
Then the join of `Zero` and `Pos` would be "non-negative" instead of `Top`, allowing you to prove that such join is greated than `Neg`.
Then the join of `Zero` and `Pos` would be "non-negative" instead of `Top`, allowing you to prove that such join is greater than `Neg`.
For example, have a look at the following program: `tests/regression/99-tutorials/02-first-extend.c`.

_Hint:_
The easiest way to do this is to use the powerset lattice of `{-, 0, +}`.
For example, "non-negative" is represented by `{0, +}`, while negative is represented by `{-}`.
To do this, modify `SL` by using `SetDomain.FiniteSet` (takes a `struct` with a list of finite elements as second parameter) instead of `Lattice.Flat` and reimplementing the two functions using `singleton` and `for_all`.
To do this, modify `SL` by using `SetDomain.FiniteSet` (which needs a finite list of elements to be added to `Signs`) instead of `Lattice.Flat` and reimplementing the two functions using `singleton` and `for_all`.
31 changes: 17 additions & 14 deletions src/analyses/apron/relationAnalysis.apron.ml
Original file line number Diff line number Diff line change
Expand Up @@ -388,11 +388,11 @@ struct
let st = ctx.local in
let reachable_from_args = reachable_from_args ctx args in
let fundec = Node.find_fundec ctx.node in
if M.tracing then M.tracel "combine" "relation f: %a" CilType.Varinfo.pretty f.svar;
if M.tracing then M.tracel "combine" "relation formals: %a" (d_list "," CilType.Varinfo.pretty) f.sformals;
if M.tracing then M.tracel "combine" "relation args: %a" (d_list "," d_exp) args;
if M.tracing then M.tracel "combine" "relation st: %a" D.pretty st;
if M.tracing then M.tracel "combine" "relation fun_st: %a" D.pretty fun_st;
if M.tracing then M.tracel "combine-rel" "relation f: %a" CilType.Varinfo.pretty f.svar;
if M.tracing then M.tracel "combine-rel" "relation formals: %a" (d_list "," CilType.Varinfo.pretty) f.sformals;
if M.tracing then M.tracel "combine-rel" "relation args: %a" (d_list "," d_exp) args;
if M.tracing then M.tracel "combine-rel" "relation st: %a" D.pretty st;
if M.tracing then M.tracel "combine-rel" "relation fun_st: %a" D.pretty fun_st;
let new_fun_rel = RD.add_vars fun_st.rel (RD.vars st.rel) in
let arg_substitutes =
let filter_actuals (x,e) =
Expand All @@ -418,7 +418,7 @@ struct
in
let any_local_reachable = any_local_reachable fundec reachable_from_args in
let arg_vars = f.sformals |> List.filter (RD.Tracked.varinfo_tracked) |> List.map RV.arg in
if M.tracing then M.tracel "combine" "relation remove vars: %a" (docList (fun v -> Pretty.text (Apron.Var.to_string v))) arg_vars;
if M.tracing then M.tracel "combine-rel" "relation remove vars: %a" (docList (fun v -> Pretty.text (Apron.Var.to_string v))) arg_vars;
RD.remove_vars_with new_fun_rel arg_vars; (* fine to remove arg vars that also exist in caller because unify from new_rel adds them back with proper constraints *)
let tainted = f_ask.f Queries.MayBeTainted in
let tainted_vars = TaintPartialContexts.conv_varset tainted in
Expand All @@ -432,7 +432,7 @@ struct
)
in
let unify_rel = RD.unify new_rel new_fun_rel in (* TODO: unify_with *)
if M.tracing then M.tracel "combine" "relation unifying %a %a = %a" RD.pretty new_rel RD.pretty new_fun_rel RD.pretty unify_rel;
if M.tracing then M.tracel "combine-rel" "relation unifying %a %a = %a" RD.pretty new_rel RD.pretty new_fun_rel RD.pretty unify_rel;
{fun_st with rel = unify_rel}

let combine_assign ctx r fe f args fc fun_st (f_ask : Queries.ask) =
Expand Down Expand Up @@ -694,15 +694,18 @@ struct
ctx.local

let event ctx e octx =
let ask = Analyses.ask_of_ctx ctx in
let st = ctx.local in
match e with
| Events.Lock (addr, _) when ThreadFlag.has_ever_been_multi (Analyses.ask_of_ctx ctx) -> (* TODO: is this condition sound? *)
Priv.lock (Analyses.ask_of_ctx ctx) ctx.global st addr
| Events.Unlock addr when ThreadFlag.has_ever_been_multi (Analyses.ask_of_ctx ctx) -> (* TODO: is this condition sound? *)
if addr = UnknownPtr then
M.info ~category:Unsound "Unknown mutex unlocked, relation privatization unsound"; (* TODO: something more sound *)
| Events.Lock (addr, _) when ThreadFlag.has_ever_been_multi ask -> (* TODO: is this condition sound? *)
CommonPriv.lift_lock ask (fun st m ->
Priv.lock ask ctx.global st m
) st addr
| Events.Unlock addr when ThreadFlag.has_ever_been_multi ask -> (* TODO: is this condition sound? *)
WideningTokens.with_local_side_tokens (fun () ->
Priv.unlock (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg st addr
CommonPriv.lift_unlock ask (fun st m ->
Priv.unlock ask ctx.global ctx.sideg st m
) st addr
)
| Events.EnterMultiThreaded ->
Priv.enter_multithreaded (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg st
Expand Down Expand Up @@ -781,7 +784,7 @@ struct
PCU.RH.replace results ctx.node new_value;
end;
WideningTokens.with_local_side_tokens (fun () ->
Priv.sync (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg ctx.local (reason :> [`Normal | `Join | `Return | `Init | `Thread])
Priv.sync (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg ctx.local (reason :> [`Normal | `Join | `JoinCall | `Return | `Init | `Thread])
)

let init marshal =
Expand Down
80 changes: 52 additions & 28 deletions src/analyses/apron/relationPriv.apron.ml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ module type S =
val lock: Q.ask -> (V.t -> G.t) -> relation_components_t -> LockDomain.Addr.t -> relation_components_t
val unlock: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> relation_components_t -> LockDomain.Addr.t -> relation_components_t

val sync: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> relation_components_t -> [`Normal | `Join | `Return | `Init | `Thread] -> relation_components_t
val sync: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> relation_components_t -> [`Normal | `Join | `JoinCall | `Return | `Init | `Thread] -> relation_components_t

val escape: Node.t -> Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> relation_components_t -> EscapeDomain.EscapedVars.t -> relation_components_t
val enter_multithreaded: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> relation_components_t -> relation_components_t
Expand Down Expand Up @@ -99,8 +99,7 @@ struct
{ st with rel = rel_local }

let sync (ask: Q.ask) getg sideg (st: relation_components_t) reason =
match reason with
| `Join when ConfCheck.branched_thread_creation () ->
let branched_sync () =
if ask.f (Q.MustBeSingleThreaded {since_start = true}) then
st
else
Expand All @@ -113,7 +112,14 @@ struct
)
in
{st with rel = rel_local}
in
match reason with
| `Join when ConfCheck.branched_thread_creation () ->
branched_sync ()
| `JoinCall when ConfCheck.branched_thread_creation () ->
branched_sync ()
| `Join
| `JoinCall
| `Normal
| `Init
| `Thread
Expand Down Expand Up @@ -341,17 +347,8 @@ struct
let thread_join ?(force=false) ask getg exp st = st
let thread_return ask getg sideg tid st = st

let sync ask getg sideg (st: relation_components_t) reason =
match reason with
| `Return -> (* required for thread return *)
(* TODO: implement? *)
begin match ThreadId.get_current ask with
| `Lifted x (* when CPA.mem x st.cpa *) ->
st
| _ ->
st
end
| `Join when ConfCheck.branched_thread_creation () ->
let sync (ask:Q.ask) getg sideg (st: relation_components_t) reason =
let branched_sync () =
if ask.f (Q.MustBeSingleThreaded { since_start= true }) then
st
else
Expand Down Expand Up @@ -380,7 +377,22 @@ struct
let rel_local' = RD.meet rel_local (getg ()) in
{st with rel = rel_local'} *)
st
in
match reason with
| `Return -> (* required for thread return *)
(* TODO: implement? *)
begin match ThreadId.get_current ask with
| `Lifted x (* when CPA.mem x st.cpa *) ->
st
| _ ->
st
end
| `Join when ConfCheck.branched_thread_creation () ->
branched_sync ()
| `JoinCall when ConfCheck.branched_thread_creation_at_call ask ->
branched_sync ()
| `Join
| `JoinCall
| `Normal
| `Init
| `Thread ->
Expand Down Expand Up @@ -631,17 +643,8 @@ struct
let thread_join ?(force=false) ask getg exp st = st
let thread_return ask getg sideg tid st = st

let sync ask getg sideg (st: relation_components_t) reason =
match reason with
| `Return -> (* required for thread return *)
(* TODO: implement? *)
begin match ThreadId.get_current ask with
| `Lifted x (* when CPA.mem x st.cpa *) ->
st
| _ ->
st
end
| `Join when ConfCheck.branched_thread_creation () ->
let sync (ask:Q.ask) getg sideg (st: relation_components_t) reason =
let branched_sync () =
if ask.f (Q.MustBeSingleThreaded {since_start = true}) then
st
else
Expand All @@ -664,7 +667,22 @@ struct
)
in
{st with rel = rel_local}
in
match reason with
| `Return -> (* required for thread return *)
(* TODO: implement? *)
begin match ThreadId.get_current ask with
| `Lifted x (* when CPA.mem x st.cpa *) ->
st
| _ ->
st
end
| `Join when ConfCheck.branched_thread_creation () ->
branched_sync ()
| `JoinCall when ConfCheck.branched_thread_creation_at_call ask ->
branched_sync ()
| `Join
| `JoinCall
| `Normal
| `Init
| `Thread ->
Expand Down Expand Up @@ -1232,9 +1250,7 @@ struct
st

let sync (ask:Q.ask) getg sideg (st: relation_components_t) reason =
match reason with
| `Return -> st (* TODO: implement? *)
| `Join when ConfCheck.branched_thread_creation () ->
let branched_sync () =
if ask.f (Q.MustBeSingleThreaded {since_start = true}) then
st
else
Expand All @@ -1249,7 +1265,15 @@ struct
)
in
{st with rel = rel_local}
in
match reason with
| `Return -> st (* TODO: implement? *)
| `Join when ConfCheck.branched_thread_creation () ->
branched_sync ()
| `JoinCall when ConfCheck.branched_thread_creation_at_call ask ->
branched_sync ()
| `Join
| `JoinCall
| `Normal
| `Init
| `Thread ->
Expand Down
23 changes: 13 additions & 10 deletions src/analyses/base.ml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ module IdxDom = ValueDomain.IndexDomain
module AD = ValueDomain.AD
module Addr = ValueDomain.Addr
module Offs = ValueDomain.Offs
module ZeroInit = ValueDomain.ZeroInit
module LF = LibraryFunctions
module CArrays = ValueDomain.CArrays
module PU = PrecisionUtil
Expand Down Expand Up @@ -450,7 +451,7 @@ struct
else
ctx.local

let sync ctx reason = sync' (reason :> [`Normal | `Join | `Return | `Init | `Thread]) ctx
let sync ctx reason = sync' (reason :> [`Normal | `Join | `JoinCall | `Return | `Init | `Thread]) ctx

let publish_all ctx reason =
ignore (sync' reason ctx)
Expand Down Expand Up @@ -2662,7 +2663,7 @@ struct
| 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 size); *)
set_many ~ctx st [(heap_var, TVoid [], Blob (VD.bot (), eval_int ~ctx st size, true));
set_many ~ctx st [(heap_var, TVoid [], Blob (VD.bot (), eval_int ~ctx st size, ZeroInit.malloc));
(eval_lv ~ctx st lv, (Cilfacade.typeOfLval lv), Address heap_var)]
| _ -> st
end
Expand All @@ -2675,7 +2676,7 @@ struct
else AD.of_var (heap_var false ctx)
in
(* ignore @@ printf "malloc will allocate %a bytes\n" ID.pretty (eval_int ~ctx size); *)
set_many ~ctx st [(heap_var, TVoid [], Blob (VD.bot (), eval_int ~ctx st size, true));
set_many ~ctx st [(heap_var, TVoid [], Blob (VD.bot (), eval_int ~ctx st size, ZeroInit.malloc));
(eval_lv ~ctx st lv, (Cilfacade.typeOfLval lv), Address heap_var)]
| _ -> st
end
Expand All @@ -2692,15 +2693,15 @@ struct
let countval = eval_int ~ctx st n in
if ID.to_int countval = Some Z.one then (
set_many ~ctx st [
(add_null (AD.of_var heap_var), TVoid [], Blob (VD.bot (), sizeval, false));
(add_null (AD.of_var heap_var), TVoid [], Blob (VD.bot (), sizeval, ZeroInit.calloc));
(eval_lv ~ctx st lv, (Cilfacade.typeOfLval lv), Address (add_null (AD.of_var heap_var)))
]
)
else (
let blobsize = ID.mul (ID.cast_to ik @@ sizeval) (ID.cast_to ik @@ countval) in
(* the memory that was allocated by calloc is set to bottom, but we keep track that it originated from calloc, so when bottom is read from memory allocated by calloc it is turned to zero *)
set_many ~ctx st [
(add_null (AD.of_var heap_var), TVoid [], Array (CArrays.make (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) Z.one) (Blob (VD.bot (), blobsize, false))));
(add_null (AD.of_var heap_var), TVoid [], Array (CArrays.make (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) Z.one) (Blob (VD.bot (), blobsize, ZeroInit.calloc))));
(eval_lv ~ctx st lv, (Cilfacade.typeOfLval lv), Address (add_null (AD.of_mval (heap_var, `Index (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) Z.zero, `NoOffset)))))
]
)
Expand All @@ -2723,7 +2724,7 @@ struct
let p_addr' = AD.remove NullPtr p_addr in (* realloc with NULL is same as malloc, remove to avoid unknown value from NullPtr access *)
let p_addr_get = get ~ctx st p_addr' None in (* implicitly includes join of malloc value (VD.bot) *)
let size_int = eval_int ~ctx st size in
let heap_val:value = Blob (p_addr_get, size_int, true) in (* copy old contents with new size *)
let heap_val:value = Blob (p_addr_get, size_int, ZeroInit.malloc) in (* copy old contents with new size *)
let heap_addr = AD.of_var (heap_var false ctx) in
let heap_addr' =
if get_bool "sem.malloc.fail" then
Expand Down Expand Up @@ -3072,12 +3073,14 @@ struct
match e with
| Events.Lock (addr, _) when ThreadFlag.has_ever_been_multi ask -> (* TODO: is this condition sound? *)
if M.tracing then M.tracel "priv" "LOCK EVENT %a" LockDomain.Addr.pretty addr;
Priv.lock ask (priv_getg ctx.global) st addr
CommonPriv.lift_lock ask (fun st m ->
Priv.lock ask (priv_getg ctx.global) st m
) st addr
| Events.Unlock addr when ThreadFlag.has_ever_been_multi ask -> (* TODO: is this condition sound? *)
if addr = UnknownPtr then
M.info ~category:Unsound "Unknown mutex unlocked, base privatization unsound"; (* TODO: something more sound *)
WideningTokens.with_local_side_tokens (fun () ->
Priv.unlock ask (priv_getg ctx.global) (priv_sideg ctx.sideg) st addr
CommonPriv.lift_unlock ask (fun st m ->
Priv.unlock ask (priv_getg ctx.global) (priv_sideg ctx.sideg) st m
) st addr
)
| Events.Escape escaped ->
Priv.escape ask (priv_getg ctx.global) (priv_sideg ctx.sideg) st escaped
Expand Down
Loading

0 comments on commit d3594c9

Please sign in to comment.