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

breakpoint_test.c (7793B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (C) 2011 Red Hat, Inc., Frederic Weisbecker <fweisbec@redhat.com>
      4 *
      5 * Selftests for breakpoints (and more generally the do_debug() path) in x86.
      6 */
      7
      8
      9#include <sys/ptrace.h>
     10#include <unistd.h>
     11#include <stddef.h>
     12#include <sys/user.h>
     13#include <stdio.h>
     14#include <stdlib.h>
     15#include <signal.h>
     16#include <sys/types.h>
     17#include <sys/wait.h>
     18#include <errno.h>
     19#include <string.h>
     20
     21#include "../kselftest.h"
     22
     23#define COUNT_ISN_BPS	4
     24#define COUNT_WPS	4
     25
     26/* Breakpoint access modes */
     27enum {
     28	BP_X = 1,
     29	BP_RW = 2,
     30	BP_W = 4,
     31};
     32
     33static pid_t child_pid;
     34
     35/*
     36 * Ensures the child and parent are always "talking" about
     37 * the same test sequence. (ie: that we haven't forgotten
     38 * to call check_trapped() somewhere).
     39 */
     40static int nr_tests;
     41
     42static void set_breakpoint_addr(void *addr, int n)
     43{
     44	int ret;
     45
     46	ret = ptrace(PTRACE_POKEUSER, child_pid,
     47		     offsetof(struct user, u_debugreg[n]), addr);
     48	if (ret)
     49		ksft_exit_fail_msg("Can't set breakpoint addr: %s\n",
     50			strerror(errno));
     51}
     52
     53static void toggle_breakpoint(int n, int type, int len,
     54			      int local, int global, int set)
     55{
     56	int ret;
     57
     58	int xtype, xlen;
     59	unsigned long vdr7, dr7;
     60
     61	switch (type) {
     62	case BP_X:
     63		xtype = 0;
     64		break;
     65	case BP_W:
     66		xtype = 1;
     67		break;
     68	case BP_RW:
     69		xtype = 3;
     70		break;
     71	}
     72
     73	switch (len) {
     74	case 1:
     75		xlen = 0;
     76		break;
     77	case 2:
     78		xlen = 4;
     79		break;
     80	case 4:
     81		xlen = 0xc;
     82		break;
     83	case 8:
     84		xlen = 8;
     85		break;
     86	}
     87
     88	dr7 = ptrace(PTRACE_PEEKUSER, child_pid,
     89		     offsetof(struct user, u_debugreg[7]), 0);
     90
     91	vdr7 = (xlen | xtype) << 16;
     92	vdr7 <<= 4 * n;
     93
     94	if (local) {
     95		vdr7 |= 1 << (2 * n);
     96		vdr7 |= 1 << 8;
     97	}
     98	if (global) {
     99		vdr7 |= 2 << (2 * n);
    100		vdr7 |= 1 << 9;
    101	}
    102
    103	if (set)
    104		dr7 |= vdr7;
    105	else
    106		dr7 &= ~vdr7;
    107
    108	ret = ptrace(PTRACE_POKEUSER, child_pid,
    109		     offsetof(struct user, u_debugreg[7]), dr7);
    110	if (ret) {
    111		ksft_print_msg("Can't set dr7: %s\n", strerror(errno));
    112		exit(-1);
    113	}
    114}
    115
    116/* Dummy variables to test read/write accesses */
    117static unsigned long long dummy_var[4];
    118
    119/* Dummy functions to test execution accesses */
    120static void dummy_func(void) { }
    121static void dummy_func1(void) { }
    122static void dummy_func2(void) { }
    123static void dummy_func3(void) { }
    124
    125static void (*dummy_funcs[])(void) = {
    126	dummy_func,
    127	dummy_func1,
    128	dummy_func2,
    129	dummy_func3,
    130};
    131
    132static int trapped;
    133
    134static void check_trapped(void)
    135{
    136	/*
    137	 * If we haven't trapped, wake up the parent
    138	 * so that it notices the failure.
    139	 */
    140	if (!trapped)
    141		kill(getpid(), SIGUSR1);
    142	trapped = 0;
    143
    144	nr_tests++;
    145}
    146
    147static void write_var(int len)
    148{
    149	char *pcval; short *psval; int *pival; long long *plval;
    150	int i;
    151
    152	for (i = 0; i < 4; i++) {
    153		switch (len) {
    154		case 1:
    155			pcval = (char *)&dummy_var[i];
    156			*pcval = 0xff;
    157			break;
    158		case 2:
    159			psval = (short *)&dummy_var[i];
    160			*psval = 0xffff;
    161			break;
    162		case 4:
    163			pival = (int *)&dummy_var[i];
    164			*pival = 0xffffffff;
    165			break;
    166		case 8:
    167			plval = (long long *)&dummy_var[i];
    168			*plval = 0xffffffffffffffffLL;
    169			break;
    170		}
    171		check_trapped();
    172	}
    173}
    174
    175static void read_var(int len)
    176{
    177	char cval; short sval; int ival; long long lval;
    178	int i;
    179
    180	for (i = 0; i < 4; i++) {
    181		switch (len) {
    182		case 1:
    183			cval = *(char *)&dummy_var[i];
    184			break;
    185		case 2:
    186			sval = *(short *)&dummy_var[i];
    187			break;
    188		case 4:
    189			ival = *(int *)&dummy_var[i];
    190			break;
    191		case 8:
    192			lval = *(long long *)&dummy_var[i];
    193			break;
    194		}
    195		check_trapped();
    196	}
    197}
    198
    199/*
    200 * Do the r/w/x accesses to trigger the breakpoints. And run
    201 * the usual traps.
    202 */
    203static void trigger_tests(void)
    204{
    205	int len, local, global, i;
    206	char val;
    207	int ret;
    208
    209	ret = ptrace(PTRACE_TRACEME, 0, NULL, 0);
    210	if (ret) {
    211		ksft_print_msg("Can't be traced? %s\n", strerror(errno));
    212		return;
    213	}
    214
    215	/* Wake up father so that it sets up the first test */
    216	kill(getpid(), SIGUSR1);
    217
    218	/* Test instruction breakpoints */
    219	for (local = 0; local < 2; local++) {
    220		for (global = 0; global < 2; global++) {
    221			if (!local && !global)
    222				continue;
    223
    224			for (i = 0; i < COUNT_ISN_BPS; i++) {
    225				dummy_funcs[i]();
    226				check_trapped();
    227			}
    228		}
    229	}
    230
    231	/* Test write watchpoints */
    232	for (len = 1; len <= sizeof(long); len <<= 1) {
    233		for (local = 0; local < 2; local++) {
    234			for (global = 0; global < 2; global++) {
    235				if (!local && !global)
    236					continue;
    237				write_var(len);
    238			}
    239		}
    240	}
    241
    242	/* Test read/write watchpoints (on read accesses) */
    243	for (len = 1; len <= sizeof(long); len <<= 1) {
    244		for (local = 0; local < 2; local++) {
    245			for (global = 0; global < 2; global++) {
    246				if (!local && !global)
    247					continue;
    248				read_var(len);
    249			}
    250		}
    251	}
    252
    253	/* Icebp trap */
    254	asm(".byte 0xf1\n");
    255	check_trapped();
    256
    257	/* Int 3 trap */
    258	asm("int $3\n");
    259	check_trapped();
    260
    261	kill(getpid(), SIGUSR1);
    262}
    263
    264static void check_success(const char *msg)
    265{
    266	int child_nr_tests;
    267	int status;
    268	int ret;
    269
    270	/* Wait for the child to SIGTRAP */
    271	wait(&status);
    272
    273	ret = 0;
    274
    275	if (WSTOPSIG(status) == SIGTRAP) {
    276		child_nr_tests = ptrace(PTRACE_PEEKDATA, child_pid,
    277					&nr_tests, 0);
    278		if (child_nr_tests == nr_tests)
    279			ret = 1;
    280		if (ptrace(PTRACE_POKEDATA, child_pid, &trapped, 1))
    281			ksft_exit_fail_msg("Can't poke: %s\n", strerror(errno));
    282	}
    283
    284	nr_tests++;
    285
    286	if (ret)
    287		ksft_test_result_pass(msg);
    288	else
    289		ksft_test_result_fail(msg);
    290}
    291
    292static void launch_instruction_breakpoints(char *buf, int local, int global)
    293{
    294	int i;
    295
    296	for (i = 0; i < COUNT_ISN_BPS; i++) {
    297		set_breakpoint_addr(dummy_funcs[i], i);
    298		toggle_breakpoint(i, BP_X, 1, local, global, 1);
    299		ptrace(PTRACE_CONT, child_pid, NULL, 0);
    300		sprintf(buf, "Test breakpoint %d with local: %d global: %d\n",
    301			i, local, global);
    302		check_success(buf);
    303		toggle_breakpoint(i, BP_X, 1, local, global, 0);
    304	}
    305}
    306
    307static void launch_watchpoints(char *buf, int mode, int len,
    308			       int local, int global)
    309{
    310	const char *mode_str;
    311	int i;
    312
    313	if (mode == BP_W)
    314		mode_str = "write";
    315	else
    316		mode_str = "read";
    317
    318	for (i = 0; i < COUNT_WPS; i++) {
    319		set_breakpoint_addr(&dummy_var[i], i);
    320		toggle_breakpoint(i, mode, len, local, global, 1);
    321		ptrace(PTRACE_CONT, child_pid, NULL, 0);
    322		sprintf(buf,
    323			"Test %s watchpoint %d with len: %d local: %d global: %d\n",
    324			mode_str, i, len, local, global);
    325		check_success(buf);
    326		toggle_breakpoint(i, mode, len, local, global, 0);
    327	}
    328}
    329
    330/* Set the breakpoints and check the child successfully trigger them */
    331static void launch_tests(void)
    332{
    333	char buf[1024];
    334	unsigned int tests = 0;
    335	int len, local, global, i;
    336
    337	tests += 3 * COUNT_ISN_BPS;
    338	tests += sizeof(long) / 2 * 3 * COUNT_WPS;
    339	tests += sizeof(long) / 2 * 3 * COUNT_WPS;
    340	tests += 2;
    341	ksft_set_plan(tests);
    342
    343	/* Instruction breakpoints */
    344	for (local = 0; local < 2; local++) {
    345		for (global = 0; global < 2; global++) {
    346			if (!local && !global)
    347				continue;
    348			launch_instruction_breakpoints(buf, local, global);
    349		}
    350	}
    351
    352	/* Write watchpoint */
    353	for (len = 1; len <= sizeof(long); len <<= 1) {
    354		for (local = 0; local < 2; local++) {
    355			for (global = 0; global < 2; global++) {
    356				if (!local && !global)
    357					continue;
    358				launch_watchpoints(buf, BP_W, len,
    359						   local, global);
    360			}
    361		}
    362	}
    363
    364	/* Read-Write watchpoint */
    365	for (len = 1; len <= sizeof(long); len <<= 1) {
    366		for (local = 0; local < 2; local++) {
    367			for (global = 0; global < 2; global++) {
    368				if (!local && !global)
    369					continue;
    370				launch_watchpoints(buf, BP_RW, len,
    371						   local, global);
    372			}
    373		}
    374	}
    375
    376	/* Icebp traps */
    377	ptrace(PTRACE_CONT, child_pid, NULL, 0);
    378	check_success("Test icebp\n");
    379
    380	/* Int 3 traps */
    381	ptrace(PTRACE_CONT, child_pid, NULL, 0);
    382	check_success("Test int 3 trap\n");
    383
    384	ptrace(PTRACE_CONT, child_pid, NULL, 0);
    385}
    386
    387int main(int argc, char **argv)
    388{
    389	pid_t pid;
    390	int ret;
    391
    392	ksft_print_header();
    393
    394	pid = fork();
    395	if (!pid) {
    396		trigger_tests();
    397		exit(0);
    398	}
    399
    400	child_pid = pid;
    401
    402	wait(NULL);
    403
    404	launch_tests();
    405
    406	wait(NULL);
    407
    408	ksft_exit_pass();
    409}