Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/3] arm64: Fix text-poke byte-order and late PLT/veneer ROX handling
@ 2026-10-10  8:47 Rui Qi
  2026-10-10  8:47 ` [PATCH 1/3] arm64/module: Fix big-endian breakage in late data relocations Rui Qi
  2026-10-10  8:50 ` [PATCH 2/3] arm64: patching: Fix instruction byte order in aarch64_insn_set() Rui Qi
  0 siblings, 2 replies; 5+ messages in thread
From: Rui Qi @ 2026-10-10  8:47 UTC (permalink / raw)
  To: catalin.marinas, will
  Cc: mark.rutland, ardb, song, dylanbhatch, linux-arm-kernel,
	linux-kernel, Rui Qi

This series fixes three bugs in the arm64 text-poke / module relocation
path, all introduced by the recent text-poke rework (451c3cab9a65
"arm64: patching: implement text_poke API" and 91b89a634487
"arm64/module: Use text-poke API for late relocations.").  The first
two are big-endian byte-order issues; the third is a panic when
livepatch emits PLT entries into read-only executable memory.

Patch 1 fixes big-endian breakage in late data relocations.  The
WRITE_PLACE() macro declared its temporary with the type of the source
argument (s64), but aarch64_insn_copy() copies only sizeof(*place)
bytes from the start of that temporary.  On a big-endian kernel the
most significant bytes sit at the lowest address, so a 16- or 32-bit
relocation copies the sign-extension upper half instead of the
computed value.  Typing the temporary after the destination element
fixes this.

Patch 2 fixes big-endian breakage in aarch64_insn_set().  The
instruction is passed to text_poke_memset() in host byte order, but
A64 instructions must be little-endian in memory.  On a big-endian
kernel the written bytes are reversed and the CPU takes an UNDEFINED
instruction exception when fetching them.  This is reachable in-tree:
bpf_arch_text_invalidate() fills freed JIT regions with
AARCH64_BREAK_FAULT through aarch64_insn_set(), and arm64 selects
HAVE_EBPF_JIT without a big-endian guard.  Converting to little-endian
before the memset, mirroring aarch64_insn_write(), fixes this.

Patch 3 (the main fix) routes PLT/veneer emission through the
text-poke API.  module_emit_plt_entry() and module_emit_veneer_for_adrp()
wrote PLT entries into the .plt section by direct assignment, but once
a module is fully formed its .plt is read-only/executable.  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.  Routing through the text-poke API (mirroring WRITE_PLACE())
maps the page through the fixmap, avoiding the fault.

All three are tagged for stable.

Base-commit: next-20261008

Rui Qi (3):
  arm64/module: Fix big-endian breakage in late data relocations
  arm64: patching: Fix instruction byte order in aarch64_insn_set()
  arm64: module: Use text-poke for late PLT/veneer emission

 arch/arm64/kernel/module-plts.c | 35 ++++++++++++++++++++++++++++++---
 arch/arm64/kernel/module.c      |  2 +-
 arch/arm64/kernel/patching.c    |  4 +++-
 3 files changed, 36 insertions(+), 5 deletions(-)

-- 
2.20.1


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 1/3] arm64/module: Fix big-endian breakage in late data relocations
  2026-10-10  8:47 [PATCH 0/3] arm64: Fix text-poke byte-order and late PLT/veneer ROX handling Rui Qi
@ 2026-10-10  8:47 ` Rui Qi
  2026-10-10  8:50 ` [PATCH 2/3] arm64: patching: Fix instruction byte order in aarch64_insn_set() Rui Qi
  1 sibling, 0 replies; 5+ messages in thread
From: Rui Qi @ 2026-10-10  8:47 UTC (permalink / raw)
  To: catalin.marinas, will
  Cc: mark.rutland, ardb, song, dylanbhatch, linux-arm-kernel,
	linux-kernel, Rui Qi, stable

The WRITE_PLACE macro declares its temporary value with the type of the
source argument:

    __typeof__(val) __val = (val);

For the 16-bit and 32-bit data relocations in reloc_data(), val is the
s64 result of do_reloc(), so __val is an 8-byte integer.  When the
module is already loaded (state != MODULE_STATE_UNFORMED), the macro
hands the address of __val to aarch64_insn_copy() and asks it to copy
only sizeof(*place) -- 2 or 4 -- bytes.

aarch64_insn_copy() performs a raw byte copy with no endian conversion.
On a big-endian kernel the most significant bytes of the s64 sit at the
lowest address, so copying only the first 2/4 bytes grabs the
sign-extension upper half instead of the computed relocation value,
corrupting the patched location.  The UNFORMED direct-assignment path
is unaffected because the compiler narrows the s64 to the destination
type.

Type the temporary after the destination element instead.

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>
---
 arch/arm64/kernel/module.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm64/kernel/module.c b/arch/arm64/kernel/module.c
index 24adb581af0e..0fdf4c1c4e05 100644
--- a/arch/arm64/kernel/module.c
+++ b/arch/arm64/kernel/module.c
@@ -50,7 +50,7 @@ static u64 do_reloc(enum aarch64_reloc_op reloc_op, __le32 *place, u64 val)
 }
 
 #define WRITE_PLACE(place, val, mod) do {				\
-	__typeof__(val) __val = (val);					\
+	__typeof__(*place) __val = (val);					\
 									\
 	if (mod->state == MODULE_STATE_UNFORMED)			\
 		*(place) = __val;					\
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 2/3] arm64: patching: Fix instruction byte order in aarch64_insn_set()
  2026-10-10  8:47 [PATCH 0/3] arm64: Fix text-poke byte-order and late PLT/veneer ROX handling Rui Qi
  2026-10-10  8:47 ` [PATCH 1/3] arm64/module: Fix big-endian breakage in late data relocations Rui Qi
@ 2026-10-10  8:50 ` Rui Qi
  2026-10-10  8:50   ` [PATCH 3/3] arm64: module: Use text-poke for late PLT/veneer emission Rui Qi
  2026-10-15 16:57   ` [PATCH 2/3] arm64: patching: Fix instruction byte order in aarch64_insn_set() Catalin Marinas
  1 sibling, 2 replies; 5+ messages in thread
From: Rui Qi @ 2026-10-10  8:50 UTC (permalink / raw)
  To: catalin.marinas, will
  Cc: mark.rutland, ardb, song, dylanbhatch, linux-arm-kernel,
	linux-kernel, Rui Qi, stable

aarch64_insn_set() passes the host-endian u32 instruction straight to
text_poke_memset(), which memset32()'s it into memory in host byte order.
AArch64 instructions must be little-endian in memory, so on a big-endian
kernel the written bytes are reversed and the CPU takes an UNDEFINED
instruction exception when fetching them.  The sibling aarch64_insn_write()
already converts with cpu_to_le32(); aarch64_insn_set() does not.

This is reachable in-tree: bpf_arch_text_invalidate() fills freed JIT
regions with AARCH64_BREAK_FAULT through aarch64_insn_set(), and arm64
selects HAVE_EBPF_JIT without a big-endian guard, so the path is taken on
BE kernels with BPF JIT.  The direct-fill loop in the same file already
cpu_to_le32()'s the constant, confirming the expected byte order.

Convert the instruction to little-endian before the memset, mirroring
aarch64_insn_write().  On LE the conversion is a no-op, so behaviour is
unchanged.

Fixes: 451c3cab9a65e656c3b3d106831fc02d56b8c34a ("arm64: patching: implement text_poke API")
Cc: stable@vger.kernel.org
Signed-off-by: Rui Qi <qirui.001@bytedance.com>
---
 arch/arm64/kernel/patching.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/kernel/patching.c b/arch/arm64/kernel/patching.c
index 09f019c6547a..ffd790b2b277 100644
--- a/arch/arm64/kernel/patching.c
+++ b/arch/arm64/kernel/patching.c
@@ -170,10 +170,12 @@ noinstr void *aarch64_insn_copy(void *dst, void *src, size_t len)
  */
 noinstr void *aarch64_insn_set(void *dst, u32 insn, size_t len)
 {
+	__le32 __insn = cpu_to_le32(insn);
+
 	if ((uintptr_t)dst & 0x3)
 		return NULL;
 
-	return __text_poke(text_poke_memset, dst, &insn, len);
+	return __text_poke(text_poke_memset, dst, &__insn, len);
 }
 
 int __kprobes aarch64_insn_patch_text_nosync(void *addr, u32 insn)
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 3/3] arm64: module: Use text-poke for late PLT/veneer emission
  2026-10-10  8:50 ` [PATCH 2/3] arm64: patching: Fix instruction byte order in aarch64_insn_set() Rui Qi
@ 2026-10-10  8:50   ` Rui Qi
  2026-10-15 16:57   ` [PATCH 2/3] arm64: patching: Fix instruction byte order in aarch64_insn_set() Catalin Marinas
  1 sibling, 0 replies; 5+ messages in thread
From: Rui Qi @ 2026-10-10  8:50 UTC (permalink / raw)
  To: catalin.marinas, will
  Cc: mark.rutland, ardb, song, dylanbhatch, linux-arm-kernel,
	linux-kernel, Rui Qi, stable

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 text-poke API, mirroring WRITE_PLACE().

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>
---
 arch/arm64/kernel/module-plts.c | 35 ++++++++++++++++++++++++++++++---
 1 file changed, 32 insertions(+), 3 deletions(-)

diff --git a/arch/arm64/kernel/module-plts.c b/arch/arm64/kernel/module-plts.c
index 7afd370da9f4..fa89ed014bd1 100644
--- a/arch/arm64/kernel/module-plts.c
+++ b/arch/arm64/kernel/module-plts.c
@@ -9,6 +9,29 @@
 #include <linux/module.h>
 #include <linux/moduleloader.h>
 #include <linux/sort.h>
+#include <linux/text-patching.h>
+
+/*
+ * The .plt section is marked ROX once the module is fully formed, so
+ * late (livepatch) relocations that emit PLT entries cannot use direct
+ * assignment.  Mirror WRITE_PLACE() and route the write through the
+ * text-poke API when the module is already loaded.
+ *
+ * Return: 0 on success, -EINVAL if the text-poke write failed.
+ */
+static int plt_entry_write(struct plt_entry *plt,
+			   struct plt_entry *entry, struct module *mod)
+{
+	if (mod->state == MODULE_STATE_UNFORMED) {
+		*plt = *entry;
+		return 0;
+	}
+
+	if (!aarch64_insn_copy(plt, entry, sizeof(*plt)))
+		return -EINVAL;
+
+	return 0;
+}
 
 static struct plt_entry __get_adrp_add_pair(u64 dst, u64 pc,
 					    enum aarch64_insn_register reg)
@@ -76,11 +99,14 @@ 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]);
+	if (plt_entry_write(&plt[i], &entry, mod))
+		return 0;
 
 	/*
 	 * Check if the entry we just created is a duplicate. Given that the
@@ -107,6 +133,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 +148,10 @@ 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);
+	if (plt_entry_write(&plt[i], &entry, mod))
+		return 0;
 
 	return (u64)&plt[i];
 }
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH 2/3] arm64: patching: Fix instruction byte order in aarch64_insn_set()
  2026-10-10  8:50 ` [PATCH 2/3] arm64: patching: Fix instruction byte order in aarch64_insn_set() Rui Qi
  2026-10-10  8:50   ` [PATCH 3/3] arm64: module: Use text-poke for late PLT/veneer emission Rui Qi
@ 2026-10-15 16:57   ` Catalin Marinas
  1 sibling, 0 replies; 5+ messages in thread
From: Catalin Marinas @ 2026-10-15 16:57 UTC (permalink / raw)
  To: Rui Qi
  Cc: will, mark.rutland, ardb, song, dylanbhatch, linux-arm-kernel,
	linux-kernel, stable

On Sat, Oct 10, 2026 at 04:50:54PM +0800, Rui Qi wrote:
> aarch64_insn_set() passes the host-endian u32 instruction straight to
> text_poke_memset(), which memset32()'s it into memory in host byte order.
> AArch64 instructions must be little-endian in memory, so on a big-endian
> kernel the written bytes are reversed and the CPU takes an UNDEFINED
> instruction exception when fetching them.  The sibling aarch64_insn_write()
> already converts with cpu_to_le32(); aarch64_insn_set() does not.
> 
> This is reachable in-tree: bpf_arch_text_invalidate() fills freed JIT
> regions with AARCH64_BREAK_FAULT through aarch64_insn_set(), and arm64
> selects HAVE_EBPF_JIT without a big-endian guard, so the path is taken on
> BE kernels with BPF JIT.  The direct-fill loop in the same file already
> cpu_to_le32()'s the constant, confirming the expected byte order.
> 
> Convert the instruction to little-endian before the memset, mirroring
> aarch64_insn_write().  On LE the conversion is a no-op, so behaviour is
> unchanged.

We are just about to remove big-endian support (in 7.4).

-- 
Catalin


^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-10-15 16:58 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-10  8:47 [PATCH 0/3] arm64: Fix text-poke byte-order and late PLT/veneer ROX handling Rui Qi
2026-10-10  8:47 ` [PATCH 1/3] arm64/module: Fix big-endian breakage in late data relocations Rui Qi
2026-10-10  8:50 ` [PATCH 2/3] arm64: patching: Fix instruction byte order in aarch64_insn_set() Rui Qi
2026-10-10  8:50   ` [PATCH 3/3] arm64: module: Use text-poke for late PLT/veneer emission Rui Qi
2026-10-15 16:57   ` [PATCH 2/3] arm64: patching: Fix instruction byte order in aarch64_insn_set() Catalin Marinas

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox