diff --git a/src/plugins/value/engine/compute_functions.ml b/src/plugins/value/engine/compute_functions.ml
index 09d04639e829e0f8edd3baad947cdddf0af58241..a932d0eebdcf98c0dc1a21575316dd7a9cb5208b 100644
--- a/src/plugins/value/engine/compute_functions.ml
+++ b/src/plugins/value/engine/compute_functions.ml
@@ -113,20 +113,25 @@ let post_analysis () =
   (* Remove redundant alarms *)
   if Value_parameters.RmAssert.get () then !Db.Value.rm_asserts ()
 
-(* Register a signal handler for SIGUSR1, that will be used to abort Value *)
-let () =
-  let prev = ref (fun _ -> ()) in
-  let handler (_signal: int) =
-    !prev Sys.sigusr1; (* Call previous signal handler *)
-    Value_parameters.warning "Stopping analysis at user request@.";
-    Iterator.signal_abort ()
+(* Registers signal handlers for SIGUSR1 and SIGINT to cleanly abort the Eva
+   analysis. Returns a function that restores previous signal behaviors after
+   the analysis. *)
+let register_signal_handler () =
+  let warn () =
+    Value_parameters.warning ~once:true "Stopping analysis at user request@."
   in
-  try
-    match Sys.signal Sys.sigusr1 (Sys.Signal_handle handler) with
-    | Sys.Signal_default | Sys.Signal_ignore -> ()
-    | Sys.Signal_handle f -> prev := f
-  with Invalid_argument _ -> () (* Ignore: SIGURSR1 is not available on Windows,
-                                   and possibly on other platforms. *)
+  let stop _ = warn (); Iterator.signal_abort () in
+  let interrupt _ = warn (); raise Sys.Break in
+  let register_handler signal handler =
+    match Sys.signal signal (Sys.Signal_handle handler) with
+    | previous_behavior -> fun () -> Sys.set_signal signal previous_behavior
+    | exception Invalid_argument _ -> fun () -> ()
+    (* Ignore: SIGURSR1 is not available on Windows,
+       and possibly on other platforms. *)
+  in
+  let restore_sigusr1 = register_handler Sys.sigusr1 stop in
+  let restore_sigint = register_handler Sys.sigint interrupt in
+  fun () -> restore_sigusr1 (); restore_sigint ()
 
 module Make (Abstract: Abstractions.Eva) = struct
 
@@ -327,7 +332,8 @@ module Make (Abstract: Abstractions.Eva) = struct
     Db.Value.Call_Value_Callbacks.apply (cvalue_state, [kf, Kglobal])
 
   let compute kf init_state =
-    try
+    let restore_signals = register_signal_handler () in
+    let compute () =
       Value_util.push_call_stack kf Kglobal;
       store_initial_state kf init_state;
       let call =
@@ -344,10 +350,13 @@ module Make (Abstract: Abstractions.Eva) = struct
       post_analysis ();
       Abstract.Dom.post_analysis final_state;
       Value_results.print_summary ();
-    with Log.AbortError _ | Log.AbortFatal _ | Log.FeatureRequest _ as e ->
+      restore_signals ()
+    in
+    let cleanup () =
       Abstract.Dom.Store.mark_as_computed ();
-      post_analysis_cleanup ~aborted:true;
-      raise e
+      post_analysis_cleanup ~aborted:true
+    in
+    Value_util.protect compute ~cleanup
 
   let compute_from_entry_point kf ~lib_entry =
     pre_analysis ();
@@ -355,10 +364,9 @@ module Make (Abstract: Abstractions.Eva) = struct
       (if lib_entry then "n in" else " ")
       Kernel_function.pretty kf;
     let initial_state =
-      try Init.initial_state_with_formals ~lib_entry kf
-      with Log.AbortError _ | Log.AbortFatal _ | Log.FeatureRequest _ as e ->
-        post_analysis_cleanup ~aborted:true;
-        raise e
+      Value_util.protect
+        (fun () -> Init.initial_state_with_formals ~lib_entry kf)
+        ~cleanup:(fun () -> post_analysis_cleanup ~aborted:true)
     in
     match initial_state with
     | `Bottom ->
diff --git a/src/plugins/value/engine/iterator.ml b/src/plugins/value/engine/iterator.ml
index 6f1f546680aa2eb4d61cbe7716d2911c0f21dc3a..933035419a65831bb45ff24fc2c41ae84fd27181 100644
--- a/src/plugins/value/engine/iterator.ml
+++ b/src/plugins/value/engine/iterator.ml
@@ -29,7 +29,7 @@ let check_signals, signal_abort =
   (fun () ->
      if !signal_emitted then begin
        signal_emitted := false;
-       raise (Log.AbortError "eva")
+       raise Sys.Break
      end),
   (fun () -> signal_emitted := true)
 
@@ -765,7 +765,7 @@ module Computer
         end)
         ()
     in
-    try
+    let compute () =
       let results = Dataflow.compute () in
       if Value_parameters.ValShowProgress.get () then
         Value_parameters.feedback "Recording results for %a"
@@ -779,11 +779,12 @@ module Computer
            Kernel_function.pretty kf;
        | _ -> ());
       results, !Dataflow.cacheable
-    with Log.AbortError _ | Log.AbortFatal _ | Log.FeatureRequest _ as e ->
-      (* analysis was aborted: pop the call stack and inform the caller *)
+    in
+    let cleanup () =
       Dataflow.mark_degeneration ();
-      Dataflow.merge_results ();
-      raise e
+      Dataflow.merge_results ()
+    in
+    Value_util.protect compute ~cleanup
 end
 
 
diff --git a/src/plugins/value/engine/transfer_stmt.ml b/src/plugins/value/engine/transfer_stmt.ml
index 874b2b65af01462ba3bfc492638d958e221e30be..f18e4c385e8a192ee184d3e37392c7b49b15844c 100644
--- a/src/plugins/value/engine/transfer_stmt.ml
+++ b/src/plugins/value/engine/transfer_stmt.ml
@@ -319,7 +319,7 @@ module Make (Abstract: Abstractions.Eva) = struct
       (* Changed by compute_call_ref, called from process_call *)
       Cil.CurrentLoc.set (Cil_datatype.Stmt.loc stmt);
     in
-    try
+    let process () =
       let domain_valuation = Eval.to_domain_valuation valuation in
       let res =
         (* Process the call according to the domain decision. *)
@@ -332,10 +332,9 @@ module Make (Abstract: Abstractions.Eva) = struct
       in
       cleanup ();
       res
-    with Log.AbortError _ | Log.AbortFatal _ | Log.FeatureRequest _ as e ->
-      InOutCallback.clear ();
-      cleanup ();
-      raise e
+    in
+    Value_util.protect process
+      ~cleanup:(fun () -> InOutCallback.clear (); cleanup ())
 
   (* ------------------- Retro propagation on formals ----------------------- *)
 
diff --git a/src/plugins/value/utils/value_util.ml b/src/plugins/value/utils/value_util.ml
index 13a005dd2afb149c347bfdd8ed45f51fd90b3262..9f7ddc3f46156c55e8a3159cd15db1058b988464 100644
--- a/src/plugins/value/utils/value_util.ml
+++ b/src/plugins/value/utils/value_util.ml
@@ -107,6 +107,22 @@ module DegenerationPoints =
       let dependencies = [ Db.Value.self ]
     end)
 
+let protect_only_once = ref true
+
+let protect f ~cleanup =
+  let catch () = !protect_only_once && not (Kernel.SaveState.is_empty ()) in
+  try f ();
+  with
+  | Log.AbortError _ | Log.AbortFatal _ | Log.FeatureRequest _
+  | Sys.Break as e when catch () ->
+    try
+      Value_parameters.feedback ~once:true "Clean up and save partial results.";
+      cleanup ();
+      raise e;
+    with e ->
+      protect_only_once := false;
+      raise e
+
 let register_new_var v typ =
   if Cil.isFunctionType typ then
     Globals.Functions.replace_by_declaration (Cil.empty_funspec()) v v.vdecl
diff --git a/src/plugins/value/utils/value_util.mli b/src/plugins/value/utils/value_util.mli
index 0bfe89cbe916816878319417b0dc123ad79bea1c..c99393d8885d66b5e1e11023582e3b7b797f05c7 100644
--- a/src/plugins/value/utils/value_util.mli
+++ b/src/plugins/value/utils/value_util.mli
@@ -51,6 +51,11 @@ val warning_once_current : ('a, Format.formatter, unit) format -> 'a
     status associated to {!Value_parameters.wkey_alarm} *)
 val alarm_report: 'a Log.pretty_printer
 
+(** [protect f ~cleanup] runs [f]. On a user interruption or a Frama-C error,
+    if option -save is set, applies [cleanup]. This is used to clean up and
+    save partial results when the analysis is aborted. *)
+val protect: (unit -> 'a) -> cleanup:(unit -> unit) -> 'a
+
 (* Statements for which the analysis has degenerated. [true] means that this is
    the statement on which the degeneration occurred, or a statement above in
    the callstack *)
diff --git a/tests/fc_script/oracle/make-wrapper.res b/tests/fc_script/oracle/make-wrapper.res
index 09358673bd043241721520d914345a9a782b35d5..8370fe5e49498ce18397bc847647adac6ce49eaf 100644
--- a/tests/fc_script/oracle/make-wrapper.res
+++ b/tests/fc_script/oracle/make-wrapper.res
@@ -25,6 +25,7 @@ Command: ../../bin/frama-c -kernel-warn-key annot:missing-spec=abort -kernel-war
 [kernel:annot:missing-spec] make-wrapper.c:13: Warning: 
   Neither code nor specification for function large_name_to_force_line_break_in_stack_msg, generating default assigns from the prototype
 [kernel] User Error: warning annot:missing-spec treated as fatal error.
+[eva] Clean up and save partial results.
 [kernel] Frama-C aborted: invalid user input.
 [kernel] Warning: attempting to save on non-zero exit code: modifying filename into `PWD/make-for-make-wrapper.eva/framac.sav.error'.