Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
89e0074
Make HeapVar support stack allocation, update IsHeapVar and add
mrstanb Sep 20, 2023
9a06189
Implement answer for IsDynamicallyAlloced
mrstanb Sep 20, 2023
d8c5965
Update usage of HeapVar and IsHeapVar
mrstanb Sep 20, 2023
954be03
Add "alloca" as a library function
mrstanb Sep 20, 2023
aa97af8
Add Alloca special function type
mrstanb Sep 21, 2023
6e9bb2f
Classify __builtin_alloca as Alloca and not as Malloc
mrstanb Sep 21, 2023
d9df607
Implement and use IsHeapVar and IsDynamicallyAlloced right
mrstanb Sep 21, 2023
4c84a10
CI run fix: remove alloca from invalidate_actions
mrstanb Sep 21, 2023
55a8f1e
Remove __builtin_alloca from invalidate_actions
mrstanb Sep 28, 2023
21584ee
Extend UAF analysis to support alloca()
mrstanb Sep 29, 2023
05a3b98
Add alloca() UAF test case
mrstanb Sep 29, 2023
2fbcdb8
Add alloca() invalid dealloc test case
mrstanb Sep 29, 2023
9689c8c
Merge branch 'master' into stack-allocating-functions-precision
mrstanb Sep 29, 2023
5cca543
Add forgotton base_address check in BlobSize after master merge
mrstanb Sep 29, 2023
4090e74
Fix __builtin_alloca classification
mrstanb Sep 29, 2023
35513d0
Move all *alloca() functions to gcc_descs_list
mrstanb Sep 29, 2023
432a4e3
Simply HeapVar ask in heap_var
mrstanb Sep 29, 2023
3bdc92d
Extract long query ask into own function
mrstanb Sep 29, 2023
7b1d298
Rename queries:
mrstanb Sep 29, 2023
a317421
Merge branch 'master' into stack-allocating-functions-precision
mrstanb Sep 29, 2023
5f16221
Fix wrong query usage in UAF
mrstanb Sep 29, 2023
b1f2f2d
Fix test numbering
mrstanb Sep 29, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 20 additions & 10 deletions src/analyses/base.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.IsHeapVar v)) || (if not from_base_addr then o <> `NoOffset else false)
| 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 (
Expand Down Expand Up @@ -1397,7 +1397,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
Expand Down Expand Up @@ -1443,7 +1443,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
Expand Down Expand Up @@ -2009,7 +2009,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.IsDynamicallyAlloced v)) || (ctx.ask (Q.IsDynamicallyAlloced v) && not @@ ctx.ask (Q.IsHeapVar v))
| _ -> false)
in
let has_non_zero_offset = AD.exists (function
Expand Down Expand Up @@ -2277,13 +2277,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
| 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 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 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));
Expand All @@ -2293,7 +2302,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 *)
Expand Down Expand Up @@ -2336,7 +2345,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
Expand Down Expand Up @@ -2584,6 +2593,7 @@ struct
| MayBeThreadReturn
| PartAccess _
| IsHeapVar _
| IsDynamicallyAlloced _
| IsMultiple _
| CreatedThreads
| MustJoinedThreads ->
Expand Down
1 change: 1 addition & 0 deletions src/analyses/libraryDesc.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand Down
4 changes: 3 additions & 1 deletion src/analyses/libraryFunctions.ml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,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);
("__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 });
Expand Down Expand Up @@ -438,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.[
Expand Down
2 changes: 1 addition & 1 deletion src/analyses/mallocFresh.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions src/analyses/memLeak.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.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
Expand Down
43 changes: 30 additions & 13 deletions src/analyses/useAfterFree.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand All @@ -13,7 +18,7 @@ struct

let name () = "useAfterFree"

module D = ToppedVarInfoSet
module D = StackAndHeapVars
module C = Lattice.Unit
module G = ThreadIdSet
module V = VarinfoV
Expand Down Expand Up @@ -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 ->
Expand Down Expand Up @@ -85,13 +90,13 @@ 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 =
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
Expand Down Expand Up @@ -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 *)
Expand All @@ -153,21 +158,26 @@ 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
if Queries.AD.is_top reachable_from_args || D.is_top caller_state then
[caller_state, caller_state]
else
let reachable_vars = Queries.AD.to_var_may reachable_from_args in
let callee_state = 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;
Expand All @@ -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.IsHeapVar 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
Expand Down
5 changes: 4 additions & 1 deletion src/analyses/wrapperFunctionAnalysis.ml
Original file line number Diff line number Diff line change
Expand Up @@ -133,16 +133,19 @@ 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
in
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 && not @@ hasAttribute "stack_alloca" v.vattr
| Q.IsDynamicallyAlloced v ->
NodeVarinfoMap.mem_varinfo v
| Q.IsMultiple v ->
begin match NodeVarinfoMap.from_varinfo v with
Expand Down
17 changes: 12 additions & 5 deletions src/domains/queries.ml
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,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 *)
| 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? *)
Expand Down Expand Up @@ -154,6 +155,7 @@ struct
| MayBePublicWithout _ -> (module MayBool)
| MayBeThreadReturn -> (module MayBool)
| IsHeapVar _ -> (module MayBool)
| IsDynamicallyAlloced _ -> (module MayBool)
| MustBeProtectedBy _ -> (module MustBool)
| MustBeAtomic -> (module MustBool)
| MustBeSingleThreaded _ -> (module MustBool)
Expand All @@ -165,7 +167,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)
Expand Down Expand Up @@ -218,6 +220,7 @@ struct
| MayBePublicWithout _ -> MayBool.top ()
| MayBeThreadReturn -> MayBool.top ()
| IsHeapVar _ -> MayBool.top ()
| IsDynamicallyAlloced _ -> MayBool.top ()
| MutexType _ -> MutexAttrDomain.top ()
| MustBeProtectedBy _ -> MustBool.top ()
| MustBeAtomic -> MustBool.top ()
Expand All @@ -230,7 +233,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 ()
Expand Down Expand Up @@ -290,7 +293,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
Expand All @@ -315,6 +318,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
Expand Down Expand Up @@ -354,6 +358,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
Expand Down Expand Up @@ -394,6 +399,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
Expand Down Expand Up @@ -441,8 +447,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
Expand Down
16 changes: 16 additions & 0 deletions tests/regression/74-use_after_free/13-alloca-uaf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//PARAM: --set ana.activated[+] useAfterFree
#include <stdlib.h>
#include <alloca.h>

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;
}
Loading