Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve multi-threaded UAF analysis and add SVComp result generation #1123

Merged
merged 24 commits into from
Sep 29, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
a57112b
Add regression test case with a joined thread
mrstanb Jul 26, 2023
0b3bfc9
Add global variable for detection of UAF occurrences
mrstanb Jul 26, 2023
77f7e89
Add SVComp result generation for memory safety
mrstanb Jul 26, 2023
cfef9bd
Fix multi-line if expression
mrstanb Jul 30, 2023
cbaffbb
Side-effect a tuple of tids and sets of joined threads
mrstanb Jul 30, 2023
c01585d
Warn if threadJoins is deactivated
mrstanb Jul 31, 2023
49a6d54
Update joined thread test case annotations
mrstanb Jul 31, 2023
b02d0a7
Remove threadJoins activation warning
mrstanb Aug 2, 2023
50f20b0
Use a map domain for the globals
mrstanb Aug 2, 2023
c90fb0f
Add global vars for all three memory safety SVComp properties
mrstanb Aug 2, 2023
e28f72b
Improve SVComp result generation and support all 3 memory-safety props
mrstanb Aug 2, 2023
a054a4f
Warn for UAF only in case there's a deref happening
mrstanb Aug 26, 2023
3271430
Adapt regression tests to not WARN for UAF if there's no deref
mrstanb Aug 26, 2023
d49db71
Clean up and use proper global vars for SV-COMP
mrstanb Aug 26, 2023
034b0ab
Use proper bug names when warning for multi-threaded programs
mrstanb Aug 27, 2023
68e1b71
Add a valid-memsafety.prp file
mrstanb Aug 27, 2023
cd95e8d
Try to not warn for threads joined before free()
mrstanb Aug 28, 2023
ba4d301
Enable threadJoins for all multi-threaded test cases
mrstanb Aug 28, 2023
66f0c9d
Add checks for implicit dereferencing of pointers
mrstanb Aug 28, 2023
5464636
Accommodate implicit dereferencing in regression tests
mrstanb Aug 28, 2023
049dc58
Remove redundant intersection when side-effecting
mrstanb Aug 28, 2023
7adff2f
Merge branch 'master' into improve-uaf-analysis
mrstanb Sep 26, 2023
75f1580
Improve double free check
mrstanb Sep 28, 2023
cb29ecd
Merge branch 'master' into improve-uaf-analysis
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
65 changes: 35 additions & 30 deletions src/analyses/useAfterFree.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion tests/regression/74-use_after_free/04-function-call-uaf.c
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
8 changes: 5 additions & 3 deletions tests/regression/74-use_after_free/06-uaf-struct.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
mrstanb marked this conversation as resolved.
Show resolved Hide resolved
if (strlen(line + 5) < 31) {
strcpy(auth->name, line + 5); //WARN
}
Expand Down
8 changes: 5 additions & 3 deletions tests/regression/74-use_after_free/09-juliet-uaf.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -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
}
}
}
Expand Down
12 changes: 7 additions & 5 deletions tests/regression/74-use_after_free/11-wrapper-funs-uaf.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
mrstanb marked this conversation as resolved.
Show resolved Hide resolved

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