Disable cause-SIGFPE test on Arm processors

The way that division operations behave have changed between Armv7
and Armv8. On the later one, divisions by zero will *not* yield an
exception of any kind (for both a 32bit and 64bit app), for hardware
integer divide operation.

On Arm processors exceptions may also be a factor of:
 - if the hardware implementation supports it.
 - if the kernel has set the proper internal state registers/flags.
 - C library implementations (e.g. libgcc x clang_rt).

Aside that, a division by zero is within the realm of UD (Undefined
Behavior) in C/C++.

Since there are two categories of tests (explicit raise x caused by
instructions), it just makes sense to disable the second for Arm
since there is no reliable way to cause a SIGFPE without an explicit
raise() POSIX call.

For x86, we keep the previous implementation idea but streamlined
the code by deploying 'volatile' to ensure that the compiler
won't optimize away the result of the division (i.e no need
to call stat() and fstat()).

Bug: chromium:919548, chromium:1184398
Change-Id: Ib0fd4bdf503dcd50149dccae0577c777488c0238
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/3213431
Commit-Queue: Adenilson Cavalcanti <cavalcantii@chromium.org>
Commit-Queue: Mark Mentovai <mark@chromium.org>
Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
Adenilson Cavalcanti 2021-10-13 15:22:00 -07:00 committed by Crashpad LUCI CQ
parent 0a8985cd20
commit 07a6b70755

View File

@ -17,7 +17,6 @@
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
@ -46,9 +45,16 @@ bool CanCauseSignal(int sig) {
return sig == SIGABRT ||
sig == SIGALRM ||
sig == SIGBUS ||
#if !defined(ARCH_CPU_ARM64)
/* According to DDI0487D (Armv8 Architecture Reference Manual) the expected
* behavior for division by zero (Section 3.4.8) is: "... results in a
* zero being written to the destination register, without any
* indication that the division by zero occurred.".
* This applies to Armv8 (and not earlier) for both 32bit and 64bit app code.
*/
#if defined(ARCH_CPU_X86_FAMILY)
sig == SIGFPE ||
#endif // !defined(ARCH_CPU_ARM64)
#endif
#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARMEL)
sig == SIGILL ||
#endif // defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARMEL)
@ -115,27 +121,19 @@ void CauseSignal(int sig) {
_exit(kUnexpectedExitStatus);
}
#if !defined(ARCH_CPU_ARM64)
// ARM64 has hardware integer division instructions that dont generate a
// trap for divide-by-zero, so this doesnt produce SIGFPE.
case SIGFPE: {
// Optimization makes this tricky, so get zero from a system call likely
// to succeed, and try to do something with the result.
struct stat stat_buf;
int zero = stat("/", &stat_buf);
if (zero == -1) {
// Its important to check |== -1| and not |!= 0|. An optimizer is free
// to discard an |== 0| branch entirely, because division by zero is
// undefined behavior.
PLOG(ERROR) << "stat";
_exit(kUnexpectedExitStatus);
}
int quotient = 2 / zero;
fstat(quotient, &stat_buf);
/* Enabled only for x86, since a division by zero won't raise a signal
* on Armv8, please see comment at the top of file concerning the
* Arm architecture.
*/
#if defined(ARCH_CPU_X86_FAMILY)
volatile int a = 42;
volatile int b = 0;
a /= b;
ALLOW_UNUSED_LOCAL(a);
#endif
break;
}
#endif // ARCH_CPU_ARM64
#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARMEL)
case SIGILL: {