Path: blob/master/tools/testing/selftests/breakpoints/breakpoint_test.c
26285 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* Copyright (C) 2011 Red Hat, Inc., Frederic Weisbecker <[email protected]>3*4* Selftests for breakpoints (and more generally the do_debug() path) in x86.5*/678#include <sys/ptrace.h>9#include <unistd.h>10#include <stddef.h>11#include <sys/user.h>12#include <stdio.h>13#include <stdlib.h>14#include <signal.h>15#include <sys/types.h>16#include <sys/wait.h>17#include <errno.h>18#include <string.h>1920#include "../kselftest.h"2122#define COUNT_ISN_BPS 423#define COUNT_WPS 42425/* Breakpoint access modes */26enum {27BP_X = 1,28BP_RW = 2,29BP_W = 4,30};3132static pid_t child_pid;3334/*35* Ensures the child and parent are always "talking" about36* the same test sequence. (ie: that we haven't forgotten37* to call check_trapped() somewhere).38*/39static int nr_tests;4041static void set_breakpoint_addr(void *addr, int n)42{43int ret;4445ret = ptrace(PTRACE_POKEUSER, child_pid,46offsetof(struct user, u_debugreg[n]), addr);47if (ret)48ksft_exit_fail_msg("Can't set breakpoint addr: %s\n",49strerror(errno));50}5152static void toggle_breakpoint(int n, int type, int len,53int local, int global, int set)54{55int ret;5657int xtype, xlen;58unsigned long vdr7, dr7;5960switch (type) {61case BP_X:62xtype = 0;63break;64case BP_W:65xtype = 1;66break;67case BP_RW:68xtype = 3;69break;70}7172switch (len) {73case 1:74xlen = 0;75break;76case 2:77xlen = 4;78break;79case 4:80xlen = 0xc;81break;82case 8:83xlen = 8;84break;85}8687dr7 = ptrace(PTRACE_PEEKUSER, child_pid,88offsetof(struct user, u_debugreg[7]), 0);8990vdr7 = (xlen | xtype) << 16;91vdr7 <<= 4 * n;9293if (local) {94vdr7 |= 1 << (2 * n);95vdr7 |= 1 << 8;96}97if (global) {98vdr7 |= 2 << (2 * n);99vdr7 |= 1 << 9;100}101102if (set)103dr7 |= vdr7;104else105dr7 &= ~vdr7;106107ret = ptrace(PTRACE_POKEUSER, child_pid,108offsetof(struct user, u_debugreg[7]), dr7);109if (ret) {110ksft_print_msg("Can't set dr7: %s\n", strerror(errno));111exit(-1);112}113}114115/* Dummy variables to test read/write accesses */116static unsigned long long dummy_var[4];117118/* Dummy functions to test execution accesses */119static void dummy_func(void) { }120static void dummy_func1(void) { }121static void dummy_func2(void) { }122static void dummy_func3(void) { }123124static void (*dummy_funcs[])(void) = {125dummy_func,126dummy_func1,127dummy_func2,128dummy_func3,129};130131static int trapped;132133static void check_trapped(void)134{135/*136* If we haven't trapped, wake up the parent137* so that it notices the failure.138*/139if (!trapped)140kill(getpid(), SIGUSR1);141trapped = 0;142143nr_tests++;144}145146static void write_var(int len)147{148char *pcval; short *psval; int *pival; long long *plval;149int i;150151for (i = 0; i < 4; i++) {152switch (len) {153case 1:154pcval = (char *)&dummy_var[i];155*pcval = 0xff;156break;157case 2:158psval = (short *)&dummy_var[i];159*psval = 0xffff;160break;161case 4:162pival = (int *)&dummy_var[i];163*pival = 0xffffffff;164break;165case 8:166plval = (long long *)&dummy_var[i];167*plval = 0xffffffffffffffffLL;168break;169}170check_trapped();171}172}173174static void read_var(int len)175{176char cval; short sval; int ival; long long lval;177int i;178179for (i = 0; i < 4; i++) {180switch (len) {181case 1:182cval = *(char *)&dummy_var[i];183break;184case 2:185sval = *(short *)&dummy_var[i];186break;187case 4:188ival = *(int *)&dummy_var[i];189break;190case 8:191lval = *(long long *)&dummy_var[i];192break;193}194check_trapped();195}196}197198/*199* Do the r/w/x accesses to trigger the breakpoints. And run200* the usual traps.201*/202static void trigger_tests(void)203{204int len, local, global, i;205char val;206int ret;207208ret = ptrace(PTRACE_TRACEME, 0, NULL, 0);209if (ret) {210ksft_print_msg("Can't be traced? %s\n", strerror(errno));211return;212}213214/* Wake up father so that it sets up the first test */215kill(getpid(), SIGUSR1);216217/* Test instruction breakpoints */218for (local = 0; local < 2; local++) {219for (global = 0; global < 2; global++) {220if (!local && !global)221continue;222223for (i = 0; i < COUNT_ISN_BPS; i++) {224dummy_funcs[i]();225check_trapped();226}227}228}229230/* Test write watchpoints */231for (len = 1; len <= sizeof(long); len <<= 1) {232for (local = 0; local < 2; local++) {233for (global = 0; global < 2; global++) {234if (!local && !global)235continue;236write_var(len);237}238}239}240241/* Test read/write watchpoints (on read accesses) */242for (len = 1; len <= sizeof(long); len <<= 1) {243for (local = 0; local < 2; local++) {244for (global = 0; global < 2; global++) {245if (!local && !global)246continue;247read_var(len);248}249}250}251252/* Icebp trap */253asm(".byte 0xf1\n");254check_trapped();255256/* Int 3 trap */257asm("int $3\n");258check_trapped();259260kill(getpid(), SIGUSR1);261}262263static void check_success(const char *msg)264{265int child_nr_tests;266int status;267int ret;268269/* Wait for the child to SIGTRAP */270wait(&status);271272ret = 0;273274if (WSTOPSIG(status) == SIGTRAP) {275child_nr_tests = ptrace(PTRACE_PEEKDATA, child_pid,276&nr_tests, 0);277if (child_nr_tests == nr_tests)278ret = 1;279if (ptrace(PTRACE_POKEDATA, child_pid, &trapped, 1))280ksft_exit_fail_msg("Can't poke: %s\n", strerror(errno));281}282283nr_tests++;284285if (ret)286ksft_test_result_pass("%s", msg);287else288ksft_test_result_fail("%s", msg);289}290291static void launch_instruction_breakpoints(char *buf, int local, int global)292{293int i;294295for (i = 0; i < COUNT_ISN_BPS; i++) {296set_breakpoint_addr(dummy_funcs[i], i);297toggle_breakpoint(i, BP_X, 1, local, global, 1);298ptrace(PTRACE_CONT, child_pid, NULL, 0);299sprintf(buf, "Test breakpoint %d with local: %d global: %d\n",300i, local, global);301check_success(buf);302toggle_breakpoint(i, BP_X, 1, local, global, 0);303}304}305306static void launch_watchpoints(char *buf, int mode, int len,307int local, int global)308{309const char *mode_str;310int i;311312if (mode == BP_W)313mode_str = "write";314else315mode_str = "read";316317for (i = 0; i < COUNT_WPS; i++) {318set_breakpoint_addr(&dummy_var[i], i);319toggle_breakpoint(i, mode, len, local, global, 1);320ptrace(PTRACE_CONT, child_pid, NULL, 0);321sprintf(buf,322"Test %s watchpoint %d with len: %d local: %d global: %d\n",323mode_str, i, len, local, global);324check_success(buf);325toggle_breakpoint(i, mode, len, local, global, 0);326}327}328329/* Set the breakpoints and check the child successfully trigger them */330static void launch_tests(void)331{332char buf[1024];333unsigned int tests = 0;334int len, local, global, i;335336tests += 3 * COUNT_ISN_BPS;337tests += sizeof(long) / 2 * 3 * COUNT_WPS;338tests += sizeof(long) / 2 * 3 * COUNT_WPS;339tests += 2;340ksft_set_plan(tests);341342/* Instruction breakpoints */343for (local = 0; local < 2; local++) {344for (global = 0; global < 2; global++) {345if (!local && !global)346continue;347launch_instruction_breakpoints(buf, local, global);348}349}350351/* Write watchpoint */352for (len = 1; len <= sizeof(long); len <<= 1) {353for (local = 0; local < 2; local++) {354for (global = 0; global < 2; global++) {355if (!local && !global)356continue;357launch_watchpoints(buf, BP_W, len,358local, global);359}360}361}362363/* Read-Write watchpoint */364for (len = 1; len <= sizeof(long); len <<= 1) {365for (local = 0; local < 2; local++) {366for (global = 0; global < 2; global++) {367if (!local && !global)368continue;369launch_watchpoints(buf, BP_RW, len,370local, global);371}372}373}374375/* Icebp traps */376ptrace(PTRACE_CONT, child_pid, NULL, 0);377check_success("Test icebp\n");378379/* Int 3 traps */380ptrace(PTRACE_CONT, child_pid, NULL, 0);381check_success("Test int 3 trap\n");382383ptrace(PTRACE_CONT, child_pid, NULL, 0);384}385386int main(int argc, char **argv)387{388pid_t pid;389int ret;390391ksft_print_header();392393pid = fork();394if (!pid) {395trigger_tests();396exit(0);397}398399child_pid = pid;400401wait(NULL);402403launch_tests();404405wait(NULL);406407ksft_exit_pass();408}409410411