[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <0fd573cc044584f00976c410955ed486@ntu.edu.tw>
Date: Wed, 17 Sep 2025 22:23:49 +0800
From: Bill Tsui <b10902118@....edu.tw>
To: Will Deacon <will@...nel.org>
Cc: oleg@...hat.com, linux@...linux.org.uk, catalin.marinas@....com,
linux-arm-kernel@...ts.infradead.org, linux-kernel@...r.kernel.org
Subject: Re: [PATCH v2 1/3] arm64: ptrace: fix hw_break_set() by setting addr
and ctrl together
The following can reproduce the error for `arm64 compat` watchpoints
(breakpoints follow the same logic). I realized that testing my patch
can take time to set up from scratch, hoping this can facilitate the
review process.
There are three parts:
1. watch_unaligned.c
2. tracee.c
3. compile & run
Basically, watch_unaligned runs tracee and set 1-byte watchpoints on it.
1. watch_unaligned.c
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <linux/ptrace.h>
#include <asm/ptrace.h>
#include <sys/uio.h>
#include <elf.h>
#include <sys/uio.h>
#include <asm/ptrace.h>
#define MAX_WATCHPOINTS 4
#define AARCH64_BREAKPOINT_EL0 2
#define ENABLE 1
typedef enum {
arm_hwbp_break = 0,
arm_hwbp_load = 1,
arm_hwbp_store = 2,
arm_hwbp_access = 3
} arm_hwbp_type;
void error_detach_close(pid_t pid, FILE* pipe, const char* msg) {
perror(msg);
ptrace(PTRACE_DETACH, pid, NULL, NULL);
pclose(pipe);
}
void watch_1byte(int pid, uint32_t addr) {
// Set hardware watchpoint on ARM64
// Use PTRACE_SETREGSET with NT_ARM_HW_WATCH for watchpoints
printf("Setting watchpoint at address 0x%x\n", addr);
struct user_hwdebug_state watch;
memset(&watch, 0, sizeof(watch));
watch.dbg_regs[0].addr = addr;
uint32_t size = 1;
uint32_t byte_mask = (1u << size) - 1u;
uint32_t type = arm_hwbp_access;
uint32_t priv = AARCH64_BREAKPOINT_EL0;
watch.dbg_regs[0].ctrl =
(byte_mask << 5) | (type << 3) | (priv << 1) | ENABLE;
struct iovec ioVec;
memset(&ioVec, 0, sizeof(ioVec));
ioVec.iov_base = &watch;
// According to lldb. Otherwise dbg_regs[16] will cause error
ioVec.iov_len = sizeof(watch.dbg_info) + sizeof(watch.pad) +
(sizeof(watch.dbg_regs[0]) * MAX_WATCHPOINTS);
if (ptrace(PTRACE_SETREGSET, pid, NT_ARM_HW_WATCH, &ioVec) == -1) {
perror("PTRACE_SETREGSET");
} else {
printf("Watchpoint at address 0x%x set successfully\n", addr);
}
printf("---\n");
}
FILE* run_tracee(pid_t *pid, uint32_t *addr) {
// run tracee and get pid & addr
FILE *pipe = popen("./tracee", "r");
if (!pipe) {
perror("popen failed");
return NULL;
}
char line[64];
if (fgets(line, sizeof(line), pipe) == NULL) {
perror("read pid failed");
}
*pid = atoi(line);
if (fgets(line, sizeof(line), pipe) == NULL) {
perror("read addr failed");
}
*addr = strtoul(line, NULL, 0);
return pipe;
}
int main() {
pid_t pid;
uint32_t addr;
FILE *pipe = run_tracee(&pid, &addr);
if (!pipe) {
return 1;
}
printf("Attaching to PID %d\n", pid);
if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) == -1) {
error_detach_close(pid, pipe, "ptrace(ATTACH)");
return 1;
}
printf("Attached\n\n");
int status = 0;
if (waitpid(pid, &status, 0) == -1) {
error_detach_close(pid, pipe, "waitpid");
return 1;
}
// The TEST
// watch non-4-byte aligned. (Fail)
watch_1byte(pid, addr);
// watch any 4-byte aligned. (Success)
watch_1byte(pid, 0);
// watch non-4-byte aligned again. (Success)
// because bp_len was previously set to 2 successfully
watch_1byte(pid, addr);
// continue and let it trigger, proving functionality
if (ptrace(PTRACE_CONT, pid, NULL, NULL) == -1) {
error_detach_close(pid, pipe,"ptrace(CONT)");
return 1;
}
if (waitpid(pid, &status, 0) == -1) {
error_detach_close(pid, pipe, "waitpid (after CONT)");
pclose(pipe);
return 1;
}
if (WIFSTOPPED(status)) {
printf("Watchpoint triggered, signal: %d\n", WSTOPSIG(status));
ptrace(PTRACE_CONT, pid, NULL, NULL);
ptrace(PTRACE_DETACH, pid, NULL, NULL);
}
pclose(pipe);
return 0;
}
2. tracee.c
#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
int main() {
volatile int a = 42;
printf("%d\n%p\n", getpid(), (void*)((uintptr_t)&a + 1));
fflush(stdout);
while (1) {
a += 1;
}
return 0;
}
3. compile & run
# 64-bit tracer
aarch64-linux-gnu-gcc watch_unaligned.c -o watch_unaligned
# 32-bit tracee
arm-linux-gnueabi-gcc tracee.c -o tracee
# run
./watch_unaligned
output:
Attaching to PID 325
Attached
Setting watchpoint at address 0xffacca39
PTRACE_SETREGSET: Invalid argument
---
Setting watchpoint at address 0x0
Watchpoint at address 0x0 set successfully
---
Setting watchpoint at address 0xffacca39
Watchpoint at address 0xffacca39 set successfully
---
Watchpoint triggered, signal: 5
Summary: the first watchpoint failed but should be supported since it
was
successfully set and triggered just after addr 0x0 with length 1.
Thanks,
Bill
Powered by blists - more mailing lists