You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
363 lines
9.3 KiB
363 lines
9.3 KiB
/*
|
|
* Copyright 2002-2019 Intel Corporation.
|
|
*
|
|
* This software is provided to you as Sample Source Code as defined in the accompanying
|
|
* End User License Agreement for the Intel(R) Software Development Products ("Agreement")
|
|
* section 1.L.
|
|
*
|
|
* This software and the related documents are provided as is, with no express or implied
|
|
* warranties, other than those that are expressly stated in the License.
|
|
*/
|
|
|
|
/*!
|
|
* This application causes various "complex" instructions to fault, and then
|
|
* verifies that the fault was received at the expected location. It also
|
|
* sets all the registers to known values before the fault, and then verifies
|
|
* that the signal context contains the expected values of those registers.
|
|
*
|
|
* The "complex" instructions are chosen to match those instructions that
|
|
* Pin needs to translate into instruction sequences. Therefore, this is a
|
|
* good test that Pin correctly propagated the instruction attributes when
|
|
* doing the translation, so that the fault is later recognized as being on
|
|
* an "application" instruction (and not a Pin internal or tool instruction).
|
|
*
|
|
* It's also a good test of Pin's support for recovering the original program
|
|
* state at the location of the fault.
|
|
*/
|
|
|
|
#include <iostream>
|
|
#include <cstdlib>
|
|
#include <cerrno>
|
|
#include <cassert>
|
|
#include <signal.h>
|
|
#include <setjmp.h>
|
|
#include <sys/mman.h>
|
|
|
|
|
|
// Possible results from running a single test.
|
|
//
|
|
enum TSTATUS
|
|
{
|
|
TSTATUS_NOFAULT, // Test did not raise a fault
|
|
TSTATUS_DONE // There are no more tests to run
|
|
};
|
|
|
|
static sigjmp_buf JumpBuffer;
|
|
static unsigned TestNumber = 0;
|
|
static bool IsError = false;
|
|
|
|
static bool Initialize();
|
|
static void Handle(int, siginfo_t *, void *);
|
|
static TSTATUS DoTest(unsigned);
|
|
|
|
|
|
extern bool CheckUContextRegisters(void *);
|
|
extern "C" void DoFaultRetSp();
|
|
extern "C" void DoFaultRetTarg();
|
|
extern "C" void DoFaultRetImmSp();
|
|
extern "C" void DoFaultRetImmTarg();
|
|
extern "C" void DoFaultCallSp();
|
|
extern "C" void DoFaultCallTarg();
|
|
extern "C" void DoFaultCallRegSp();
|
|
extern "C" void DoFaultCallRegTarg();
|
|
extern "C" void DoFaultCallMemSp();
|
|
extern "C" void DoFaultCallMemTarg();
|
|
extern "C" void DoFaultCallMemBadMem();
|
|
extern "C" void DoFaultSegMov();
|
|
extern "C" void DoFaultStringOp();
|
|
extern "C" void DoFaultPushF();
|
|
extern "C" void DoFaultPopF();
|
|
extern "C" void DoFaultPush();
|
|
extern "C" void DoFaultPop();
|
|
extern "C" void DoFaultPushMem();
|
|
extern "C" void DoFaultPopMem();
|
|
extern "C" void DoFaultEnter();
|
|
extern "C" void DoFaultLeave();
|
|
extern "C" void DoFaultMaskmovdqu();
|
|
extern "C" void DoFaultBitTest();
|
|
extern "C" void DoFaultMovSegSelector();
|
|
extern "C" void DoFaultJumpMemBadMem();
|
|
extern "C" void DoFaultBadLoad();
|
|
extern "C" void DoFaultBadStore();
|
|
extern "C" void DoFaultCmov();
|
|
extern "C" char Unmapped;
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
if (!Initialize())
|
|
{
|
|
std::cerr << "Error during initializations\n";
|
|
return 1;
|
|
}
|
|
|
|
// You can choose to run just a single test by selecting the test
|
|
// number on the command line.
|
|
//
|
|
bool doOneTest = false;
|
|
unsigned oneTest;
|
|
if (argc > 2)
|
|
{
|
|
std::cerr << "Usage: " << argv[0] << " [test-number]\n";
|
|
return 1;
|
|
}
|
|
if (argc == 2)
|
|
{
|
|
errno = 0;
|
|
oneTest = std::strtoul(argv[1], 0, 10);
|
|
if (errno)
|
|
{
|
|
std::cerr << "Invalid test number " << argv[1] << "\n";
|
|
return 1;
|
|
}
|
|
doOneTest = true;
|
|
TestNumber = oneTest;
|
|
}
|
|
|
|
// Since each test causes a fault, save the state here. The fault
|
|
// handler jumps back here after each test.
|
|
//
|
|
sigsetjmp(JumpBuffer, 1);
|
|
if (doOneTest && TestNumber != oneTest)
|
|
return (IsError) ? 1 : 0;
|
|
|
|
// Run all the tests.
|
|
//
|
|
for (;;)
|
|
{
|
|
std::cout << "Starting test " << std::dec << TestNumber << " ..." << std::endl;
|
|
switch (DoTest(TestNumber))
|
|
{
|
|
case TSTATUS_DONE:
|
|
std::cout << " Last test" << std::endl;
|
|
return (IsError) ? 1 : 0;
|
|
case TSTATUS_NOFAULT:
|
|
std::cout << " *** Failed to raise signal" << std::endl;
|
|
IsError = true;
|
|
break;
|
|
default:
|
|
assert(0);
|
|
break;
|
|
}
|
|
|
|
TestNumber++;
|
|
if (doOneTest)
|
|
return (IsError) ? 1 : 0;
|
|
}
|
|
|
|
assert(0);
|
|
return 1;
|
|
}
|
|
|
|
|
|
static bool Initialize()
|
|
{
|
|
// Set up a signal handler using an alternate stack. Some of the
|
|
// tests corrupt $SP, so we can't use the normal stack for the
|
|
// handler.
|
|
//
|
|
stack_t ss;
|
|
ss.ss_sp = new char[SIGSTKSZ];
|
|
ss.ss_flags = 0;
|
|
ss.ss_size = SIGSTKSZ;
|
|
if (sigaltstack(&ss, 0) != 0)
|
|
return false;
|
|
|
|
struct sigaction act;
|
|
act.sa_sigaction = Handle;
|
|
act.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
|
sigemptyset(&act.sa_mask);
|
|
if (sigaction(SIGSEGV, &act, 0) != 0)
|
|
return false;
|
|
|
|
// There is a page in the assembly file that should be unmapped.
|
|
//
|
|
void *ptr = &Unmapped;
|
|
if (munmap(ptr, 4096) != 0)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// This is the fault handler.
|
|
//
|
|
static void Handle(int sig, siginfo_t *, void *uctxt)
|
|
{
|
|
// Make sure we got the expected signal.
|
|
//
|
|
if (sig == SIGSEGV)
|
|
{
|
|
std::cout << " Received SEGV";
|
|
}
|
|
else
|
|
{
|
|
std::cout << " *** Received unexpected signal " << std::dec << sig;
|
|
IsError = true;
|
|
}
|
|
std::cout << "\n";
|
|
|
|
// Make sure the fault happened at the expected PC. Make sure the
|
|
// other regiters in the fault context have the expected values.
|
|
//
|
|
if (!CheckUContextRegisters(uctxt))
|
|
IsError = true;
|
|
|
|
// Advance to the next test.
|
|
//
|
|
TestNumber++;
|
|
siglongjmp(JumpBuffer, 1);
|
|
}
|
|
|
|
|
|
// Do a specific test.
|
|
//
|
|
static TSTATUS DoTest(unsigned num)
|
|
{
|
|
switch (num)
|
|
{
|
|
case 0:
|
|
std::cout << " Fault on RET (bad SP)\n";
|
|
DoFaultRetSp();
|
|
return TSTATUS_NOFAULT;
|
|
|
|
case 1:
|
|
std::cout << " Fault on RET (bad target)\n";
|
|
DoFaultRetTarg();
|
|
return TSTATUS_NOFAULT;
|
|
|
|
case 2:
|
|
std::cout << " Fault on RET <imm> (bad SP)\n";
|
|
DoFaultRetImmSp();
|
|
return TSTATUS_NOFAULT;
|
|
|
|
case 3:
|
|
std::cout << " Fault on RET <imm> (bad targ)\n";
|
|
DoFaultRetImmTarg();
|
|
return TSTATUS_NOFAULT;
|
|
|
|
case 4:
|
|
std::cout << " Fault on CALL <imm> (bad SP)\n";
|
|
DoFaultCallSp();
|
|
return TSTATUS_NOFAULT;
|
|
|
|
case 5:
|
|
std::cout << " Fault on CALL <imm> (bad target)\n";
|
|
DoFaultCallTarg();
|
|
return TSTATUS_NOFAULT;
|
|
|
|
case 6:
|
|
std::cout << " Fault on CALL *%rx (bad SP)\n";
|
|
DoFaultCallRegSp();
|
|
return TSTATUS_NOFAULT;
|
|
|
|
case 7:
|
|
std::cout << " Fault on CALL *%rx (bad target)\n";
|
|
DoFaultCallRegTarg();
|
|
return TSTATUS_NOFAULT;
|
|
|
|
case 8:
|
|
std::cout << " Fault on CALL *[mem] (bad SP)\n";
|
|
DoFaultCallMemSp();
|
|
return TSTATUS_NOFAULT;
|
|
|
|
case 9:
|
|
std::cout << " Fault on CALL *[mem] (bad target)\n";
|
|
DoFaultCallMemTarg();
|
|
return TSTATUS_NOFAULT;
|
|
|
|
case 10:
|
|
std::cout << " Fault on CALL *[mem] (bad mem location)\n";
|
|
DoFaultCallMemBadMem();
|
|
return TSTATUS_NOFAULT;
|
|
|
|
case 11:
|
|
std::cout << " Fault on MOV %gs:[mem], %rx (bad segment selector)\n";
|
|
DoFaultSegMov();
|
|
return TSTATUS_NOFAULT;
|
|
|
|
case 12:
|
|
std::cout << " Fault on MOVS\n";
|
|
DoFaultStringOp();
|
|
return TSTATUS_NOFAULT;
|
|
|
|
case 13:
|
|
std::cout << " Fault on PUSHF\n";
|
|
DoFaultPushF();
|
|
return TSTATUS_NOFAULT;
|
|
|
|
case 14:
|
|
std::cout << " Fault on POPF\n";
|
|
DoFaultPopF();
|
|
return TSTATUS_NOFAULT;
|
|
|
|
case 15:
|
|
std::cout << " Fault on PUSH\n";
|
|
DoFaultPush();
|
|
return TSTATUS_NOFAULT;
|
|
|
|
case 16:
|
|
std::cout << " Fault on POP\n";
|
|
DoFaultPop();
|
|
return TSTATUS_NOFAULT;
|
|
|
|
case 17:
|
|
std::cout << " Fault on PUSH [mem] (bad mem location)\n";
|
|
DoFaultPushMem();
|
|
return TSTATUS_NOFAULT;
|
|
|
|
case 18:
|
|
std::cout << " Fault on POP [mem] (bad mem location)\n";
|
|
DoFaultPopMem();
|
|
return TSTATUS_NOFAULT;
|
|
|
|
case 19:
|
|
std::cout << " Fault on ENTER\n";
|
|
DoFaultEnter();
|
|
return TSTATUS_NOFAULT;
|
|
|
|
case 20:
|
|
std::cout << " Fault on LEAVE\n";
|
|
DoFaultLeave();
|
|
return TSTATUS_NOFAULT;
|
|
|
|
case 21:
|
|
std::cout << " Fault on MASKMOVDQU\n";
|
|
DoFaultMaskmovdqu();
|
|
return TSTATUS_NOFAULT;
|
|
|
|
case 22:
|
|
std::cout << " Fault on BT [mem]\n";
|
|
DoFaultBitTest();
|
|
return TSTATUS_NOFAULT;
|
|
|
|
case 23:
|
|
std::cout << " Fault on MOV %seg, [mem] (bad mem location)\n";
|
|
DoFaultMovSegSelector();
|
|
return TSTATUS_NOFAULT;
|
|
|
|
case 24:
|
|
std::cout << " Fault on JMP *[mem] (bad mem location)\n";
|
|
DoFaultJumpMemBadMem();
|
|
return TSTATUS_NOFAULT;
|
|
|
|
case 25:
|
|
std::cout << " Fault on MOV [mem], %rx (bad load location)\n";
|
|
DoFaultBadLoad();
|
|
return TSTATUS_NOFAULT;
|
|
|
|
case 26:
|
|
std::cout << " Fault on MOV %rx, [mem] (bad store location)\n";
|
|
DoFaultBadStore();
|
|
return TSTATUS_NOFAULT;
|
|
|
|
case 27:
|
|
std::cout << " Fault on CMOV [mem], %rx (bad load location)\n";
|
|
DoFaultCmov();
|
|
return TSTATUS_NOFAULT;
|
|
|
|
default:
|
|
return TSTATUS_DONE;
|
|
}
|
|
}
|