* [PATCH v2] arm64: module: Use text-poke for late PLT/veneer emission
@ 2026-11-11 7:39 Rui Qi
2026-11-13 18:23 ` Catalin Marinas
0 siblings, 1 reply; 2+ messages in thread
From: Rui Qi @ 2026-11-11 7:39 UTC (permalink / raw)
To: Catalin Marinas, will
Cc: mark.rutland, ardb, song, dylanbhatch, linux-arm-kernel,
linux-kernel, stable, Rui Qi
module_emit_plt_entry() and module_emit_veneer_for_adrp() write PLT
entries into the .plt section by direct assignment. Once a module is
fully formed its .plt is read-only/executable, so a late (livepatch)
relocation that falls back to PLT emission -- an out-of-range
R_AARCH64_CALL26/JUMP26, or an ADRP under CONFIG_ARM64_ERRATUM_843419
-- triggers a synchronous Data Abort and panics.
This is the same ROX premise that commit 91b89a634487
("arm64/module: Use text-poke API for late relocations.") handled for
the relocation writes through WRITE_PLACE(); the PLT/veneer emitters
were left using direct assignment.
The late path is reachable without remapping:
klp_write_section_relocs() applies .klp.rela.* sections through
apply_relocate_add() with the patch module already MODULE_STATE_LIVE,
so .plt is ROX.
Route the emitters through the same text-poke write path the relocation
code already uses, rather than open-coding a parallel helper.
Fixes: 91b89a634487d5614e51ee773a889ed57f5551ca ("arm64/module: Use text-poke API for late relocations.")
Cc: stable@vger.kernel.org
Signed-off-by: Rui Qi <qirui.001@bytedance.com>
---
Changes since v1 (formerly 3/3 of a 3-patch series, now standalone):
- Dropped the former 1/3 and 2/3 (big-endian byte-order fixups): arm64
big-endian support is being removed in v7.4 (Catalin Marinas), so the
BE-only paths are no longer reachable.
- Incorporated review (Catalin Marinas): unify the relocation write
helper and the open-coded PLT/veneer write helper into a single
module_write_place() macro in asm/module.h, used by module.c and
module-plts.c; drop the redundant aarch64_insn_copy() NULL check
(destination is always sufficiently aligned, __text_poke() !NULL).
arch/arm64/include/asm/module.h | 16 ++++++++++++++++
arch/arm64/kernel/module-plts.c | 10 +++++++---
arch/arm64/kernel/module.c | 22 ++++++----------------
3 files changed, 29 insertions(+), 19 deletions(-)
diff --git a/arch/arm64/include/asm/module.h b/arch/arm64/include/asm/module.h
index fb9b88eebeb1..4a1a47f8ba1a 100644
--- a/arch/arm64/include/asm/module.h
+++ b/arch/arm64/include/asm/module.h
@@ -6,6 +6,7 @@
#define __ASM_MODULE_H
#include <asm-generic/module.h>
+#include <asm/text-patching.h>
struct mod_plt_sec {
int plt_shndx;
@@ -29,6 +30,21 @@ u64 module_emit_plt_entry(struct module *mod, Elf64_Shdr *sechdrs,
u64 module_emit_veneer_for_adrp(struct module *mod, Elf64_Shdr *sechdrs,
void *loc, u64 val);
+/*
+ * Write a relocation value to @place within a module. While the module
+ * is MODULE_STATE_UNFORMED the destination is writable and a direct store
+ * is used; once the module is fully formed the region is read-only and
+ * executable, so the write is routed through the text-poke API.
+ */
+#define module_write_place(place, val, mod) do { \
+ __typeof__(*place) __val = (val); \
+ \
+ if (mod->state == MODULE_STATE_UNFORMED) \
+ *(place) = __val; \
+ else \
+ aarch64_insn_copy(place, &(__val), sizeof(*place)); \
+} while (0)
+
struct plt_entry {
/*
* A program that conforms to the AArch64 Procedure Call Standard
diff --git a/arch/arm64/kernel/module-plts.c b/arch/arm64/kernel/module-plts.c
index 7afd370da9f4..87d623789265 100644
--- a/arch/arm64/kernel/module-plts.c
+++ b/arch/arm64/kernel/module-plts.c
@@ -76,11 +76,13 @@ u64 module_emit_plt_entry(struct module *mod, Elf64_Shdr *sechdrs,
int i = pltsec->plt_num_entries;
int j = i - 1;
u64 val = sym->st_value + rela->r_addend;
+ struct plt_entry entry;
if (is_forbidden_offset_for_adrp(&plt[i].adrp))
i++;
- plt[i] = get_plt_entry(val, &plt[i]);
+ entry = get_plt_entry(val, &plt[i]);
+ module_write_place(&plt[i], entry, mod);
/*
* Check if the entry we just created is a duplicate. Given that the
@@ -107,6 +109,7 @@ u64 module_emit_veneer_for_adrp(struct module *mod, Elf64_Shdr *sechdrs,
int i = pltsec->plt_num_entries++;
u32 br;
int rd;
+ struct plt_entry entry;
if (WARN_ON(pltsec->plt_num_entries > pltsec->plt_max_entries))
return 0;
@@ -121,8 +124,9 @@ u64 module_emit_veneer_for_adrp(struct module *mod, Elf64_Shdr *sechdrs,
br = aarch64_insn_gen_branch_imm((u64)&plt[i].br, (u64)loc + 4,
AARCH64_INSN_BRANCH_NOLINK);
- plt[i] = __get_adrp_add_pair(val, (u64)&plt[i], rd);
- plt[i].br = cpu_to_le32(br);
+ entry = __get_adrp_add_pair(val, (u64)&plt[i], rd);
+ entry.br = cpu_to_le32(br);
+ module_write_place(&plt[i], entry, mod);
return (u64)&plt[i];
}
diff --git a/arch/arm64/kernel/module.c b/arch/arm64/kernel/module.c
index 24adb581af0e..595c358b4f0e 100644
--- a/arch/arm64/kernel/module.c
+++ b/arch/arm64/kernel/module.c
@@ -23,7 +23,6 @@
#include <asm/insn.h>
#include <asm/scs.h>
#include <asm/sections.h>
-#include <asm/text-patching.h>
enum aarch64_reloc_op {
RELOC_OP_NONE,
@@ -49,15 +48,6 @@ static u64 do_reloc(enum aarch64_reloc_op reloc_op, __le32 *place, u64 val)
return 0;
}
-#define WRITE_PLACE(place, val, mod) do { \
- __typeof__(val) __val = (val); \
- \
- if (mod->state == MODULE_STATE_UNFORMED) \
- *(place) = __val; \
- else \
- aarch64_insn_copy(place, &(__val), sizeof(*place)); \
-} while (0)
-
static int reloc_data(enum aarch64_reloc_op op, void *place, u64 val, int len,
struct module *me)
{
@@ -77,7 +67,7 @@ static int reloc_data(enum aarch64_reloc_op op, void *place, u64 val, int len,
switch (len) {
case 16:
- WRITE_PLACE((s16 *)place, sval, me);
+ module_write_place((s16 *)place, sval, me);
switch (op) {
case RELOC_OP_ABS:
if (sval < 0 || sval > U16_MAX)
@@ -93,7 +83,7 @@ static int reloc_data(enum aarch64_reloc_op op, void *place, u64 val, int len,
}
break;
case 32:
- WRITE_PLACE((s32 *)place, sval, me);
+ module_write_place((s32 *)place, sval, me);
switch (op) {
case RELOC_OP_ABS:
if (sval < 0 || sval > U32_MAX)
@@ -109,7 +99,7 @@ static int reloc_data(enum aarch64_reloc_op op, void *place, u64 val, int len,
}
break;
case 64:
- WRITE_PLACE((s64 *)place, sval, me);
+ module_write_place((s64 *)place, sval, me);
break;
default:
pr_err("Invalid length (%d) for data relocation\n", len);
@@ -157,7 +147,7 @@ static int reloc_insn_movw(enum aarch64_reloc_op op, __le32 *place, u64 val,
/* Update the instruction with the new encoding. */
insn = aarch64_insn_encode_immediate(AARCH64_INSN_IMM_16, insn, imm);
- WRITE_PLACE(place, cpu_to_le32(insn), me);
+ module_write_place(place, cpu_to_le32(insn), me);
if (imm > U16_MAX)
return -ERANGE;
@@ -183,7 +173,7 @@ static int reloc_insn_imm(enum aarch64_reloc_op op, __le32 *place, u64 val,
/* Update the instruction's immediate field. */
insn = aarch64_insn_encode_immediate(imm_type, insn, imm);
- WRITE_PLACE(place, cpu_to_le32(insn), me);
+ module_write_place(place, cpu_to_le32(insn), me);
/*
* Extract the upper value bits (including the sign bit) and
@@ -224,7 +214,7 @@ static int reloc_insn_adrp(struct module *mod, Elf64_Shdr *sechdrs,
AARCH64_INSN_BRANCH_NOLINK);
}
- WRITE_PLACE(place, cpu_to_le32(insn), me);
+ module_write_place(place, cpu_to_le32(insn), me);
return 0;
}
--
2.20.1
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [PATCH v2] arm64: module: Use text-poke for late PLT/veneer emission
2026-11-11 7:39 [PATCH v2] arm64: module: Use text-poke for late PLT/veneer emission Rui Qi
@ 2026-11-13 18:23 ` Catalin Marinas
0 siblings, 0 replies; 2+ messages in thread
From: Catalin Marinas @ 2026-11-13 18:23 UTC (permalink / raw)
To: Rui Qi
Cc: will, mark.rutland, ardb, song, dylanbhatch, linux-arm-kernel,
linux-kernel, stable
Thanks Rui for respinning.
On Wed, Nov 11, 2026 at 03:39:06PM +0800, Rui Qi wrote:
> diff --git a/arch/arm64/include/asm/module.h b/arch/arm64/include/asm/module.h
> index fb9b88eebeb1..4a1a47f8ba1a 100644
> --- a/arch/arm64/include/asm/module.h
> +++ b/arch/arm64/include/asm/module.h
> @@ -6,6 +6,7 @@
> #define __ASM_MODULE_H
>
> #include <asm-generic/module.h>
> +#include <asm/text-patching.h>
>
> struct mod_plt_sec {
> int plt_shndx;
> @@ -29,6 +30,21 @@ u64 module_emit_plt_entry(struct module *mod, Elf64_Shdr *sechdrs,
> u64 module_emit_veneer_for_adrp(struct module *mod, Elf64_Shdr *sechdrs,
> void *loc, u64 val);
>
> +/*
> + * Write a relocation value to @place within a module. While the module
> + * is MODULE_STATE_UNFORMED the destination is writable and a direct store
> + * is used; once the module is fully formed the region is read-only and
> + * executable, so the write is routed through the text-poke API.
> + */
> +#define module_write_place(place, val, mod) do { \
> + __typeof__(*place) __val = (val); \
> + \
> + if (mod->state == MODULE_STATE_UNFORMED) \
> + *(place) = __val; \
> + else \
> + aarch64_insn_copy(place, &(__val), sizeof(*place)); \
> +} while (0)
A nit here, I'd move this macro to asm/text-patching.h since
asm/module.h is pulled in by linux/module.h. Even better if you could
make this a function (either static inline or out of line).
> --- a/arch/arm64/kernel/module-plts.c
> +++ b/arch/arm64/kernel/module-plts.c
> @@ -76,11 +76,13 @@ u64 module_emit_plt_entry(struct module *mod, Elf64_Shdr *sechdrs,
> int i = pltsec->plt_num_entries;
> int j = i - 1;
> u64 val = sym->st_value + rela->r_addend;
> + struct plt_entry entry;
>
> if (is_forbidden_offset_for_adrp(&plt[i].adrp))
> i++;
>
> - plt[i] = get_plt_entry(val, &plt[i]);
> + entry = get_plt_entry(val, &plt[i]);
> + module_write_place(&plt[i], entry, mod);
Sashiko reported PLT exhaustion/overrun and ADRP corruption when
relocations are reapplied after unloading and reloading a target module:
https://sashiko.dev/#/patchset/20261111073906.652400-1-qirui.001@bytedance.com
These look like existing problems, only exposed now because we no longer
get the ROX fault. I think they can be addressed in separate follow-up
patches. This patch still fixes the fault on the first application.
However, could we include a bounds check before writing the PLT entry,
maybe with a WARN_ON_ONCE? Once late writes use text-poke, retrying
after slot exhaustion can overwrite memory beyond .plt.
Thanks.
--
Catalin
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-11-13 18:23 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-11 7:39 [PATCH v2] arm64: module: Use text-poke for late PLT/veneer emission Rui Qi
2026-11-13 18:23 ` Catalin Marinas
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.