lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite for Android: free password hash cracker in your pocket
[<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

Powered by Openwall GNU/*/Linux Powered by OpenVZ