cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

get_syscall_info.c (6555B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * Copyright (c) 2018 Dmitry V. Levin <ldv@altlinux.org>
      4 * All rights reserved.
      5 *
      6 * Check whether PTRACE_GET_SYSCALL_INFO semantics implemented in the kernel
      7 * matches userspace expectations.
      8 */
      9
     10#include "../kselftest_harness.h"
     11#include <err.h>
     12#include <signal.h>
     13#include <asm/unistd.h>
     14#include "linux/ptrace.h"
     15
     16static int
     17kill_tracee(pid_t pid)
     18{
     19	if (!pid)
     20		return 0;
     21
     22	int saved_errno = errno;
     23
     24	int rc = kill(pid, SIGKILL);
     25
     26	errno = saved_errno;
     27	return rc;
     28}
     29
     30static long
     31sys_ptrace(int request, pid_t pid, unsigned long addr, unsigned long data)
     32{
     33	return syscall(__NR_ptrace, request, pid, addr, data);
     34}
     35
     36#define LOG_KILL_TRACEE(fmt, ...)				\
     37	do {							\
     38		kill_tracee(pid);				\
     39		TH_LOG("wait #%d: " fmt,			\
     40		       ptrace_stop, ##__VA_ARGS__);		\
     41	} while (0)
     42
     43TEST(get_syscall_info)
     44{
     45	static const unsigned long args[][7] = {
     46		/* a sequence of architecture-agnostic syscalls */
     47		{
     48			__NR_chdir,
     49			(unsigned long) "",
     50			0xbad1fed1,
     51			0xbad2fed2,
     52			0xbad3fed3,
     53			0xbad4fed4,
     54			0xbad5fed5
     55		},
     56		{
     57			__NR_gettid,
     58			0xcaf0bea0,
     59			0xcaf1bea1,
     60			0xcaf2bea2,
     61			0xcaf3bea3,
     62			0xcaf4bea4,
     63			0xcaf5bea5
     64		},
     65		{
     66			__NR_exit_group,
     67			0,
     68			0xfac1c0d1,
     69			0xfac2c0d2,
     70			0xfac3c0d3,
     71			0xfac4c0d4,
     72			0xfac5c0d5
     73		}
     74	};
     75	const unsigned long *exp_args;
     76
     77	pid_t pid = fork();
     78
     79	ASSERT_LE(0, pid) {
     80		TH_LOG("fork: %m");
     81	}
     82
     83	if (pid == 0) {
     84		/* get the pid before PTRACE_TRACEME */
     85		pid = getpid();
     86		ASSERT_EQ(0, sys_ptrace(PTRACE_TRACEME, 0, 0, 0)) {
     87			TH_LOG("PTRACE_TRACEME: %m");
     88		}
     89		ASSERT_EQ(0, kill(pid, SIGSTOP)) {
     90			/* cannot happen */
     91			TH_LOG("kill SIGSTOP: %m");
     92		}
     93		for (unsigned int i = 0; i < ARRAY_SIZE(args); ++i) {
     94			syscall(args[i][0],
     95				args[i][1], args[i][2], args[i][3],
     96				args[i][4], args[i][5], args[i][6]);
     97		}
     98		/* unreachable */
     99		_exit(1);
    100	}
    101
    102	const struct {
    103		unsigned int is_error;
    104		int rval;
    105	} *exp_param, exit_param[] = {
    106		{ 1, -ENOENT },	/* chdir */
    107		{ 0, pid }	/* gettid */
    108	};
    109
    110	unsigned int ptrace_stop;
    111
    112	for (ptrace_stop = 0; ; ++ptrace_stop) {
    113		struct ptrace_syscall_info info = {
    114			.op = 0xff	/* invalid PTRACE_SYSCALL_INFO_* op */
    115		};
    116		const size_t size = sizeof(info);
    117		const int expected_none_size =
    118			(void *) &info.entry - (void *) &info;
    119		const int expected_entry_size =
    120			(void *) &info.entry.args[6] - (void *) &info;
    121		const int expected_exit_size =
    122			(void *) (&info.exit.is_error + 1) -
    123			(void *) &info;
    124		int status;
    125		long rc;
    126
    127		ASSERT_EQ(pid, wait(&status)) {
    128			/* cannot happen */
    129			LOG_KILL_TRACEE("wait: %m");
    130		}
    131		if (WIFEXITED(status)) {
    132			pid = 0;	/* the tracee is no more */
    133			ASSERT_EQ(0, WEXITSTATUS(status));
    134			break;
    135		}
    136		ASSERT_FALSE(WIFSIGNALED(status)) {
    137			pid = 0;	/* the tracee is no more */
    138			LOG_KILL_TRACEE("unexpected signal %u",
    139					WTERMSIG(status));
    140		}
    141		ASSERT_TRUE(WIFSTOPPED(status)) {
    142			/* cannot happen */
    143			LOG_KILL_TRACEE("unexpected wait status %#x", status);
    144		}
    145
    146		switch (WSTOPSIG(status)) {
    147		case SIGSTOP:
    148			ASSERT_EQ(0, ptrace_stop) {
    149				LOG_KILL_TRACEE("unexpected signal stop");
    150			}
    151			ASSERT_EQ(0, sys_ptrace(PTRACE_SETOPTIONS, pid, 0,
    152						PTRACE_O_TRACESYSGOOD)) {
    153				LOG_KILL_TRACEE("PTRACE_SETOPTIONS: %m");
    154			}
    155			ASSERT_LT(0, (rc = sys_ptrace(PTRACE_GET_SYSCALL_INFO,
    156						      pid, size,
    157						      (unsigned long) &info))) {
    158				LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO: %m");
    159			}
    160			ASSERT_EQ(expected_none_size, rc) {
    161				LOG_KILL_TRACEE("signal stop mismatch");
    162			}
    163			ASSERT_EQ(PTRACE_SYSCALL_INFO_NONE, info.op) {
    164				LOG_KILL_TRACEE("signal stop mismatch");
    165			}
    166			ASSERT_TRUE(info.arch) {
    167				LOG_KILL_TRACEE("signal stop mismatch");
    168			}
    169			ASSERT_TRUE(info.instruction_pointer) {
    170				LOG_KILL_TRACEE("signal stop mismatch");
    171			}
    172			ASSERT_TRUE(info.stack_pointer) {
    173				LOG_KILL_TRACEE("signal stop mismatch");
    174			}
    175			break;
    176
    177		case SIGTRAP | 0x80:
    178			ASSERT_LT(0, (rc = sys_ptrace(PTRACE_GET_SYSCALL_INFO,
    179						      pid, size,
    180						      (unsigned long) &info))) {
    181				LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO: %m");
    182			}
    183			switch (ptrace_stop) {
    184			case 1: /* entering chdir */
    185			case 3: /* entering gettid */
    186			case 5: /* entering exit_group */
    187				exp_args = args[ptrace_stop / 2];
    188				ASSERT_EQ(expected_entry_size, rc) {
    189					LOG_KILL_TRACEE("entry stop mismatch");
    190				}
    191				ASSERT_EQ(PTRACE_SYSCALL_INFO_ENTRY, info.op) {
    192					LOG_KILL_TRACEE("entry stop mismatch");
    193				}
    194				ASSERT_TRUE(info.arch) {
    195					LOG_KILL_TRACEE("entry stop mismatch");
    196				}
    197				ASSERT_TRUE(info.instruction_pointer) {
    198					LOG_KILL_TRACEE("entry stop mismatch");
    199				}
    200				ASSERT_TRUE(info.stack_pointer) {
    201					LOG_KILL_TRACEE("entry stop mismatch");
    202				}
    203				ASSERT_EQ(exp_args[0], info.entry.nr) {
    204					LOG_KILL_TRACEE("entry stop mismatch");
    205				}
    206				ASSERT_EQ(exp_args[1], info.entry.args[0]) {
    207					LOG_KILL_TRACEE("entry stop mismatch");
    208				}
    209				ASSERT_EQ(exp_args[2], info.entry.args[1]) {
    210					LOG_KILL_TRACEE("entry stop mismatch");
    211				}
    212				ASSERT_EQ(exp_args[3], info.entry.args[2]) {
    213					LOG_KILL_TRACEE("entry stop mismatch");
    214				}
    215				ASSERT_EQ(exp_args[4], info.entry.args[3]) {
    216					LOG_KILL_TRACEE("entry stop mismatch");
    217				}
    218				ASSERT_EQ(exp_args[5], info.entry.args[4]) {
    219					LOG_KILL_TRACEE("entry stop mismatch");
    220				}
    221				ASSERT_EQ(exp_args[6], info.entry.args[5]) {
    222					LOG_KILL_TRACEE("entry stop mismatch");
    223				}
    224				break;
    225			case 2: /* exiting chdir */
    226			case 4: /* exiting gettid */
    227				exp_param = &exit_param[ptrace_stop / 2 - 1];
    228				ASSERT_EQ(expected_exit_size, rc) {
    229					LOG_KILL_TRACEE("exit stop mismatch");
    230				}
    231				ASSERT_EQ(PTRACE_SYSCALL_INFO_EXIT, info.op) {
    232					LOG_KILL_TRACEE("exit stop mismatch");
    233				}
    234				ASSERT_TRUE(info.arch) {
    235					LOG_KILL_TRACEE("exit stop mismatch");
    236				}
    237				ASSERT_TRUE(info.instruction_pointer) {
    238					LOG_KILL_TRACEE("exit stop mismatch");
    239				}
    240				ASSERT_TRUE(info.stack_pointer) {
    241					LOG_KILL_TRACEE("exit stop mismatch");
    242				}
    243				ASSERT_EQ(exp_param->is_error,
    244					  info.exit.is_error) {
    245					LOG_KILL_TRACEE("exit stop mismatch");
    246				}
    247				ASSERT_EQ(exp_param->rval, info.exit.rval) {
    248					LOG_KILL_TRACEE("exit stop mismatch");
    249				}
    250				break;
    251			default:
    252				LOG_KILL_TRACEE("unexpected syscall stop");
    253				abort();
    254			}
    255			break;
    256
    257		default:
    258			LOG_KILL_TRACEE("unexpected stop signal %#x",
    259					WSTOPSIG(status));
    260			abort();
    261		}
    262
    263		ASSERT_EQ(0, sys_ptrace(PTRACE_SYSCALL, pid, 0, 0)) {
    264			LOG_KILL_TRACEE("PTRACE_SYSCALL: %m");
    265		}
    266	}
    267
    268	ASSERT_EQ(ARRAY_SIZE(args) * 2, ptrace_stop);
    269}
    270
    271TEST_HARNESS_MAIN