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

bp-modify.c (4834B)


      1// SPDX-License-Identifier: GPL-2.0
      2#include <linux/compiler.h>
      3#include <sys/types.h>
      4#include <sys/wait.h>
      5#include <sys/user.h>
      6#include <syscall.h>
      7#include <unistd.h>
      8#include <stdio.h>
      9#include <stdlib.h>
     10#include <string.h>
     11#include <sys/ptrace.h>
     12#include <asm/ptrace.h>
     13#include <errno.h>
     14#include "debug.h"
     15#include "tests/tests.h"
     16#include "arch-tests.h"
     17
     18static noinline int bp_1(void)
     19{
     20	pr_debug("in %s\n", __func__);
     21	return 0;
     22}
     23
     24static noinline int bp_2(void)
     25{
     26	pr_debug("in %s\n", __func__);
     27	return 0;
     28}
     29
     30static int spawn_child(void)
     31{
     32	int child = fork();
     33
     34	if (child == 0) {
     35		/*
     36		 * The child sets itself for as tracee and
     37		 * waits in signal for parent to trace it,
     38		 * then it calls bp_1 and quits.
     39		 */
     40		int err = ptrace(PTRACE_TRACEME, 0, NULL, NULL);
     41
     42		if (err) {
     43			pr_debug("failed to PTRACE_TRACEME\n");
     44			exit(1);
     45		}
     46
     47		raise(SIGCONT);
     48		bp_1();
     49		exit(0);
     50	}
     51
     52	return child;
     53}
     54
     55/*
     56 * This tests creates HW breakpoint, tries to
     57 * change it and checks it was properly changed.
     58 */
     59static int bp_modify1(void)
     60{
     61	pid_t child;
     62	int status;
     63	unsigned long rip = 0, dr7 = 1;
     64
     65	child = spawn_child();
     66
     67	waitpid(child, &status, 0);
     68	if (WIFEXITED(status)) {
     69		pr_debug("tracee exited prematurely 1\n");
     70		return TEST_FAIL;
     71	}
     72
     73	/*
     74	 * The parent does following steps:
     75	 *  - creates a new breakpoint (id 0) for bp_2 function
     76	 *  - changes that breakpoint to bp_1 function
     77	 *  - waits for the breakpoint to hit and checks
     78	 *    it has proper rip of bp_1 function
     79	 *  - detaches the child
     80	 */
     81	if (ptrace(PTRACE_POKEUSER, child,
     82		   offsetof(struct user, u_debugreg[0]), bp_2)) {
     83		pr_debug("failed to set breakpoint, 1st time: %s\n",
     84			 strerror(errno));
     85		goto out;
     86	}
     87
     88	if (ptrace(PTRACE_POKEUSER, child,
     89		   offsetof(struct user, u_debugreg[0]), bp_1)) {
     90		pr_debug("failed to set breakpoint, 2nd time: %s\n",
     91			 strerror(errno));
     92		goto out;
     93	}
     94
     95	if (ptrace(PTRACE_POKEUSER, child,
     96		   offsetof(struct user, u_debugreg[7]), dr7)) {
     97		pr_debug("failed to set dr7: %s\n", strerror(errno));
     98		goto out;
     99	}
    100
    101	if (ptrace(PTRACE_CONT, child, NULL, NULL)) {
    102		pr_debug("failed to PTRACE_CONT: %s\n", strerror(errno));
    103		goto out;
    104	}
    105
    106	waitpid(child, &status, 0);
    107	if (WIFEXITED(status)) {
    108		pr_debug("tracee exited prematurely 2\n");
    109		return TEST_FAIL;
    110	}
    111
    112	rip = ptrace(PTRACE_PEEKUSER, child,
    113		     offsetof(struct user_regs_struct, rip), NULL);
    114	if (rip == (unsigned long) -1) {
    115		pr_debug("failed to PTRACE_PEEKUSER: %s\n",
    116			 strerror(errno));
    117		goto out;
    118	}
    119
    120	pr_debug("rip %lx, bp_1 %p\n", rip, bp_1);
    121
    122out:
    123	if (ptrace(PTRACE_DETACH, child, NULL, NULL)) {
    124		pr_debug("failed to PTRACE_DETACH: %s", strerror(errno));
    125		return TEST_FAIL;
    126	}
    127
    128	return rip == (unsigned long) bp_1 ? TEST_OK : TEST_FAIL;
    129}
    130
    131/*
    132 * This tests creates HW breakpoint, tries to
    133 * change it to bogus value and checks the original
    134 * breakpoint is hit.
    135 */
    136static int bp_modify2(void)
    137{
    138	pid_t child;
    139	int status;
    140	unsigned long rip = 0, dr7 = 1;
    141
    142	child = spawn_child();
    143
    144	waitpid(child, &status, 0);
    145	if (WIFEXITED(status)) {
    146		pr_debug("tracee exited prematurely 1\n");
    147		return TEST_FAIL;
    148	}
    149
    150	/*
    151	 * The parent does following steps:
    152	 *  - creates a new breakpoint (id 0) for bp_1 function
    153	 *  - tries to change that breakpoint to (-1) address
    154	 *  - waits for the breakpoint to hit and checks
    155	 *    it has proper rip of bp_1 function
    156	 *  - detaches the child
    157	 */
    158	if (ptrace(PTRACE_POKEUSER, child,
    159		   offsetof(struct user, u_debugreg[0]), bp_1)) {
    160		pr_debug("failed to set breakpoint: %s\n",
    161			 strerror(errno));
    162		goto out;
    163	}
    164
    165	if (ptrace(PTRACE_POKEUSER, child,
    166		   offsetof(struct user, u_debugreg[7]), dr7)) {
    167		pr_debug("failed to set dr7: %s\n", strerror(errno));
    168		goto out;
    169	}
    170
    171	if (!ptrace(PTRACE_POKEUSER, child,
    172		   offsetof(struct user, u_debugreg[0]), (unsigned long) (-1))) {
    173		pr_debug("failed, breakpoint set to bogus address\n");
    174		goto out;
    175	}
    176
    177	if (ptrace(PTRACE_CONT, child, NULL, NULL)) {
    178		pr_debug("failed to PTRACE_CONT: %s\n", strerror(errno));
    179		goto out;
    180	}
    181
    182	waitpid(child, &status, 0);
    183	if (WIFEXITED(status)) {
    184		pr_debug("tracee exited prematurely 2\n");
    185		return TEST_FAIL;
    186	}
    187
    188	rip = ptrace(PTRACE_PEEKUSER, child,
    189		     offsetof(struct user_regs_struct, rip), NULL);
    190	if (rip == (unsigned long) -1) {
    191		pr_debug("failed to PTRACE_PEEKUSER: %s\n",
    192			 strerror(errno));
    193		goto out;
    194	}
    195
    196	pr_debug("rip %lx, bp_1 %p\n", rip, bp_1);
    197
    198out:
    199	if (ptrace(PTRACE_DETACH, child, NULL, NULL)) {
    200		pr_debug("failed to PTRACE_DETACH: %s", strerror(errno));
    201		return TEST_FAIL;
    202	}
    203
    204	return rip == (unsigned long) bp_1 ? TEST_OK : TEST_FAIL;
    205}
    206
    207int test__bp_modify(struct test_suite *test __maybe_unused,
    208		    int subtest __maybe_unused)
    209{
    210	TEST_ASSERT_VAL("modify test 1 failed\n", !bp_modify1());
    211	TEST_ASSERT_VAL("modify test 2 failed\n", !bp_modify2());
    212
    213	return 0;
    214}