162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <linux/compiler.h> 362306a36Sopenharmony_ci#include <sys/types.h> 462306a36Sopenharmony_ci#include <sys/wait.h> 562306a36Sopenharmony_ci#include <sys/user.h> 662306a36Sopenharmony_ci#include <syscall.h> 762306a36Sopenharmony_ci#include <unistd.h> 862306a36Sopenharmony_ci#include <stdio.h> 962306a36Sopenharmony_ci#include <stdlib.h> 1062306a36Sopenharmony_ci#include <string.h> 1162306a36Sopenharmony_ci#include <sys/ptrace.h> 1262306a36Sopenharmony_ci#include <asm/ptrace.h> 1362306a36Sopenharmony_ci#include <errno.h> 1462306a36Sopenharmony_ci#include "debug.h" 1562306a36Sopenharmony_ci#include "tests/tests.h" 1662306a36Sopenharmony_ci#include "arch-tests.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic noinline int bp_1(void) 1962306a36Sopenharmony_ci{ 2062306a36Sopenharmony_ci pr_debug("in %s\n", __func__); 2162306a36Sopenharmony_ci return 0; 2262306a36Sopenharmony_ci} 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic noinline int bp_2(void) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci pr_debug("in %s\n", __func__); 2762306a36Sopenharmony_ci return 0; 2862306a36Sopenharmony_ci} 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic int spawn_child(void) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci int child = fork(); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci if (child == 0) { 3562306a36Sopenharmony_ci /* 3662306a36Sopenharmony_ci * The child sets itself for as tracee and 3762306a36Sopenharmony_ci * waits in signal for parent to trace it, 3862306a36Sopenharmony_ci * then it calls bp_1 and quits. 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_ci int err = ptrace(PTRACE_TRACEME, 0, NULL, NULL); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci if (err) { 4362306a36Sopenharmony_ci pr_debug("failed to PTRACE_TRACEME\n"); 4462306a36Sopenharmony_ci exit(1); 4562306a36Sopenharmony_ci } 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci raise(SIGCONT); 4862306a36Sopenharmony_ci bp_1(); 4962306a36Sopenharmony_ci exit(0); 5062306a36Sopenharmony_ci } 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci return child; 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/* 5662306a36Sopenharmony_ci * This tests creates HW breakpoint, tries to 5762306a36Sopenharmony_ci * change it and checks it was properly changed. 5862306a36Sopenharmony_ci */ 5962306a36Sopenharmony_cistatic int bp_modify1(void) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci pid_t child; 6262306a36Sopenharmony_ci int status; 6362306a36Sopenharmony_ci unsigned long rip = 0, dr7 = 1; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci child = spawn_child(); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci waitpid(child, &status, 0); 6862306a36Sopenharmony_ci if (WIFEXITED(status)) { 6962306a36Sopenharmony_ci pr_debug("tracee exited prematurely 1\n"); 7062306a36Sopenharmony_ci return TEST_FAIL; 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci /* 7462306a36Sopenharmony_ci * The parent does following steps: 7562306a36Sopenharmony_ci * - creates a new breakpoint (id 0) for bp_2 function 7662306a36Sopenharmony_ci * - changes that breakpoint to bp_1 function 7762306a36Sopenharmony_ci * - waits for the breakpoint to hit and checks 7862306a36Sopenharmony_ci * it has proper rip of bp_1 function 7962306a36Sopenharmony_ci * - detaches the child 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_ci if (ptrace(PTRACE_POKEUSER, child, 8262306a36Sopenharmony_ci offsetof(struct user, u_debugreg[0]), bp_2)) { 8362306a36Sopenharmony_ci pr_debug("failed to set breakpoint, 1st time: %s\n", 8462306a36Sopenharmony_ci strerror(errno)); 8562306a36Sopenharmony_ci goto out; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci if (ptrace(PTRACE_POKEUSER, child, 8962306a36Sopenharmony_ci offsetof(struct user, u_debugreg[0]), bp_1)) { 9062306a36Sopenharmony_ci pr_debug("failed to set breakpoint, 2nd time: %s\n", 9162306a36Sopenharmony_ci strerror(errno)); 9262306a36Sopenharmony_ci goto out; 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (ptrace(PTRACE_POKEUSER, child, 9662306a36Sopenharmony_ci offsetof(struct user, u_debugreg[7]), dr7)) { 9762306a36Sopenharmony_ci pr_debug("failed to set dr7: %s\n", strerror(errno)); 9862306a36Sopenharmony_ci goto out; 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (ptrace(PTRACE_CONT, child, NULL, NULL)) { 10262306a36Sopenharmony_ci pr_debug("failed to PTRACE_CONT: %s\n", strerror(errno)); 10362306a36Sopenharmony_ci goto out; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci waitpid(child, &status, 0); 10762306a36Sopenharmony_ci if (WIFEXITED(status)) { 10862306a36Sopenharmony_ci pr_debug("tracee exited prematurely 2\n"); 10962306a36Sopenharmony_ci return TEST_FAIL; 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci rip = ptrace(PTRACE_PEEKUSER, child, 11362306a36Sopenharmony_ci offsetof(struct user_regs_struct, rip), NULL); 11462306a36Sopenharmony_ci if (rip == (unsigned long) -1) { 11562306a36Sopenharmony_ci pr_debug("failed to PTRACE_PEEKUSER: %s\n", 11662306a36Sopenharmony_ci strerror(errno)); 11762306a36Sopenharmony_ci goto out; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci pr_debug("rip %lx, bp_1 %p\n", rip, bp_1); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ciout: 12362306a36Sopenharmony_ci if (ptrace(PTRACE_DETACH, child, NULL, NULL)) { 12462306a36Sopenharmony_ci pr_debug("failed to PTRACE_DETACH: %s", strerror(errno)); 12562306a36Sopenharmony_ci return TEST_FAIL; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci return rip == (unsigned long) bp_1 ? TEST_OK : TEST_FAIL; 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci/* 13262306a36Sopenharmony_ci * This tests creates HW breakpoint, tries to 13362306a36Sopenharmony_ci * change it to bogus value and checks the original 13462306a36Sopenharmony_ci * breakpoint is hit. 13562306a36Sopenharmony_ci */ 13662306a36Sopenharmony_cistatic int bp_modify2(void) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci pid_t child; 13962306a36Sopenharmony_ci int status; 14062306a36Sopenharmony_ci unsigned long rip = 0, dr7 = 1; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci child = spawn_child(); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci waitpid(child, &status, 0); 14562306a36Sopenharmony_ci if (WIFEXITED(status)) { 14662306a36Sopenharmony_ci pr_debug("tracee exited prematurely 1\n"); 14762306a36Sopenharmony_ci return TEST_FAIL; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* 15162306a36Sopenharmony_ci * The parent does following steps: 15262306a36Sopenharmony_ci * - creates a new breakpoint (id 0) for bp_1 function 15362306a36Sopenharmony_ci * - tries to change that breakpoint to (-1) address 15462306a36Sopenharmony_ci * - waits for the breakpoint to hit and checks 15562306a36Sopenharmony_ci * it has proper rip of bp_1 function 15662306a36Sopenharmony_ci * - detaches the child 15762306a36Sopenharmony_ci */ 15862306a36Sopenharmony_ci if (ptrace(PTRACE_POKEUSER, child, 15962306a36Sopenharmony_ci offsetof(struct user, u_debugreg[0]), bp_1)) { 16062306a36Sopenharmony_ci pr_debug("failed to set breakpoint: %s\n", 16162306a36Sopenharmony_ci strerror(errno)); 16262306a36Sopenharmony_ci goto out; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci if (ptrace(PTRACE_POKEUSER, child, 16662306a36Sopenharmony_ci offsetof(struct user, u_debugreg[7]), dr7)) { 16762306a36Sopenharmony_ci pr_debug("failed to set dr7: %s\n", strerror(errno)); 16862306a36Sopenharmony_ci goto out; 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci if (!ptrace(PTRACE_POKEUSER, child, 17262306a36Sopenharmony_ci offsetof(struct user, u_debugreg[0]), (unsigned long) (-1))) { 17362306a36Sopenharmony_ci pr_debug("failed, breakpoint set to bogus address\n"); 17462306a36Sopenharmony_ci goto out; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (ptrace(PTRACE_CONT, child, NULL, NULL)) { 17862306a36Sopenharmony_ci pr_debug("failed to PTRACE_CONT: %s\n", strerror(errno)); 17962306a36Sopenharmony_ci goto out; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci waitpid(child, &status, 0); 18362306a36Sopenharmony_ci if (WIFEXITED(status)) { 18462306a36Sopenharmony_ci pr_debug("tracee exited prematurely 2\n"); 18562306a36Sopenharmony_ci return TEST_FAIL; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci rip = ptrace(PTRACE_PEEKUSER, child, 18962306a36Sopenharmony_ci offsetof(struct user_regs_struct, rip), NULL); 19062306a36Sopenharmony_ci if (rip == (unsigned long) -1) { 19162306a36Sopenharmony_ci pr_debug("failed to PTRACE_PEEKUSER: %s\n", 19262306a36Sopenharmony_ci strerror(errno)); 19362306a36Sopenharmony_ci goto out; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci pr_debug("rip %lx, bp_1 %p\n", rip, bp_1); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ciout: 19962306a36Sopenharmony_ci if (ptrace(PTRACE_DETACH, child, NULL, NULL)) { 20062306a36Sopenharmony_ci pr_debug("failed to PTRACE_DETACH: %s", strerror(errno)); 20162306a36Sopenharmony_ci return TEST_FAIL; 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci return rip == (unsigned long) bp_1 ? TEST_OK : TEST_FAIL; 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ciint test__bp_modify(struct test_suite *test __maybe_unused, 20862306a36Sopenharmony_ci int subtest __maybe_unused) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci TEST_ASSERT_VAL("modify test 1 failed\n", !bp_modify1()); 21162306a36Sopenharmony_ci TEST_ASSERT_VAL("modify test 2 failed\n", !bp_modify2()); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci return 0; 21462306a36Sopenharmony_ci} 215