From: Andi Kleen <ak@kernel.org>
To: linux-kernel@vger.kernel.org
Cc: mhiramat@kernel.org, oleg@redhat.com, peterz@infradead.org,
tglx@kernel.org, x86@kernel.org, jolsa@kernel.org,
linux-perf-users@vger.kernel.org, adrian.hunter@intel.com,
Andi Kleen <ak@kernel.org>
Subject: [RFC v1 10/19] ptwrite uprobes: Minimal memory references and fault handling
Date: Mon, 31 Aug 2026 08:04:46 -0700 [thread overview]
Message-ID: <20260831150651.1134594-11-ak@kernel.org> (raw)
In-Reply-To: <20260831150651.1134594-1-ak@kernel.org>
Add support for memory references. Currently this is only
simple cases, no indirect memory references or strings,
that would require saving/restoring registers. Only 8 and 4 byte
memory references are supported.
This requires fault handling using the fault notifier hook added
earlier. When a fault happens inside a probe return to a specially
generated tail that logs zeroes.
It increases the in memory overhead of uprobe_ptwrite_page somewhat
(to roughly a KB). I tried to find alternatives, but they all
complicated the code, and 1KB should be still acceptable.
Assisted-by: omp:gpt-5.6-luna
Signed-off-by: Andi Kleen <ak@kernel.org>
---
arch/x86/include/asm/uprobes.h | 14 +
arch/x86/kernel/uprobes.c | 308 +++++++++++++++++--
include/linux/uprobes.h | 4 +-
samples/uprobe-ptwrite/uprobe_ptwrite_test.c | 41 ++-
4 files changed, 344 insertions(+), 23 deletions(-)
diff --git a/arch/x86/include/asm/uprobes.h b/arch/x86/include/asm/uprobes.h
index 5cc870e857a5..efffdc44f00a 100644
--- a/arch/x86/include/asm/uprobes.h
+++ b/arch/x86/include/asm/uprobes.h
@@ -48,6 +48,8 @@ struct uprobe_ptwrite_arch {
u8 jmp_off; /* offset of the final jmp's rel32 field */
u8 ndata; /* number of u64 data slots */
u8 orig[MAX_UINSN_BYTES]; /* pristine file bytes, before generic analysis */
+ u16 ft_off; /* fault table offset within the block (0 if none) */
+ u8 nft; /* number of fault entries */
};
/* Per-mm page holding generated ptwrite stub blocks (mirrors trampolines). */
@@ -56,6 +58,18 @@ struct uprobe_ptwrite_page {
struct page *page; /* stub blocks written via kmap */
unsigned long vaddr; /* mapping base */
u16 cursor; /* next free block offset */
+ u16 nblocks;
+ struct {
+ u16 off; /* block offset in the page */
+ u16 len; /* generated block length */
+ u16 ft_off; /* fault table offset within the block */
+ u8 orig0; /* original site byte 0 (pun restore) */
+ u8 pun; /* instruction-pun mechanism (single-byte poke) */
+ u8 site_len; /* original instruction length (pun identity) */
+ s32 site_off; /* probe site - page base (idempotent reinstall) */
+ u8 site_insn[MAX_UINSN_BYTES]; /* original bytes (pun identity) */
+ } index[PAGE_SIZE / 32]; /* exact: min block = 32 B (nargs >= 1), */
+ /* so <= 128 blocks fit a page */
};
struct arch_uprobe {
diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c
index df652c56414b..90e702a4a8e9 100644
--- a/arch/x86/kernel/uprobes.c
+++ b/arch/x86/kernel/uprobes.c
@@ -24,6 +24,7 @@
#include <asm/nops.h>
#include <asm/cpufeature.h>
#include <asm/cpuid/api.h>
+#include <asm/traps.h>
/* Post-execution fixups. */
@@ -727,6 +728,7 @@ static struct vm_area_struct *get_uprobe_trampoline(struct mm_struct *mm, unsign
void arch_uprobe_init_state(struct mm_struct *mm)
{
+
INIT_HLIST_HEAD(&mm->uprobes_state.head_ptwrite);
}
@@ -923,9 +925,12 @@ asm (
extern u8 uprobe_trampoline_entry[];
+static struct notifier_block uprobe_user_fault_nb;
+
static int __init arch_uprobes_init(void)
{
tramp_mapping_pages[0] = virt_to_page(uprobe_trampoline_entry);
+ register_x86_user_fault_notifier(&uprobe_user_fault_nb);
return 0;
}
@@ -1259,6 +1264,20 @@ static int ptwrite_emit_riprel(u8 *p, s32 disp)
return 9;
}
+static int ptwrite_emit_riprel32(u8 *p, s32 disp)
+{
+ /*
+ * ptwritel disp32(%rip) : F3 0F AE 25 <disp32> (8 bytes)
+ * modrm 0x25 = mod 00, reg 100 (/4, PTWRITE), rm 101 (RIP-relative).
+ */
+ *p++ = 0xf3;
+ *p++ = 0x0f;
+ *p++ = 0xae;
+ *p++ = 0x25;
+ memcpy(p, &disp, 4);
+ return 8;
+}
+
bool arch_uprobe_ptwrite_supported(void)
{
u32 eax, ebx, ecx, edx;
@@ -1293,7 +1312,11 @@ static const struct {
{ offsetof(struct pt_regs, r14), 14 }, { offsetof(struct pt_regs, r15), 15 },
};
-/* Compile the register, stack-pointer, and immediate fetch forms. */
+
+/*
+ * Compile one tracefs fetch arg (arch-neutral form, see
+ * uprobe_ptwrite_fetch) into a ptwrite descriptor entry.
+ */
int arch_uprobe_ptwrite_fetch(struct uprobe_ptwrite_arg *a,
const struct uprobe_ptwrite_fetch *f)
{
@@ -1301,21 +1324,30 @@ int arch_uprobe_ptwrite_fetch(struct uprobe_ptwrite_arg *a,
switch (f->kind) {
case UPROBE_PTW_FETCH_REG:
+ case UPROBE_PTW_FETCH_MEMREG:
for (j = 0; j < ARRAY_SIZE(ptwrite_reg_map); j++)
if (ptwrite_reg_map[j].off == f->reg) {
idx = ptwrite_reg_map[j].idx;
break;
}
if (idx < 0)
- return -EINVAL;
- a->src = UPROBE_PTW_SRC_REG;
+ return -EINVAL; /* not an x86-64 GPR */
+ a->src = f->kind == UPROBE_PTW_FETCH_REG ?
+ UPROBE_PTW_SRC_REG : UPROBE_PTW_SRC_MEM;
a->reg = idx;
+ if (f->kind == UPROBE_PTW_FETCH_MEMREG)
+ a->val = (u64)(s32)f->imm;
break;
- case UPROBE_PTW_FETCH_STACKP:
+ case UPROBE_PTW_FETCH_STACKP: /* $stack: SP value, never faults */
a->src = UPROBE_PTW_SRC_REG;
a->reg = 4; /* rsp */
break;
- case UPROBE_PTW_FETCH_IMM:
+ case UPROBE_PTW_FETCH_STACKN: /* [rsp + imm] */
+ a->src = UPROBE_PTW_SRC_MEM;
+ a->reg = 4; /* rsp */
+ a->val = f->imm;
+ break;
+ case UPROBE_PTW_FETCH_IMM: /* \IMM */
a->src = UPROBE_PTW_SRC_IMM;
a->val = f->imm;
break;
@@ -1325,15 +1357,43 @@ int arch_uprobe_ptwrite_fetch(struct uprobe_ptwrite_arg *a,
return 0;
}
+
+/*
+ * Worst-case stub block: header ptwriteq (9) + max args of the largest form
+ * (MEM: 5 opcode + SIB + 4 disp + 2 short jmp + 9 fixup = 21 B) + final jmp
+ * (5) -> code; data: header + fault-word slots (16); fault table
+ * [nft][{start,end,fixup} x nft] (2 + 6*nft). Must fit UPROBE_PTWRITE_STUB_SIZE;
+ * prepare() also enforces it with -E2BIG at runtime.
+ */
+static_assert((((9 + UPROBE_PTWRITE_MAX_ARGS * 21 + 5 + 7) & ~7) +
+ 16 + 2 + 6 * UPROBE_PTWRITE_MAX_ARGS) <= UPROBE_PTWRITE_STUB_SIZE,
+ "worst-case ptwrite stub block exceeds UPROBE_PTWRITE_STUB_SIZE");
+
+static bool ptwrite_has_room(const u8 *base, const u8 *p, size_t len)
+{
+ return p >= base && (size_t)(p - base) <=
+ sizeof(((struct uprobe_ptwrite_arch *)0)->stub) - len;
+}
+
+#define PTW_NEED(_len) do { \
+ if (!ptwrite_has_room(code, p, (_len))) \
+ return -E2BIG; \
+ } while (0)
+
+
+
int arch_uprobe_ptwrite_prepare(struct arch_uprobe *auprobe,
const struct uprobe_ptwrite_desc *desc)
{
struct uprobe_ptwrite_arch *ptw = &auprobe->ptwrite;
u8 *code = ptw->stub, *p = ptw->stub;
u16 imm_off[UPROBE_PTWRITE_MAX_ARGS];
- unsigned int data_off;
+ u8 fixup_len[UPROBE_PTWRITE_MAX_ARGS];
+ struct { u16 start, end, fixup; } __packed mft[UPROBE_PTWRITE_MAX_ARGS];
+ u16 mdisp[UPROBE_PTWRITE_MAX_ARGS];
+ unsigned int data_off, flt_off, ft_off;
unsigned int hdr_off = 0;
- unsigned int imm_idx = 0, n_imm = 0;
+ unsigned int imm_idx = 0, n_imm = 0, mem_idx = 0, n_mem = 0;
u64 hdr;
int i;
@@ -1341,7 +1401,7 @@ int arch_uprobe_ptwrite_prepare(struct arch_uprobe *auprobe,
return -EINVAL;
if (desc->nargs > UPROBE_PTWRITE_MAX_ARGS)
return -E2BIG;
- if (desc->flags)
+ if (desc->flags & ~UPROBE_PTWRITE_FL_ALLOW_MEM)
return -EINVAL;
/* The generic registration path copied these bytes before this hook. */
@@ -1358,24 +1418,89 @@ int arch_uprobe_ptwrite_prepare(struct arch_uprobe *auprobe,
return -E2BIG;
n_imm++;
break;
+ case UPROBE_PTW_SRC_MEM:
+ if (!(desc->flags & UPROBE_PTWRITE_FL_ALLOW_MEM))
+ return -EINVAL;
+ if (desc->args[i].reg > 15)
+ return -EINVAL;
+ /*
+ * u64 args use ptwriteq (8-byte load); u32/s32/x32
+ * args use ptwritel (4-byte load). Any other size
+ * would read the wrong width.
+ */
+ if (desc->args[i].size != 4 &&
+ desc->args[i].size != 8)
+ return -EINVAL;
+ if (n_mem >= ARRAY_SIZE(mft))
+ return -E2BIG;
+ n_mem++;
+ break;
default:
return -EINVAL;
}
}
/* header word emission (disp32 patched below) */
+ PTW_NEED(9);
p += ptwrite_emit_riprel(p, 0);
for (i = 0; i < desc->nargs; i++) {
- if (desc->args[i].src == UPROBE_PTW_SRC_REG) {
+ switch (desc->args[i].src) {
+ case UPROBE_PTW_SRC_REG:
+ PTW_NEED(5);
p += ptwrite_emit_reg(p, desc->args[i].reg);
- } else {
+ break;
+ case UPROBE_PTW_SRC_IMM:
+ if (imm_idx >= ARRAY_SIZE(imm_off))
+ return -E2BIG;
+ PTW_NEED(9);
imm_off[imm_idx++] = p - code;
p += ptwrite_emit_riprel(p, 0);
+ break;
+ case UPROBE_PTW_SRC_MEM: {
+ /*
+ * ptwrite[q|l] disp32(%reg), short jump, and fault
+ * fixup. The largest form is 21 bytes.
+ */
+ u8 reg = desc->args[i].reg;
+ bool wide = desc->args[i].size == 8;
+ unsigned int arg_len = (wide ? 9 : 8) +
+ ((reg & 7) == 4) + 2 + (wide ? 9 : 8);
+ int start;
+
+ if (mem_idx >= ARRAY_SIZE(mft))
+ return -E2BIG;
+ PTW_NEED(arg_len);
+ start = p - code;
+ *p++ = 0xf3;
+ if (wide)
+ *p++ = (reg & 8) ? 0x49 : 0x48; /* REX.W */
+ else if (reg & 8)
+ *p++ = 0x41; /* REX.B only (32-bit operand) */
+ *p++ = 0x0f;
+ *p++ = 0xae;
+ *p++ = 0xa0 | (reg & 7); /* mod 10, reg /4, rm reg */
+ if ((reg & 7) == 4) /* SIB escape: base rsp/esp/r12 */
+ *p++ = 0x24;
+ mft[mem_idx].start = start;
+ mdisp[mem_idx] = p - code;
+ p += 4;
+ mft[mem_idx].end = p - code;
+ *p++ = 0xeb;
+ *p++ = wide ? 9 : 8;
+ mft[mem_idx].fixup = p - code;
+ fixup_len[mem_idx] = wide ?
+ ptwrite_emit_riprel(p, 0) :
+ ptwrite_emit_riprel32(p, 0);
+ p += fixup_len[mem_idx];
+ mem_idx++;
+ break;
+ }
}
}
/* final jmp back to probe+5; rel32 patched per-mm at install */
+ PTW_NEED(5);
*p++ = 0xe9;
if (p - code > U8_MAX)
return -E2BIG;
@@ -1383,11 +1508,23 @@ int arch_uprobe_ptwrite_prepare(struct arch_uprobe *auprobe,
p += 4;
data_off = (p - code + 7) & ~7UL;
- if (data_off + 8 * (1 + n_imm) > sizeof(ptw->stub))
+ /* data: header + imm slots + one shared fault-word slot (0) */
+ if (data_off + 8 * (1 + n_imm + (n_mem ? 1 : 0)) > sizeof(ptw->stub))
return -E2BIG;
+ flt_off = data_off + 8 * (1 + n_imm);
- /* data slots: header, then imm values in emission order */
- hdr = ((u64)desc->event_id << 48) | ((u64)desc->nargs << 40);
+ /* fault table: [u16 nft][{start,end,fixup} x nft], block-relative */
+ ft_off = (flt_off + 8 * (n_mem ? 1 : 0) + 7) & ~7UL;
+ if (ft_off + 2 + 6 * n_mem > sizeof(ptw->stub))
+ return -E2BIG;
+ if (n_mem) {
+ *(u16 *)(code + ft_off) = n_mem;
+ memcpy(code + ft_off + 2, mft, 6 * n_mem);
+ }
+
+ /* data slots: header, imm values in emission order, fault word */
+ hdr = ((u64)desc->event_id << 48) | ((u64)desc->nargs << 40) |
+ UPROBE_PTW_HDR_MAGIC;
*(u64 *)(code + data_off) = hdr;
/* patch the header's disp32: hdr slot - end of header insn */
@@ -1403,8 +1540,33 @@ int arch_uprobe_ptwrite_prepare(struct arch_uprobe *auprobe,
imm_idx++;
}
- ptw->stub_len = data_off + 8 * (1 + n_imm);
- ptw->ndata = 1 + n_imm;
+ /* memory arg disp32s (absolute vs the base reg) + fixup disp32s */
+ mem_idx = 0;
+ for (i = 0; i < desc->nargs; i++) {
+ s32 disp;
+
+ if (desc->args[i].src != UPROBE_PTW_SRC_MEM)
+ continue;
+ disp = (s32)desc->args[i].val;
+ *(s32 *)(code + mdisp[mem_idx]) = disp;
+ /*
+ * fixup's RIP-relative disp: flt slot - end of fixup insn.
+ * disp32 field starts at flen - 4 in both forms
+ */
+ *(s32 *)(code + mft[mem_idx].fixup + fixup_len[mem_idx] - 4) =
+ (s32)(flt_off - (mft[mem_idx].fixup +
+ fixup_len[mem_idx]));
+ mem_idx++;
+ }
+
+ /* the shared fault word: failed reads emit 0 */
+ if (n_mem)
+ *(u64 *)(code + flt_off) = 0;
+
+ ptw->stub_len = n_mem ? ft_off + 2 + 6 * n_mem : data_off + 8 * (1 + n_imm);
+ ptw->ndata = 1 + n_imm + (n_mem ? 1 : 0);
+ ptw->ft_off = n_mem ? ft_off : 0;
+ ptw->nft = n_mem;
return 0;
}
#undef PTW_NEED
@@ -1664,6 +1826,7 @@ int arch_uprobe_install_ptwrite(struct arch_uprobe *auprobe,
struct uprobe_ptwrite_arch *ptw_a = &auprobe->ptwrite;
unsigned long block_off, stub_addr;
u8 *kaddr, orig[5];
+ s64 site_delta;
s32 rel;
int ret;
@@ -1689,13 +1852,22 @@ int arch_uprobe_install_ptwrite(struct arch_uprobe *auprobe,
return -ENOMEM;
block_off = ptw->cursor;
- if (block_off > PAGE_SIZE ||
- ptw_a->stub_len > PAGE_SIZE - block_off)
+ if (block_off > PAGE_SIZE || ptw_a->stub_len > PAGE_SIZE - block_off)
+ return -ENOMEM;
+ if (ptw->nblocks >= ARRAY_SIZE(ptw->index))
return -ENOMEM;
stub_addr = ptw->vaddr + block_off;
if (!ptwrite_rel32(stub_addr + ptw_a->jmp_off + 4,
vaddr + 5, &rel))
return -ERANGE;
+ site_delta = (s64)vaddr - (s64)ptw->vaddr;
+ if (site_delta < INT_MIN || site_delta > INT_MAX)
+ return -ERANGE;
+
+ ptw->index[ptw->nblocks].off = block_off;
+ ptw->index[ptw->nblocks].len = ptw_a->stub_len;
+ ptw->index[ptw->nblocks].ft_off = ptw_a->ft_off;
+ ptw->nblocks++;
kaddr = kmap_local_page(ptw->page);
memcpy(kaddr + block_off, ptw_a->stub, ptw_a->stub_len);
@@ -1704,9 +1876,13 @@ int arch_uprobe_install_ptwrite(struct arch_uprobe *auprobe,
kunmap_local(kaddr);
ret = ptwrite_text_poke(auprobe, vma, vaddr, stub_addr);
- if (!ret)
- ptw->cursor = block_off + ptw_a->stub_len;
- return ret;
+ if (ret)
+ /* Publish rollback before readers use the reduced block count. */
+ smp_store_release(&ptw->nblocks, ptw->nblocks - 1);
+ return ret;
+ }
+ ptw->cursor = block_off + ptw_a->stub_len;
+ return 0;
}
int arch_uprobe_uninstall_ptwrite(struct arch_uprobe *auprobe,
@@ -1724,6 +1900,98 @@ int arch_uprobe_uninstall_ptwrite(struct arch_uprobe *auprobe,
UPROBE_SWBP_INSN, false, false, false, false, NULL);
}
+/*
+ * Ptwrite memory faults are fixed up for fault classes routed through the
+ * user-fault notifier. #AC is intentionally not handled: alignment checking
+ * is normally disabled for user processes and is not a supported ptwrite mode.
+ */
+static bool uprobe_ptwrite_handle_fault(struct pt_regs *regs)
+{
+ struct mm_struct *mm = current->mm;
+ struct uprobes_state *state;
+ struct uprobe_ptwrite_page *ptw;
+ unsigned long ip = instruction_pointer(regs);
+ int b;
+
+ if (!mm)
+ return false;
+ state = &mm->uprobes_state;
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(ptw, &state->head_ptwrite, node) {
+ unsigned long boff;
+ u16 nblocks;
+
+ if (ip < ptw->vaddr || ip >= ptw->vaddr + PAGE_SIZE)
+ continue;
+ boff = ip - ptw->vaddr;
+ /* Acquire published metadata before scanning blocks in the fault path. */
+ nblocks = smp_load_acquire(&ptw->nblocks);
+ for (b = 0; b < nblocks; b++) {
+ u16 off = ptw->index[b].off;
+ u16 len = ptw->index[b].len;
+ u16 fto = ptw->index[b].ft_off;
+ u8 *kaddr;
+ u16 nft, i;
+
+ /* no fault table, or IP outside this block: not ours */
+ if (!fto || boff < off || boff >= off + len)
+ continue;
+ if (off >= PAGE_SIZE || len > PAGE_SIZE - off ||
+ len < sizeof(nft) ||
+ fto > len - sizeof(nft)) {
+ rcu_read_unlock();
+ return false;
+ }
+ kaddr = kmap_local_page(ptw->page);
+ nft = *(u16 *)(kaddr + off + fto);
+ if (nft > UPROBE_PTWRITE_MAX_ARGS ||
+ nft > (len - fto - sizeof(nft)) / 6) {
+ kunmap_local(kaddr);
+ rcu_read_unlock();
+ return false;
+ }
+ for (i = 0; i < nft; i++) {
+ u16 *e = (u16 *)(kaddr + off + fto +
+ sizeof(nft) + i * 6);
+
+ if (e[0] >= e[1] || e[1] > len || e[2] >= len) {
+ kunmap_local(kaddr);
+ rcu_read_unlock();
+ return false;
+ }
+ if (boff >= off + e[0] && boff < off + e[1]) {
+ regs->ip = ptw->vaddr + off + e[2];
+ kunmap_local(kaddr);
+ rcu_read_unlock();
+ return true;
+ }
+ }
+ kunmap_local(kaddr);
+ rcu_read_unlock();
+ return false;
+ }
+ }
+ rcu_read_unlock();
+ return false;
+}
+
+static int uprobe_user_fault_notify(struct notifier_block *self,
+ unsigned long val, void *data)
+{
+ struct x86_user_fault_args *args = data;
+
+ if (!args || !args->regs)
+ return NOTIFY_DONE;
+
+ if (uprobe_ptwrite_handle_fault(args->regs))
+ return NOTIFY_STOP;
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block uprobe_user_fault_nb = {
+ .notifier_call = uprobe_user_fault_notify,
+};
static bool __is_optimized(struct mm_struct *mm, uprobe_opcode_t *insn, unsigned long vaddr)
{
diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h
index c0d65ea5353e..c0d189bef8bd 100644
--- a/include/linux/uprobes.h
+++ b/include/linux/uprobes.h
@@ -213,10 +213,10 @@ enum uprobe_ptwrite_src {
struct uprobe_ptwrite_arg {
u8 src; /* enum uprobe_ptwrite_src */
- u8 reg; /* x86-64 GPR index (0=rax..15=r15) for SRC_REG */
+ u8 reg; /* x86-64 GPR index (0=rax..15=r15) for SRC_REG/SRC_MEM */
u8 size; /* declared type size 1/2/4/8 (decoder hint) */
u8 reserved;
- u64 val; /* SRC_IMM: constant; SRC_REG: unused */
+ u64 val; /* SRC_IMM: constant, SRC_MEM: disp32 (low 32 bits) */
};
struct uprobe_ptwrite_desc {
diff --git a/samples/uprobe-ptwrite/uprobe_ptwrite_test.c b/samples/uprobe-ptwrite/uprobe_ptwrite_test.c
index c3b9dd6bec16..5eafc26104ad 100644
--- a/samples/uprobe-ptwrite/uprobe_ptwrite_test.c
+++ b/samples/uprobe-ptwrite/uprobe_ptwrite_test.c
@@ -11,9 +11,11 @@
* Usage (module params):
* path=/path/to/prog file to probe
* offset=0xADDR file offset of the probe site
- * args="r0,r1,i0x42" comma-separated;
+ * args="r0,r1,i0x42,m3,m2:0x8,m4:0x10:4" comma-separated;
* r<N> = x86-64 GPR index 0..15,
* i<hex> = immediate constant,
+ * m<N>[:<disp>][:<size>] = memory arg [reg + disp32],
+ * size 4 (u32 load) or 8 (u64 load, default)
* event_id=0x1234 identifier carried in the PTW header word
*/
#include <linux/module.h>
@@ -87,6 +89,43 @@ static int parse_probe_args(void)
}
a->src = UPROBE_PTW_SRC_IMM;
a->val = v;
+ } else if (tok[0] == 'm') {
+ /*
+ * m<R>[:<disp>][:<size>]: memory arg [reg + disp32],
+ * size 4 (u32 load) or 8 (u64 load, default)
+ */
+ char *colon = strchr(tok, ':');
+ char *szs = NULL;
+ unsigned long reg;
+ long long disp = 0;
+ unsigned long size = 8;
+
+ if (colon) {
+ *colon = '\0';
+ szs = strchr(colon + 1, ':');
+ if (szs)
+ *szs++ = '\0';
+ }
+ if (kstrtoul(tok + 1, 10, ®) || reg > 15) {
+ pr_err("uprobe_ptwrite_test: bad mem reg '%s'\n", tok);
+ goto err;
+ }
+ if (colon && kstrtoll(colon + 1, 0, &disp)) {
+ pr_err("uprobe_ptwrite_test: bad mem disp '%s'\n",
+ colon + 1);
+ goto err;
+ }
+ if (szs && (kstrtoul(szs, 10, &size) ||
+ (size != 4 && size != 8))) {
+ pr_err("uprobe_ptwrite_test: bad mem size '%s'\n",
+ szs);
+ goto err;
+ }
+ a->src = UPROBE_PTW_SRC_MEM;
+ a->reg = reg;
+ a->val = (u64)(s32)disp;
+ a->size = size;
+ desc.flags |= UPROBE_PTWRITE_FL_ALLOW_MEM;
} else {
pr_err("uprobe_ptwrite_test: bad arg '%s'\n", tok);
goto err;
--
2.54.0
next prev parent reply other threads:[~2026-08-31 15:07 UTC|newest]
Thread overview: 41+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-31 15:04 [RFC] ptwrite uprobes Andi Kleen
2026-08-31 15:04 ` [RFC v1 01/19] uprobes: guard trace cleanup against error pointers Andi Kleen
2026-08-31 18:15 ` sashiko-bot
2026-09-01 0:49 ` Masami Hiramatsu
2026-08-31 15:04 ` [RFC v1 02/19] uprobes: Correctly reject anonymous VMAs for breakpoint installation Andi Kleen
2026-08-31 18:29 ` sashiko-bot
2026-08-31 15:04 ` [RFC v1 03/19] uprobes: Print warning for missing breakpoint install Andi Kleen
2026-08-31 18:42 ` sashiko-bot
2026-08-31 15:04 ` [RFC v1 04/19] ptwrite uprobes: Add infrastructure for ptwrite uprobes Andi Kleen
2026-08-31 18:55 ` sashiko-bot
2026-08-31 15:04 ` [RFC v1 05/19] ptwrite uprobes: Add minimal low level support for x86 Andi Kleen
2026-08-31 19:11 ` sashiko-bot
2026-09-02 16:35 ` Lorenzo Stoakes (ARM)
2026-08-31 15:04 ` [RFC v1 06/19] ptwrite uprobes: Add a sample module to exercise interface Andi Kleen
2026-08-31 19:19 ` sashiko-bot
2026-08-31 15:04 ` [RFC v1 07/19] ptwrite uprobes: Add support to tracing infrastructure Andi Kleen
2026-08-31 19:31 ` sashiko-bot
2026-08-31 15:04 ` [RFC v1 08/19] ptwrite uprobes / x86: Add a user fault notifier chain Andi Kleen
2026-08-31 19:38 ` sashiko-bot
2026-08-31 15:04 ` [RFC v1 09/19] ptwrite uprobes: Factor file-backed instruction reads Andi Kleen
2026-08-31 19:45 ` sashiko-bot
2026-08-31 15:04 ` Andi Kleen [this message]
2026-08-31 19:59 ` [RFC v1 10/19] ptwrite uprobes: Minimal memory references and fault handling sashiko-bot
2026-08-31 15:04 ` [RFC v1 11/19] ptwrite uprobes: Add multinop support Andi Kleen
2026-08-31 20:09 ` sashiko-bot
2026-08-31 15:04 ` [RFC v1 12/19] ptwrite uprobes: Add pacing to the probes Andi Kleen
2026-08-31 20:19 ` sashiko-bot
2026-08-31 15:04 ` [RFC v1 13/19] ptwrite uprobes: Support instruction puning Andi Kleen
2026-08-31 20:39 ` sashiko-bot
2026-08-31 15:04 ` [RFC v1 14/19] ptwrite uprobes: Use atomic patching for multinop sites Andi Kleen
2026-08-31 21:08 ` sashiko-bot
2026-08-31 15:04 ` [RFC v1 15/19] ptwrite uprobes: Add a tutorial and overview documentation Andi Kleen
2026-08-31 21:10 ` sashiko-bot
2026-08-31 15:04 ` [RFC v1 16/19] ptwrite uprobes / perf tools pt: Improve FUP error handling for ptwrite Andi Kleen
2026-08-31 21:19 ` sashiko-bot
2026-08-31 15:04 ` [RFC v1 17/19] ptwrite uprobes / perf tools probe: Add support of ptwrite probes Andi Kleen
2026-08-31 21:32 ` sashiko-bot
2026-08-31 15:04 ` [RFC v1 18/19] ptwrite uprobes / perf tools script: Add ptwrite uprobes decoder Andi Kleen
2026-08-31 21:39 ` sashiko-bot
2026-08-31 15:04 ` [RFC v1 19/19] ptwrite uprobes: Add self tests Andi Kleen
2026-08-31 21:47 ` sashiko-bot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260831150651.1134594-11-ak@kernel.org \
--to=ak@kernel.org \
--cc=adrian.hunter@intel.com \
--cc=jolsa@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-perf-users@vger.kernel.org \
--cc=mhiramat@kernel.org \
--cc=oleg@redhat.com \
--cc=peterz@infradead.org \
--cc=tglx@kernel.org \
--cc=x86@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.