Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 14 additions & 0 deletions src/analyses/base.ml
Original file line number Diff line number Diff line change
Expand Up @@ -2226,6 +2226,20 @@ struct
in
List.filter_map (create_thread ~multiple (Some (Mem id, NoOffset)) (Some ptc_arg)) start_funvars_with_unknown
end
| SignalHandler { signal; handler }, _ -> begin
(* extra sync so that we do not analyze new threads with bottom global invariant *)
publish_all man `Thread;
(* Collect the threads. *)
let start_addr = eval_tv ~man man.local handler in
let start_funvars = AD.to_var_may start_addr in
let start_funvars_with_unknown =
if AD.mem Addr.UnknownPtr start_addr then
dummyFunDec.svar :: start_funvars
else
start_funvars
in
List.filter_map (create_thread ~multiple:false None (Some signal)) start_funvars_with_unknown
end
| _, _ ->
let shallow_args = LibraryDesc.Accesses.find desc.accs { kind = Spawn; deep = false } args in
let deep_args = LibraryDesc.Accesses.find desc.accs { kind = Spawn; deep = true } args in
Expand Down
1 change: 1 addition & 0 deletions src/util/library/libraryDesc.ml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ type special =
| Bounded of { exp: Cil.exp} (** Used to check for bounds for termination analysis. *)
| Rand
| Once of { once_control: Cil.exp; init_routine: Cil.exp; }
| SignalHandler of { signal: Cil.exp; handler: Cil.exp; }
| Unknown (** Anything not belonging to other types. *) (* TODO: rename to Other? *)


Expand Down
4 changes: 2 additions & 2 deletions src/util/library/libraryFunctions.ml
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[
("remove", unknown [drop "pathname" [r]]);
("raise", unknown [drop "sig" []]); (* safe-ish, we don't handle signal handlers for now *)
("timespec_get", unknown [drop "ts" [w]; drop "base" []]);
("signal", unknown [drop "signum" []; drop "handler" [s]]);
("signal", special [__ "signum" []; __ "handler" [s]] @@ fun signal handler -> SignalHandler {signal; handler});
("va_arg", unknown [drop "ap" [r]; drop "T" []]);
("va_start", unknown [drop "ap" [r_deep]; drop "parmN" []]);
("va_end", unknown [drop "ap" [r_deep]]);
Expand Down Expand Up @@ -301,7 +301,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[
("gethostname", unknown [drop "name" [w]; drop "len" []]);
("getpeername", unknown [drop "sockfd" []; drop "addr" [w_deep]; drop "addrlen" [r; w]]);
("socket", unknown [drop "domain" []; drop "type" []; drop "protocol" []]);
("sigaction", unknown [drop "signum" []; drop "act" [r_deep; s_deep]; drop "oldact" [w_deep]]);
("sigaction", unknown [drop "signum" []; drop "act" [r_deep; s_deep]; drop "oldact" [w_deep]]); (* TODO: SignalHandler special *)
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sigaction still uses the generic unknown descriptor, so installing a handler via sigaction will continue to be treated as spawning a non-unique thread through Spawn accesses. If the intent of this PR is to make signal-handler threads unique in general, consider introducing a SignalHandler-style special for sigaction as well (extracting the handler from struct sigaction), or otherwise documenting why sigaction is intentionally left imprecise/unsound for now.

Suggested change
("sigaction", unknown [drop "signum" []; drop "act" [r_deep; s_deep]; drop "oldact" [w_deep]]); (* TODO: SignalHandler special *)
("sigaction", unknown [drop "signum" []; drop "act" [r_deep; s_deep]; drop "oldact" [w_deep]]); (* Intentionally modeled as unknown: signal-handler threads installed via sigaction are treated as non-unique (like generic Spawn). A more precise SignalHandler-style special may be added in the future. *)

Copilot uses AI. Check for mistakes.
("tcgetattr", unknown [drop "fd" []; drop "termios_p" [w_deep]]);
("tcsetattr", unknown [drop "fd" []; drop "optional_actions" []; drop "termios_p" [r_deep]]);
("access", unknown [drop "pathname" [r]; drop "mode" []]);
Expand Down
15 changes: 15 additions & 0 deletions tests/regression/03-practical/37-signal-sound.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#include <signal.h>
#include <goblint.h>

int g = 0;

void handler(int sig) {
g = 1;
}

int main() {
__goblint_check(g == 0);
signal(SIGTERM, handler);
__goblint_check(g == 0); // UNKNOWN!
return 0;
}
13 changes: 13 additions & 0 deletions tests/regression/03-practical/38-signal-unique.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#include <signal.h>

int g = 0;

void handler(int sig) {
g = 1; // NORACE
g = 2; // NORACE
}

int main() {
signal(SIGTERM, handler);
return 0;
}
11 changes: 11 additions & 0 deletions tests/regression/03-practical/39-signal-arg.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#include <signal.h>
#include <goblint.h>

void handler(int sig) {
__goblint_check(sig == SIGTERM);
}

int main() {
signal(SIGTERM, handler);
return 0;
}
Loading