Skip to content

Commit

Permalink
Merge branch 'null-byte-arrayDomain' of github.com:nathanschmidt/anal…
Browse files Browse the repository at this point in the history
…yzer into null-byte-arrayDomain

dieser
  • Loading branch information
nathanschmidt committed Aug 10, 2023
2 parents cc82623 + 7dce66d commit fcd98fa
Show file tree
Hide file tree
Showing 95 changed files with 3,323 additions and 751 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ jobs:
run: opam exec -- dune build @doc

- name: Upload artifact
uses: actions/upload-pages-artifact@v1
uses: actions/upload-pages-artifact@v2
with:
path: _build/default/_doc/_html/

Expand Down
1 change: 0 additions & 1 deletion .github/workflows/locked.yml
Original file line number Diff line number Diff line change
Expand Up @@ -174,5 +174,4 @@ jobs:

- name: Test Gobview
run: |
./goblint --enable gobview tests/regression/00-sanity/01-assert.c
python3 scripts/test-gobview.py
2 changes: 1 addition & 1 deletion docs/user-guide/inspecting.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ To build GobView (also for development):
2. The executable for the http-server can then be found in the directory `./_build/default/gobview/goblint-http-server`. It takes the analyzer directory and additional Goblint configurations such as the files to be analyzed as parameters. Run it e.g. with the following command:\
`./_build/default/gobview/goblint-http-server/goblint_http.exe -with-goblint ../analyzer/goblint -goblint --set files[+] "../analyzer/tests/regression/00-sanity/01-assert.c"`

4. Visit <http://localhost:8000>
4. Visit <http://localhost:8080>
29 changes: 14 additions & 15 deletions scripts/test-gobview.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,30 @@
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
from threading import Thread
import http.server
import socketserver
import subprocess

PORT = 9000
PORT = 8080 # has to match port defined in goblint_http.ml
DIRECTORY = "run"
IP = "localhost"
url = 'http://' + IP + ':' + str(PORT) + '/'

# cleanup
def cleanup(browser, httpd, thread):
def cleanup(browser, thread):
print("cleanup")
browser.close()
httpd.shutdown()
httpd.server_close()
p.kill()
thread.join()

# serve GobView in different thread so it does not block the testing
class Handler(http.server.SimpleHTTPRequestHandler):
def __init__(self, *args, **kwargs):
super().__init__(*args, directory=DIRECTORY, **kwargs)
class Server(socketserver.TCPServer):
allow_reuse_address = True # avoids that during a consecutive run the server cannot connect due to an 'Adress already in use' os error
def serve():
global p
goblint_http_path = '_build/default/gobview/goblint-http-server/goblint_http.exe'
p = subprocess.Popen(['./' + goblint_http_path,
'-with-goblint', '../analyzer/goblint',
'-goblint', '--set', 'files[+]', '"../analyzer/tests/regression/00-sanity/01-assert.c"'])

httpd = Server((IP, PORT), Handler)
print("serving at port", PORT)
thread = Thread(target=httpd.serve_forever, args=())
thread = Thread(target=serve, args=())
thread.start()

# installation of browser
Expand All @@ -42,6 +40,7 @@ class Server(socketserver.TCPServer):
browser = webdriver.Chrome(service=Service(ChromeDriverManager().install()),options=options)
print("finished webdriver installation \n")
browser.maximize_window()
browser.implicitly_wait(10);

try:
# retrieve and wait until page is fully loaded and rendered
Expand All @@ -62,8 +61,8 @@ class Server(socketserver.TCPServer):
panel = browser.find_element(By.CLASS_NAME, "panel")
print("found DOM elements main, sidebar-left, sidebar-right, content and panel")

cleanup(browser, httpd, thread)
cleanup(browser, thread)

except Exception as e:
cleanup(browser, httpd, thread)
cleanup(browser, thread)
raise e
2 changes: 1 addition & 1 deletion src/analyses/accessAnalysis.ml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ struct
let access_one_top ?(force=false) ?(deref=false) ctx (kind: AccessKind.t) reach exp =
if M.tracing then M.traceli "access" "access_one_top %a (kind = %a, reach = %B, deref = %B)\n" CilType.Exp.pretty exp AccessKind.pretty kind reach deref;
if force || !collect_local || !emit_single_threaded || ThreadFlag.has_ever_been_multi (Analyses.ask_of_ctx ctx) then (
if deref then
if deref && Cil.isPointerType (Cilfacade.typeOf exp) then (* avoid dereferencing integers to unknown pointers, which cause many spurious type-based accesses *)
do_access ctx kind reach exp;
if M.tracing then M.tracei "access" "distribute_access_exp\n";
Access.distribute_access_exp (do_access ctx Read false) exp;
Expand Down
6 changes: 6 additions & 0 deletions src/analyses/apron/relationAnalysis.apron.ml
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,12 @@ struct
| Unknown, "__goblint_assume_join" ->
let id = List.hd args in
Priv.thread_join ~force:true ask ctx.global id st
| Rand, _ ->
(match r with
| Some lv ->
let st = invalidate_one ask ctx st lv in
assert_fn {ctx with local = st} (BinOp (Ge, Lval lv, zero, intType)) true
| None -> st)
| _, _ ->
let lvallist e =
let s = ask.f (Queries.MayPointTo e) in
Expand Down
6 changes: 1 addition & 5 deletions src/analyses/apron/relationPriv.apron.ml
Original file line number Diff line number Diff line change
Expand Up @@ -713,11 +713,7 @@ module DownwardClosedCluster (ClusteringArg: ClusteringArg) = functor (RD: Rela
struct
open CommonPerMutex(RD)

module VS =
struct
include Printable.Std
include SetDomain.Make (CilType.Varinfo)
end
module VS = SetDomain.Make (CilType.Varinfo)
module LRD = MapDomain.MapBot (VS) (RD)

let keep_only_protected_globals ask m octs =
Expand Down
57 changes: 48 additions & 9 deletions src/analyses/base.ml
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@ struct
let var = get_var a gs st x in
let v = VD.eval_offset (Queries.to_value_domain_ask a) (fun x -> get a gs st x exp) var offs exp (Some (Var x, Offs.to_cil_offset offs)) x.vtype in
if M.tracing then M.tracec "get" "var = %a, %a = %a\n" VD.pretty var AD.pretty (AD.of_mval (x, offs)) VD.pretty v;
if full then v else match v with
if full then var else match v with
| Blob (c,s,_) -> c
| x -> x
in
Expand Down Expand Up @@ -1261,11 +1261,22 @@ struct
(* ignore @@ printf "BlobSize %a MayPointTo %a\n" d_plainexp e VD.pretty p; *)
match p with
| Address a ->
let r = get ~full:true (Analyses.ask_of_ctx ctx) ctx.global ctx.local a None in
(* ignore @@ printf "BlobSize %a = %a\n" d_plainexp e VD.pretty r; *)
(match r with
| Blob (_,s,_) -> `Lifted s
| _ -> Queries.Result.top q)
let s = addrToLvalSet a in
let has_offset = function
| `NoOffset -> false
| `Field _
| `Index _ -> true
in
(* If there's a non-heap var or an offset in the lval set, we answer with bottom *)
if ValueDomainQueries.LS.exists (fun (v, o) -> (not @@ ctx.ask (Queries.IsHeapVar v)) || has_offset o) s then
Queries.Result.bot q
else (
let r = get ~full:true (Analyses.ask_of_ctx ctx) ctx.global ctx.local a None in
(* ignore @@ printf "BlobSize %a = %a\n" d_plainexp e VD.pretty r; *)
(match r with
| Blob (_,s,_) -> `Lifted s
| _ -> Queries.Result.top q)
)
| _ -> Queries.Result.top q
end
| Q.MayPointTo e -> begin
Expand Down Expand Up @@ -1336,7 +1347,8 @@ struct
(* ignore @@ printf "EvalStr Unknown: %a -> %s\n" d_plainexp e (VD.short 80 x); *)
Queries.Result.top q
end
| Q.IsMultiple v -> WeakUpdates.mem v ctx.local.weak
| Q.IsMultiple v -> WeakUpdates.mem v ctx.local.weak ||
(hasAttribute "thread" v.vattr && v.vaddrof) (* thread-local variables if they have their address taken, as one could then compare several such variables *)
| Q.IterSysVars (vq, vf) ->
let vf' x = vf (Obj.repr (V.priv x)) in
Priv.iter_sys_vars (priv_getg ctx.global) vq vf'
Expand Down Expand Up @@ -1410,9 +1422,13 @@ struct
let new_value = VD.update_offset (Queries.to_value_domain_ask a) old_value offs projected_value lval_raw ((Var x), cil_offset) t in
if WeakUpdates.mem x st.weak then
VD.join old_value new_value
else if invariant then
else if invariant then (
(* without this, invariant for ambiguous pointer might worsen precision for each individual address to their join *)
VD.meet old_value new_value
try
VD.meet old_value new_value
with Lattice.Uncomparable ->
new_value
)
else
new_value
in
Expand Down Expand Up @@ -1997,6 +2013,16 @@ struct
let st' = invalidate ~deep:false ~ctx (Analyses.ask_of_ctx ctx) gs st shallow_addrs in
invalidate ~deep:true ~ctx (Analyses.ask_of_ctx ctx) gs st' deep_addrs

let check_free_of_non_heap_mem ctx special_fn ptr =
match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local ptr with
| Address a ->
let points_to_set = addrToLvalSet a in
if Q.LS.is_top points_to_set then
M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Points-to set for pointer %a in function %s is top. Potential free of non-dynamically allocated memory may occur" d_exp ptr special_fn.vname
else if (Q.LS.exists (fun (v, _) -> not (ctx.ask (Q.IsHeapVar v))) points_to_set) || (AD.mem Addr.UnknownPtr a) then
M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Free of non-dynamically allocated memory in function %s for pointer %a" special_fn.vname d_exp ptr
| _ -> M.warn ~category:MessageCategory.Analyzer "Pointer %a in function %s doesn't evaluate to a valid address." d_exp ptr special_fn.vname

let special ctx (lv:lval option) (f: varinfo) (args: exp list) =
let invalidate_ret_lv st = match lv with
| Some lv ->
Expand Down Expand Up @@ -2315,6 +2341,8 @@ struct
| _ -> st
end
| Realloc { ptr = p; size }, _ ->
(* Realloc shouldn't be passed non-dynamically allocated memory *)
check_free_of_non_heap_mem ctx f p;
begin match lv with
| Some lv ->
let ask = Analyses.ask_of_ctx ctx in
Expand Down Expand Up @@ -2346,6 +2374,10 @@ struct
| None ->
st
end
| Free ptr, _ ->
(* Free shouldn't be passed non-dynamically allocated memory *)
check_free_of_non_heap_mem ctx f ptr;
st
| Assert { exp; refine; _ }, _ -> assert_fn ctx exp refine
| Setjmp { env }, _ ->
let ask = Analyses.ask_of_ctx ctx in
Expand Down Expand Up @@ -2383,6 +2415,13 @@ struct
let rv = ensure_not_zero @@ eval_rv ask ctx.global ctx.local value in
let t = Cilfacade.typeOf value in
set ~ctx ~t_override:t ask ctx.global ctx.local (AD.of_var !longjmp_return) t rv (* Not raising Deadcode here, deadcode is raised at a higher level! *)
| Rand, _ ->
begin match lv with
| Some x ->
let result:value = (Int (ID.starting IInt Z.zero)) in
set ~ctx (Analyses.ask_of_ctx ctx) gs st (eval_lv (Analyses.ask_of_ctx ctx) ctx.global st x) (Cilfacade.typeOfLval x) result
| None -> st
end
| _, _ ->
let st =
special_unknown_invalidate ctx (Analyses.ask_of_ctx ctx) gs st f args
Expand Down
Loading

0 comments on commit fcd98fa

Please sign in to comment.