linux-user: SIGSEGV from sigreturn need not be fatal
If the sigreturn syscall fails to read memory then this causes a SIGSEGV, but this is not necessarily a fatal signal -- the guest process can catch it. We don't implement this correctly because the behaviour of QEMU's force_sig() function has drifted away from the kernel function of the same name -- ours now does "always do a guest core dump and abort execution", whereas the kernel version simply forces the guest to take a signal, which may or may not eventually cause a core dump. Rename our force_sig() to dump_core_and_abort(), and provide a force_sig() which acts more like the kernel version as the sigreturn implementations expect it to. Since force_sig() now returns, we must update all the callsites to return -TARGET_QEMU_ESIGRETURN so that the main loop doesn't change the guest registers before the signal handler is invoked. Reviewed-by: Richard Henderson <rth@twiddle.net> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Riku Voipio <riku.voipio@linaro.org>
This commit is contained in:
		
							parent
							
								
									28298c912e
								
							
						
					
					
						commit
						c599d4d6d6
					
				| @ -512,6 +512,27 @@ void signal_init(void) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #if !defined(TARGET_OPENRISC) && !defined(TARGET_UNICORE32) && \ | ||||
|     !defined(TARGET_X86_64) | ||||
| /* Force a synchronously taken signal. The kernel force_sig() function
 | ||||
|  * also forces the signal to "not blocked, not ignored", but for QEMU | ||||
|  * that work is done in process_pending_signals(). | ||||
|  */ | ||||
| static void force_sig(int sig) | ||||
| { | ||||
|     CPUState *cpu = thread_cpu; | ||||
|     CPUArchState *env = cpu->env_ptr; | ||||
|     target_siginfo_t info; | ||||
| 
 | ||||
|     info.si_signo = sig; | ||||
|     info.si_errno = 0; | ||||
|     info.si_code = TARGET_SI_KERNEL; | ||||
|     info._sifields._kill._pid = 0; | ||||
|     info._sifields._kill._uid = 0; | ||||
|     queue_signal(env, info.si_signo, QEMU_SI_KILL, &info); | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #if !(defined(TARGET_X86_64) || defined(TARGET_UNICORE32)) | ||||
| 
 | ||||
| /* Force a SIGSEGV if we couldn't write to memory trying to set
 | ||||
| @ -526,7 +547,7 @@ static void force_sigsegv(int oldsig) | ||||
| 
 | ||||
|     if (oldsig == SIGSEGV) { | ||||
|         /* Make sure we don't try to deliver the signal again; this will
 | ||||
|          * end up with handle_pending_signal() calling force_sig(). | ||||
|          * end up with handle_pending_signal() calling dump_core_and_abort(). | ||||
|          */ | ||||
|         sigact_table[oldsig - 1]._sa_handler = TARGET_SIG_DFL; | ||||
|     } | ||||
| @ -540,7 +561,7 @@ static void force_sigsegv(int oldsig) | ||||
| #endif | ||||
| 
 | ||||
| /* abort execution with signal */ | ||||
| static void QEMU_NORETURN force_sig(int target_sig) | ||||
| static void QEMU_NORETURN dump_core_and_abort(int target_sig) | ||||
| { | ||||
|     CPUState *cpu = thread_cpu; | ||||
|     CPUArchState *env = cpu->env_ptr; | ||||
| @ -1181,7 +1202,7 @@ long do_sigreturn(CPUX86State *env) | ||||
| badframe: | ||||
|     unlock_user_struct(frame, frame_addr, 0); | ||||
|     force_sig(TARGET_SIGSEGV); | ||||
|     return 0; | ||||
|     return -TARGET_QEMU_ESIGRETURN; | ||||
| } | ||||
| 
 | ||||
| long do_rt_sigreturn(CPUX86State *env) | ||||
| @ -1212,7 +1233,7 @@ long do_rt_sigreturn(CPUX86State *env) | ||||
| badframe: | ||||
|     unlock_user_struct(frame, frame_addr, 0); | ||||
|     force_sig(TARGET_SIGSEGV); | ||||
|     return 0; | ||||
|     return -TARGET_QEMU_ESIGRETURN; | ||||
| } | ||||
| 
 | ||||
| #elif defined(TARGET_AARCH64) | ||||
| @ -1482,7 +1503,7 @@ long do_rt_sigreturn(CPUARMState *env) | ||||
|  badframe: | ||||
|     unlock_user_struct(frame, frame_addr, 0); | ||||
|     force_sig(TARGET_SIGSEGV); | ||||
|     return 0; | ||||
|     return -TARGET_QEMU_ESIGRETURN; | ||||
| } | ||||
| 
 | ||||
| long do_sigreturn(CPUARMState *env) | ||||
| @ -2004,8 +2025,8 @@ static long do_sigreturn_v1(CPUARMState *env) | ||||
|     return -TARGET_QEMU_ESIGRETURN; | ||||
| 
 | ||||
| badframe: | ||||
|     force_sig(TARGET_SIGSEGV /* , current */); | ||||
|     return 0; | ||||
|     force_sig(TARGET_SIGSEGV); | ||||
|     return -TARGET_QEMU_ESIGRETURN; | ||||
| } | ||||
| 
 | ||||
| static abi_ulong *restore_sigframe_v2_vfp(CPUARMState *env, abi_ulong *regspace) | ||||
| @ -2131,8 +2152,8 @@ static long do_sigreturn_v2(CPUARMState *env) | ||||
| 
 | ||||
| badframe: | ||||
|     unlock_user_struct(frame, frame_addr, 0); | ||||
|     force_sig(TARGET_SIGSEGV /* , current */); | ||||
|     return 0; | ||||
|     force_sig(TARGET_SIGSEGV); | ||||
|     return -TARGET_QEMU_ESIGRETURN; | ||||
| } | ||||
| 
 | ||||
| long do_sigreturn(CPUARMState *env) | ||||
| @ -2185,8 +2206,8 @@ static long do_rt_sigreturn_v1(CPUARMState *env) | ||||
| 
 | ||||
| badframe: | ||||
|     unlock_user_struct(frame, frame_addr, 0); | ||||
|     force_sig(TARGET_SIGSEGV /* , current */); | ||||
|     return 0; | ||||
|     force_sig(TARGET_SIGSEGV); | ||||
|     return -TARGET_QEMU_ESIGRETURN; | ||||
| } | ||||
| 
 | ||||
| static long do_rt_sigreturn_v2(CPUARMState *env) | ||||
| @ -2218,8 +2239,8 @@ static long do_rt_sigreturn_v2(CPUARMState *env) | ||||
| 
 | ||||
| badframe: | ||||
|     unlock_user_struct(frame, frame_addr, 0); | ||||
|     force_sig(TARGET_SIGSEGV /* , current */); | ||||
|     return 0; | ||||
|     force_sig(TARGET_SIGSEGV); | ||||
|     return -TARGET_QEMU_ESIGRETURN; | ||||
| } | ||||
| 
 | ||||
| long do_rt_sigreturn(CPUARMState *env) | ||||
| @ -2553,6 +2574,7 @@ long do_sigreturn(CPUSPARCState *env) | ||||
| segv_and_exit: | ||||
|     unlock_user_struct(sf, sf_addr, 0); | ||||
|     force_sig(TARGET_SIGSEGV); | ||||
|     return -TARGET_QEMU_ESIGRETURN; | ||||
| } | ||||
| 
 | ||||
| long do_rt_sigreturn(CPUSPARCState *env) | ||||
| @ -3110,8 +3132,8 @@ long do_sigreturn(CPUMIPSState *regs) | ||||
|     return -TARGET_QEMU_ESIGRETURN; | ||||
| 
 | ||||
| badframe: | ||||
|     force_sig(TARGET_SIGSEGV/*, current*/); | ||||
|     return 0; | ||||
|     force_sig(TARGET_SIGSEGV); | ||||
|     return -TARGET_QEMU_ESIGRETURN; | ||||
| } | ||||
| # endif /* O32 */ | ||||
| 
 | ||||
| @ -3207,8 +3229,8 @@ long do_rt_sigreturn(CPUMIPSState *env) | ||||
|     return -TARGET_QEMU_ESIGRETURN; | ||||
| 
 | ||||
| badframe: | ||||
|     force_sig(TARGET_SIGSEGV/*, current*/); | ||||
|     return 0; | ||||
|     force_sig(TARGET_SIGSEGV); | ||||
|     return -TARGET_QEMU_ESIGRETURN; | ||||
| } | ||||
| 
 | ||||
| #elif defined(TARGET_SH4) | ||||
| @ -3474,7 +3496,7 @@ long do_sigreturn(CPUSH4State *regs) | ||||
| badframe: | ||||
|     unlock_user_struct(frame, frame_addr, 0); | ||||
|     force_sig(TARGET_SIGSEGV); | ||||
|     return 0; | ||||
|     return -TARGET_QEMU_ESIGRETURN; | ||||
| } | ||||
| 
 | ||||
| long do_rt_sigreturn(CPUSH4State *regs) | ||||
| @ -3506,7 +3528,7 @@ long do_rt_sigreturn(CPUSH4State *regs) | ||||
| badframe: | ||||
|     unlock_user_struct(frame, frame_addr, 0); | ||||
|     force_sig(TARGET_SIGSEGV); | ||||
|     return 0; | ||||
|     return -TARGET_QEMU_ESIGRETURN; | ||||
| } | ||||
| #elif defined(TARGET_MICROBLAZE) | ||||
| 
 | ||||
| @ -3725,6 +3747,7 @@ long do_sigreturn(CPUMBState *env) | ||||
|     return -TARGET_QEMU_ESIGRETURN; | ||||
| badframe: | ||||
|     force_sig(TARGET_SIGSEGV); | ||||
|     return -TARGET_QEMU_ESIGRETURN; | ||||
| } | ||||
| 
 | ||||
| long do_rt_sigreturn(CPUMBState *env) | ||||
| @ -3892,6 +3915,7 @@ long do_sigreturn(CPUCRISState *env) | ||||
|     return -TARGET_QEMU_ESIGRETURN; | ||||
| badframe: | ||||
|     force_sig(TARGET_SIGSEGV); | ||||
|     return -TARGET_QEMU_ESIGRETURN; | ||||
| } | ||||
| 
 | ||||
| long do_rt_sigreturn(CPUCRISState *env) | ||||
| @ -4383,7 +4407,7 @@ long do_sigreturn(CPUS390XState *env) | ||||
| 
 | ||||
| badframe: | ||||
|     force_sig(TARGET_SIGSEGV); | ||||
|     return 0; | ||||
|     return -TARGET_QEMU_ESIGRETURN; | ||||
| } | ||||
| 
 | ||||
| long do_rt_sigreturn(CPUS390XState *env) | ||||
| @ -4414,7 +4438,7 @@ long do_rt_sigreturn(CPUS390XState *env) | ||||
| badframe: | ||||
|     unlock_user_struct(frame, frame_addr, 0); | ||||
|     force_sig(TARGET_SIGSEGV); | ||||
|     return 0; | ||||
|     return -TARGET_QEMU_ESIGRETURN; | ||||
| } | ||||
| 
 | ||||
| #elif defined(TARGET_PPC) | ||||
| @ -4973,7 +4997,7 @@ sigsegv: | ||||
|     unlock_user_struct(sr, sr_addr, 1); | ||||
|     unlock_user_struct(sc, sc_addr, 1); | ||||
|     force_sig(TARGET_SIGSEGV); | ||||
|     return 0; | ||||
|     return -TARGET_QEMU_ESIGRETURN; | ||||
| } | ||||
| 
 | ||||
| /* See arch/powerpc/kernel/signal_32.c.  */ | ||||
| @ -5028,7 +5052,7 @@ long do_rt_sigreturn(CPUPPCState *env) | ||||
| sigsegv: | ||||
|     unlock_user_struct(rt_sf, rt_sf_addr, 1); | ||||
|     force_sig(TARGET_SIGSEGV); | ||||
|     return 0; | ||||
|     return -TARGET_QEMU_ESIGRETURN; | ||||
| } | ||||
| 
 | ||||
| #elif defined(TARGET_M68K) | ||||
| @ -5358,7 +5382,7 @@ long do_sigreturn(CPUM68KState *env) | ||||
| 
 | ||||
| badframe: | ||||
|     force_sig(TARGET_SIGSEGV); | ||||
|     return 0; | ||||
|     return -TARGET_QEMU_ESIGRETURN; | ||||
| } | ||||
| 
 | ||||
| long do_rt_sigreturn(CPUM68KState *env) | ||||
| @ -5391,7 +5415,7 @@ long do_rt_sigreturn(CPUM68KState *env) | ||||
| badframe: | ||||
|     unlock_user_struct(frame, frame_addr, 0); | ||||
|     force_sig(TARGET_SIGSEGV); | ||||
|     return 0; | ||||
|     return -TARGET_QEMU_ESIGRETURN; | ||||
| } | ||||
| 
 | ||||
| #elif defined(TARGET_ALPHA) | ||||
| @ -5620,6 +5644,7 @@ long do_sigreturn(CPUAlphaState *env) | ||||
| 
 | ||||
| badframe: | ||||
|     force_sig(TARGET_SIGSEGV); | ||||
|     return -TARGET_QEMU_ESIGRETURN; | ||||
| } | ||||
| 
 | ||||
| long do_rt_sigreturn(CPUAlphaState *env) | ||||
| @ -5649,6 +5674,7 @@ long do_rt_sigreturn(CPUAlphaState *env) | ||||
| badframe: | ||||
|     unlock_user_struct(frame, frame_addr, 0); | ||||
|     force_sig(TARGET_SIGSEGV); | ||||
|     return -TARGET_QEMU_ESIGRETURN; | ||||
| } | ||||
| 
 | ||||
| #elif defined(TARGET_TILEGX) | ||||
| @ -5813,6 +5839,7 @@ long do_rt_sigreturn(CPUTLGState *env) | ||||
|  badframe: | ||||
|     unlock_user_struct(frame, frame_addr, 0); | ||||
|     force_sig(TARGET_SIGSEGV); | ||||
|     return -TARGET_QEMU_ESIGRETURN; | ||||
| } | ||||
| 
 | ||||
| #else | ||||
| @ -5879,12 +5906,12 @@ static void handle_pending_signal(CPUArchState *cpu_env, int sig, | ||||
|                    sig != TARGET_SIGURG && | ||||
|                    sig != TARGET_SIGWINCH && | ||||
|                    sig != TARGET_SIGCONT) { | ||||
|             force_sig(sig); | ||||
|             dump_core_and_abort(sig); | ||||
|         } | ||||
|     } else if (handler == TARGET_SIG_IGN) { | ||||
|         /* ignore sig */ | ||||
|     } else if (handler == TARGET_SIG_ERR) { | ||||
|         force_sig(sig); | ||||
|         dump_core_and_abort(sig); | ||||
|     } else { | ||||
|         /* compute the blocked signals during the handler execution */ | ||||
|         sigset_t *blocked_set; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Peter Maydell
						Peter Maydell