|
From: <sv...@va...> - 2013-10-15 21:05:08
|
Author: philippe
Date: Tue Oct 15 21:04:56 2013
New Revision: 13648
Log:
SIGSEGV/SIGBUS specific handling on s390, to cope with kernel peculiarity
On s390, the linux kernel does not deliver the exact address that
caused a SEGV but rather this address rounded down to a page boundary.
Also, old kernels do not deliver the address that caused a SIGBUS,
but deliver 0.
So, on s390, handle SIGSEGV and SIGBUG by always skipping one page
starting from the current value of the ptr scanning position.
As we need a correct value of ptr on this platform in the longjmp-ed
code, ptr is marked as volatile.
Modified:
trunk/memcheck/mc_leakcheck.c
Modified: trunk/memcheck/mc_leakcheck.c
==============================================================================
--- trunk/memcheck/mc_leakcheck.c (original)
+++ trunk/memcheck/mc_leakcheck.c Tue Oct 15 21:04:56 2013
@@ -945,6 +945,11 @@
end portions of the block if they are not aligned on sizeof(Addr):
These cannot be a valid pointer, and calls to MC_(is_valid_aligned_word)
will assert for a non aligned address. */
+#if defined(VGA_s390x)
+ // Define ptr as volatile, as on this platform, the value of ptr
+ // is read in code executed via a longjmp.
+ volatile
+#endif
Addr ptr = VG_ROUNDUP(start, sizeof(Addr));
const Addr end = VG_ROUNDDN(start+len, sizeof(Addr));
vki_sigset_t sigmask;
@@ -993,10 +998,22 @@
// We need to restore the signal mask, because we were
// longjmped out of a signal handler.
VG_(sigprocmask)(VKI_SIG_SETMASK, &sigmask, NULL);
+# if defined(VGA_s390x)
+ // For a SIGSEGV, s390 delivers the page address of the bad address.
+ // For a SIGBUS, old s390 kernels deliver a NULL address.
+ // bad_scanned_addr can thus not be used.
+ // So, on this platform, we always skip a full page from ptr.
+ // The below implies to mark ptr as volatile, as we read the value
+ // after a longjmp to here.
+ lc_sig_skipped_szB += VKI_PAGE_SIZE;
+ ptr = ptr + VKI_PAGE_SIZE; // Unaddressable, - skip it.
+# else
+ // On other platforms, just skip one Addr.
lc_sig_skipped_szB += sizeof(Addr);
tl_assert(bad_scanned_addr >= VG_ROUNDUP(start, sizeof(Addr)));
tl_assert(bad_scanned_addr < VG_ROUNDDN(start+len, sizeof(Addr)));
ptr = bad_scanned_addr + sizeof(Addr); // Unaddressable, - skip it.
+#endif
}
while (ptr < end) {
Addr addr;
|