aboutsummaryrefslogtreecommitdiffstats
path: root/target-i386
diff options
context:
space:
mode:
authorJiang Yunhong <yunhong.jiang@intel.com>2011-12-26 14:47:33 +0800
committerJean-Baptiste Queru <jbq@google.com>2012-01-12 08:51:07 -0800
commita9fd5a1492276fe429eab8527fc425cee5c62ed8 (patch)
tree2d99fcaf4c75746bd68247efab92e4946775a291 /target-i386
parentaa1af37d8b3c3a21eb4cac4a225225425b50d08c (diff)
downloadexternal_qemu-a9fd5a1492276fe429eab8527fc425cee5c62ed8.zip
external_qemu-a9fd5a1492276fe429eab8527fc425cee5c62ed8.tar.gz
external_qemu-a9fd5a1492276fe429eab8527fc425cee5c62ed8.tar.bz2
Fix signal handling for GS_RESTORE hack
Currently we hack QEMU's signal handler because of the GS_BASE MSR KVM bug. The reason of the hack is, when signal happens during KVM_RUN ioctl, the GS_BASE is broken, we have to restore the correct gs before run the signal handler. However, currently we take signal even when signal is set as SIG_DFL/SIG_IGN, or the signal is set with SIG_SIGINFO. Currently "kill -13" to emulator causes emulator crash. This patch fixed it as: 1) If the signal handler is set as SIG_DFL or SIG_IGN, we don't take the signal 2) If the signal handler is set with SIG_SIGINFO, we will use sa_sigaction 3) Cover all potential signals One thing left is, if the signal handler is setup dynamically, this method may still have issue because currently the signal handlers are taken statically when first KVM_RUN ioctl. Luckily this does not happen now. Change-Id: Icfbe43db665a11f2b6eaf9593075b3e643783ee7 Signed-off-by: Jiang Yunhong <yunhong.jiang@intel.com>
Diffstat (limited to 'target-i386')
-rw-r--r--target-i386/kvm-gs-restore.c111
1 files changed, 77 insertions, 34 deletions
diff --git a/target-i386/kvm-gs-restore.c b/target-i386/kvm-gs-restore.c
index a0ff565..2ca6ab6 100644
--- a/target-i386/kvm-gs-restore.c
+++ b/target-i386/kvm-gs-restore.c
@@ -32,61 +32,104 @@ static void check_and_restore_gs(void)
restoregs(initial_gs);
}
-static struct sigaction old_alarm_act, old_gio_act, old_pipe_act,old_usr1_act, old_chld_act;
-static void temp_sig_handler(int host_signum)
+struct sigact_status
+{
+ unsigned int sigaction:1;
+ __sighandler_t old_handler;
+ void (*old_sigaction) (int, siginfo_t *, void *);
+};
+static struct sigact_status o_sigact[SIGUNUSED];
+
+static void temp_sig_handler(int signum)
{
/* !!! must restore gs firstly */
check_and_restore_gs();
- switch (host_signum)
- {
- case SIGALRM:
- old_alarm_act.sa_handler(host_signum);
- break;
- case SIGIO:
- old_gio_act.sa_handler(host_signum);
- break;
+ if (signum < SIGHUP || signum >= SIGUNUSED)
+ {
+ fprintf(stderr, "Invalid signal %x in temp_sig_handler\n", signum);
+ abort();
+ }
- case SIGUSR1:
- old_usr1_act.sa_handler(host_signum);
- break;
+ if ( !o_sigact[signum].sigaction && o_sigact[signum].old_handler)
+ o_sigact[signum].old_handler(signum);
+ else
+ {
+ fprintf(stderr, "Invalid signal in temp_sig_handler: "
+ "signal %x sa_info %s!!\n",
+ signum, o_sigact[signum].sigaction ? "set":"not set" );
+ abort();
+ }
+}
- case SIGPIPE:
- old_pipe_act.sa_handler(host_signum);
- break;
+static void temp_sig_sigaction(int signum, siginfo_t *info, void *ucontext)
+{
+ /* !!! must restore gs firstly */
+ check_and_restore_gs();
- case SIGCHLD:
- old_chld_act.sa_handler(host_signum);
- break;
+ if (signum < SIGHUP || signum >= SIGUNUSED)
+ {
+ fprintf(stderr, "Invalid signal %x in temp_sig_sigaction\n", signum);
+ abort();
+ }
- default:
- fprintf(stderr, "Not take signal %x!!\n", host_signum);
- break;
+ if ( o_sigact[signum].sigaction && o_sigact[signum].old_sigaction )
+ o_sigact[signum].old_sigaction(signum, info, ucontext);
+ else
+ {
+ fprintf(stderr, "Invalid signal in temp_sig_sigaction: "
+ "signal %x sa_info %s!!\n",
+ signum, o_sigact[signum].sigaction ? "set":"not set" );
+ abort();
}
}
static int sig_taken = 0;
+
static int take_signal_handler(void)
{
- struct sigaction act;
- int ret;
+ int i;
if (gs_need_restore == KVM_GS_RESTORE_NO)
return 0;
if (sig_taken)
return 0;
+ memset(o_sigact, 0, sizeof(o_sigact));
+
+ /* SIGHUP is 1 in POSIX */
+ for (i = SIGHUP; i < SIGUNUSED; i++)
+ {
+ int sigret;
+ struct sigaction act, old_act;
+
+ sigret = sigaction(i, NULL, &old_act);
+ if (sigret)
+ continue;
+ /* We don't need take the handler for default or ignore signals */
+ if ( !(old_act.sa_flags & SA_SIGINFO) &&
+ ((old_act.sa_handler == SIG_IGN ) ||
+ (old_act.sa_handler == SIG_DFL)))
+ continue;
+
+ memcpy(&act, &old_act, sizeof(struct sigaction));
+
+ if (old_act.sa_flags & SA_SIGINFO)
+ {
+ o_sigact[i].old_sigaction = old_act.sa_sigaction;
+ o_sigact[i].sigaction = 1;
+ act.sa_sigaction = temp_sig_sigaction;
+ }
+ else
+ {
+ o_sigact[i].old_handler = old_act.sa_handler;
+ act.sa_handler = temp_sig_handler;
+ }
+
+ sigaction(i, &act, NULL);
+ continue;
+ }
sig_taken = 1;
- sigfillset(&act.sa_mask);
- act.sa_flags = 0;
- act.sa_handler = temp_sig_handler;
- /* Did we missed any other signal ? */
- sigaction(SIGALRM, &act, &old_alarm_act);
- sigaction(SIGIO, &act, &old_gio_act);
- sigaction(SIGUSR1, &act, &old_usr1_act);
- sigaction(SIGPIPE, &act, &old_pipe_act);
- act.sa_flags = SA_NOCLDSTOP;
- sigaction(SIGCHLD, &act, &old_chld_act);
return 1;
}