Skip to content

Commit

Permalink
Merge pull request #1082 from goblint/thread-unsafe-funs
Browse files Browse the repository at this point in the history
Detect calls of thread-unsafe functions as races
  • Loading branch information
sim642 authored Aug 15, 2023
2 parents b1b710e + 8465e0b commit b52da13
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 15 deletions.
27 changes: 14 additions & 13 deletions src/analyses/libraryFunctions.ml
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[
("getchar", unknown []);
("putchar", unknown [drop "ch" []]);
("puts", unknown [drop "s" [r]]);
("rand", unknown ~attrs:[ThreadUnsafe] []);
("rand", special ~attrs:[ThreadUnsafe] [] Rand);
("strerror", unknown ~attrs:[ThreadUnsafe] [drop "errnum" []]);
("strspn", unknown [drop "s" [r]; drop "accept" [r]]);
("strcspn", unknown [drop "s" [r]; drop "accept" [r]]);
Expand Down Expand Up @@ -118,7 +118,6 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[
("_setjmp", special [__ "env" [w]] @@ fun env -> Setjmp { env }); (* only has one underscore *)
("setjmp", special [__ "env" [w]] @@ fun env -> Setjmp { env });
("longjmp", special [__ "env" [r]; __ "value" []] @@ fun env value -> Longjmp { env; value });
("rand", special [] Rand);
]

(** C POSIX library functions.
Expand Down Expand Up @@ -890,70 +889,70 @@ struct

let writesAllButFirst n f a x =
match a with
| Write | Spawn -> f a x @ drop n x
| Write | Call | Spawn -> f a x @ drop n x
| Read -> f a x
| Free -> []

let readsAllButFirst n f a x =
match a with
| Write | Spawn -> f a x
| Write | Call | Spawn -> f a x
| Read -> f a x @ drop n x
| Free -> []

let reads ns a x =
let i, o = partition ns x in
match a with
| Write | Spawn -> o
| Write | Call | Spawn -> o
| Read -> i
| Free -> []

let writes ns a x =
let i, o = partition ns x in
match a with
| Write | Spawn -> i
| Write | Call | Spawn -> i
| Read -> o
| Free -> []

let frees ns a x =
let i, o = partition ns x in
match a with
| Write | Spawn -> []
| Write | Call | Spawn -> []
| Read -> o
| Free -> i

let readsFrees rs fs a x =
match a with
| Write | Spawn -> []
| Write | Call | Spawn -> []
| Read -> keep rs x
| Free -> keep fs x

let onlyReads ns a x =
match a with
| Write | Spawn -> []
| Write | Call | Spawn -> []
| Read -> keep ns x
| Free -> []

let onlyWrites ns a x =
match a with
| Write | Spawn -> keep ns x
| Write | Call | Spawn -> keep ns x
| Read -> []
| Free -> []

let readsWrites rs ws a x =
match a with
| Write | Spawn -> keep ws x
| Write | Call | Spawn -> keep ws x
| Read -> keep rs x
| Free -> []

let readsAll a x =
match a with
| Write | Spawn -> []
| Write | Call | Spawn -> []
| Read -> x
| Free -> []

let writesAll a x =
match a with
| Write | Spawn -> x
| Write | Call | Spawn -> x
| Read -> []
| Free -> []
end
Expand Down Expand Up @@ -1165,6 +1164,8 @@ let unknown_desc ~f name = (* TODO: remove name argument, unknown function shoul
| Read when GobConfig.get_bool "sem.unknown_function.read.args" -> args
| Read -> []
| Free -> []
| Call when get_bool "sem.unknown_function.call.args" -> args
| Call -> []
| Spawn when get_bool "sem.unknown_function.spawn" -> args
| Spawn -> []
in
Expand Down
1 change: 1 addition & 0 deletions src/analyses/mutexAnalysis.ml
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@ struct
let write = match kind with
| Write | Free -> true
| Read -> false
| Call
| Spawn -> false (* TODO: nonsense? *)
in
let s = GProtecting.make ~write ~recovered:is_recovered_to_st locks in
Expand Down
15 changes: 14 additions & 1 deletion src/analyses/raceAnalysis.ml
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ struct
let g: V.t = Obj.obj g in
begin match g with
| `Left g' -> (* accesses *)
(* ignore (Pretty.printf "WarnGlobal %a\n" CilType.Varinfo.pretty g); *)
(* ignore (Pretty.printf "WarnGlobal %a\n" Access.MemoRoot.pretty g'); *)
let trie = G.access (ctx.global g) in
(** Distribute access to contained fields. *)
let rec distribute_inner offset (accs, children) ancestor_accs =
Expand Down Expand Up @@ -193,6 +193,19 @@ struct
| _ ->
ctx.local

let special ctx (lvalOpt: lval option) (f:varinfo) (arglist:exp list) : D.t =
(* perform shallow and deep invalidate according to Library descriptors *)
let desc = LibraryFunctions.find f in
if List.mem LibraryDesc.ThreadUnsafe desc.attrs then (
let e = Lval (Var f, NoOffset) in
let conf = 110 in
let loc = Option.get !Node.current_node in
let vo = Some f in
let a = Obj.obj (ctx.ask (PartAccess (Memory {exp=e; var_opt=vo; kind=Call}))) in
side_access ctx (conf, Call, loc, e, a) ((`Var f), `NoOffset) ;
);
ctx.local

let finalize () =
let total = !safe + !unsafe + !vulnerable in
if total > 0 then (
Expand Down
4 changes: 3 additions & 1 deletion src/domains/accessKind.ml
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
(** Kinds of memory accesses. *)

type t =
| Write (** argument may be read or written to *)
| Write (** argument may be written to *)
| Read (** argument may be read *)
| Free (** argument may be freed *)
| Call (** argument may be called *)
| Spawn (** argument may be spawned *)
[@@deriving eq, ord, hash]
(** Specifies what is known about an argument. *)
Expand All @@ -12,6 +13,7 @@ let show: t -> string = function
| Write -> "write"
| Read -> "read"
| Free -> "free"
| Call -> "call"
| Spawn -> "spawn"

include Printable.SimpleShow (
Expand Down
7 changes: 7 additions & 0 deletions src/util/options.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1317,6 +1317,13 @@
"type": "boolean",
"default": true
},
"call": {
"title": "sem.unknown_function.call",
"description":
"Unknown function call calls reachable functions",
"type": "boolean",
"default": true
},
"invalidate": {
"title": "sem.unknown_function.invalidate",
"type": "object",
Expand Down
22 changes: 22 additions & 0 deletions tests/regression/04-mutex/90-thread-unsafe_fun_rc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#include <pthread.h>
#include <stdio.h>

pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;

void *t_fun(void *arg) {
pthread_mutex_lock(&mutex1);
rand(); // RACE!
pthread_mutex_unlock(&mutex1);
return NULL;
}

int main(void) {
pthread_t id;
pthread_create(&id, NULL, t_fun, NULL);
pthread_mutex_lock(&mutex2);
rand(); // RACE!
pthread_mutex_unlock(&mutex2);
pthread_join (id, NULL);
return 0;
}
21 changes: 21 additions & 0 deletions tests/regression/04-mutex/91-thread-unsafe_fun_nr.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#include <pthread.h>
#include <stdio.h>

pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;

void *t_fun(void *arg) {
pthread_mutex_lock(&mutex1);
rand(); // NORACE
pthread_mutex_unlock(&mutex1);
return NULL;
}

int main(void) {
pthread_t id;
pthread_create(&id, NULL, t_fun, NULL);
pthread_mutex_lock(&mutex1);
rand(); // NORACE
pthread_mutex_unlock(&mutex1);
pthread_join (id, NULL);
return 0;
}

0 comments on commit b52da13

Please sign in to comment.