Files
mckernel/test/issues/1287/arm64/1287_arm64.c
Shiratori, Takehiro 33cba1ad48 test: ptrace: Record syscall return value before reporting
Change-Id: I8e9de3bb9bfa0b07eebe472131cc62b53ef5cc8b
Refs: #1287
2020-03-19 23:31:48 +00:00

260 lines
4.7 KiB
C

/* 1287_arm64.c COPYRIGHT FUJITSU LIMITED 2019 */
/* ptrace(PTRACE_SYSCALL) args and ret check testcase */
#include <stdio.h>
#include <stdlib.h>
#include <sys/ptrace.h>
#include <errno.h>
#include <sys/uio.h>
#include <asm/ptrace.h>
#include <linux/elf.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
static unsigned long __inst_addr;
#define SYSCALL_ARG0 0x11111111
#define SYSCALL_ARG1 0x22222222
#define SYSCALL_ARG2 0x33333333
#define SYSCALL_ARG3 0x44444444
#define SYSCALL_ARG4 0x55555555
#define SYSCALL_ARG5 0x66666666
#define NOP_INST 0xd503201fUL
static int rewrite_brk_inst(pid_t cpid, void *inst_addr)
{
unsigned long addr = 0;
const unsigned long inst = ((NOP_INST << 32UL) | NOP_INST);
/* read child brk address */
addr = ptrace(PTRACE_PEEKDATA, cpid, inst_addr, NULL);
if ((addr == -1) && errno) {
perror("ptrace(PTRACE_PEEKDATA)");
return -1;
}
/* write nop instruction */
if (ptrace(PTRACE_POKETEXT, cpid, addr, inst)) {
perror("ptrace(PTRACE_POKETEXT)");
return -1;
}
return 0;
}
static int get_check_regs(pid_t cpid, int regnum, long exp)
{
int ret = -1;
struct iovec iov;
struct user_pt_regs gregs;
if (regnum < 0 || 30 < regnum) {
printf("regnum=%d invalid. (0 <= regnum <= 30)\n", regnum);
goto out;
}
/* read child regs (REGSET_GPR) */
iov.iov_base = &gregs;
iov.iov_len = sizeof(gregs);
if (ptrace(PTRACE_GETREGSET, cpid, NT_PRSTATUS, &iov)) {
perror("ptrace(PTRACE_GETREGSET, NT_PRSTATUS)");
goto out;
}
/* check */
if (gregs.regs[regnum] != exp) {
printf("reg[%d] check NG.\n", regnum);
printf("expection:0x%lx readsysno:0x%llx\n",
exp, gregs.regs[regnum]);
goto out;
}
/* success */
ret = 0;
out:
return ret;
}
static int child_func(void)
{
int ret = -1;
/* send PTRACE_TRACEME */
if (ptrace(PTRACE_TRACEME, 0, NULL, NULL)) {
perror("ptrace(PTRACE_TRACEME)");
goto out;
}
/* stop mine, brk instruction */
/* rewrite nop from parent process */
asm volatile(
"adr x10, 1f\n"
"str x10, [%0]\n"
"nop\n"
"nop\n"
"1:\n"
"brk #0\n"
"nop\n"
: /* nothing */
: "r"(&__inst_addr)
: "x10"
);
/* send magicno syscall */
syscall(__NR_getpid, SYSCALL_ARG0, SYSCALL_ARG1, SYSCALL_ARG2,
SYSCALL_ARG3, SYSCALL_ARG4, SYSCALL_ARG5);
/* success */
ret = 0;
out:
return ret;
}
static int parent_func(pid_t cpid)
{
pid_t pid = 0;
int status = 0;
int ret = -1;
int i = 0;
const long syscall_args[] = {
SYSCALL_ARG0, SYSCALL_ARG1, SYSCALL_ARG2,
SYSCALL_ARG3, SYSCALL_ARG4, SYSCALL_ARG5
};
/* wait child stop */
pid = wait(&status);
if (pid == cpid) {
if (!WIFSTOPPED(status)) {
printf("child is not stopped.\n");
goto out;
}
}
else {
perror("wait()");
goto out;
}
/* rewrite child brk instruction */
if (rewrite_brk_inst(cpid, &__inst_addr)) {
goto cont;
}
/* child continue (until next syscall enter) */
if (ptrace(PTRACE_SYSCALL, cpid, NULL, NULL)) {
perror("ptrace(PTRACE_SYSCALL)");
goto cont;
}
/* wait child stop */
pid = wait(&status);
if (pid == cpid) {
if (!WIFSTOPPED(status)) {
printf("child is not stopped.\n");
goto out;
}
}
else {
perror("wait()");
goto out;
}
/* check syscall arguments */
for (i = 0; i < sizeof(syscall_args) /
sizeof(syscall_args[0]); i++) {
if (get_check_regs(cpid, i, syscall_args[i]) != 0) {
printf("syscall enter regs[%d] NG.\n", i);
goto out;
}
}
/* child continue (until syscall exit) */
if (ptrace(PTRACE_SYSCALL, cpid, NULL, NULL)) {
perror("ptrace(PTRACE_SYSCALL)");
goto cont;
}
/* wait child stop */
pid = wait(&status);
if (pid == cpid) {
if (!WIFSTOPPED(status)) {
printf("child is not stopped.\n");
goto out;
}
}
else {
perror("wait()");
goto out;
}
/* check syscall return */
if (get_check_regs(cpid, 0, cpid) != 0) {
printf("syscall exit return val NG.\n");
goto cont;
}
/* success */
ret = 0;
cont:
/* child continue */
if (ptrace(PTRACE_CONT, cpid, NULL, NULL)) {
perror("ptrace(PTRACE_CONT)");
ret = -1;
}
out:
return ret;
}
int main(int argc, char *argv[])
{
pid_t pid = 0;
int ret = -1;
int func_ret = 0;
int status = 0;
/* create child process */
pid = fork();
switch (pid) {
case -1:
/* fork() error. */
perror("fork()");
goto out;
case 0:
/* child process */
func_ret = child_func();
/* child exit */
exit(func_ret);
break;
default:
/* parent process */
func_ret = parent_func(pid);
/* wait child */
pid = wait(&status);
if (pid != -1) {
if (WEXITSTATUS(status)) {
printf("WEXITSTATUS() is not 0.\n");
goto out;
}
}
else {
perror("wait()");
goto out;
}
/* parent_func check */
if (func_ret) {
goto out;
}
break;
}
/* sccess. */
ret = 0;
out:
return ret;
}