Skip to content

Commit 47cee14

Browse files
Xu KuohaiKernel Patches Daemon
Xu Kuohai
authored and
Kernel Patches Daemon
committed
bpf, arm64: Impelment bpf_arch_text_poke() for arm64
Impelment bpf_arch_text_poke() for arm64, so bpf trampoline code can use it to replace nop with jump, or replace jump with nop. Signed-off-by: Xu Kuohai <[email protected]> Acked-by: Song Liu <[email protected]> Reviewed-by: Jakub Sitnicki <[email protected]>
1 parent 1972d98 commit 47cee14

File tree

2 files changed

+99
-9
lines changed

2 files changed

+99
-9
lines changed

arch/arm64/net/bpf_jit.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@
270270
#define A64_BTI_C A64_HINT(AARCH64_INSN_HINT_BTIC)
271271
#define A64_BTI_J A64_HINT(AARCH64_INSN_HINT_BTIJ)
272272
#define A64_BTI_JC A64_HINT(AARCH64_INSN_HINT_BTIJC)
273+
#define A64_NOP A64_HINT(AARCH64_INSN_HINT_NOP)
273274

274275
/* DMB */
275276
#define A64_DMB_ISH aarch64_insn_gen_dmb(AARCH64_INSN_MB_ISH)

arch/arm64/net/bpf_jit_comp.c

Lines changed: 98 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#include <linux/bitfield.h>
1111
#include <linux/bpf.h>
12+
#include <linux/memory.h>
1213
#include <linux/filter.h>
1314
#include <linux/printk.h>
1415
#include <linux/slab.h>
@@ -18,6 +19,7 @@
1819
#include <asm/cacheflush.h>
1920
#include <asm/debug-monitors.h>
2021
#include <asm/insn.h>
22+
#include <asm/patching.h>
2123
#include <asm/set_memory.h>
2224

2325
#include "bpf_jit.h"
@@ -235,13 +237,13 @@ static bool is_lsi_offset(int offset, int scale)
235237
return true;
236238
}
237239

240+
#define BTI_INSNS (IS_ENABLED(CONFIG_ARM64_BTI_KERNEL) ? 1 : 0)
241+
#define PAC_INSNS (IS_ENABLED(CONFIG_ARM64_PTR_AUTH_KERNEL) ? 1 : 0)
242+
238243
/* Tail call offset to jump into */
239-
#if IS_ENABLED(CONFIG_ARM64_BTI_KERNEL) || \
240-
IS_ENABLED(CONFIG_ARM64_PTR_AUTH_KERNEL)
241-
#define PROLOGUE_OFFSET 9
242-
#else
243-
#define PROLOGUE_OFFSET 8
244-
#endif
244+
#define PROLOGUE_OFFSET (BTI_INSNS + 2 + PAC_INSNS + 8)
245+
/* Offset of nop instruction in bpf prog entry to be poked */
246+
#define POKE_OFFSET (BTI_INSNS + 1)
245247

246248
static int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf)
247249
{
@@ -279,12 +281,15 @@ static int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf)
279281
*
280282
*/
281283

284+
if (IS_ENABLED(CONFIG_ARM64_BTI_KERNEL))
285+
emit(A64_BTI_C, ctx);
286+
287+
emit(A64_MOV(1, A64_R(9), A64_LR), ctx);
288+
emit(A64_NOP, ctx);
289+
282290
/* Sign lr */
283291
if (IS_ENABLED(CONFIG_ARM64_PTR_AUTH_KERNEL))
284292
emit(A64_PACIASP, ctx);
285-
/* BTI landing pad */
286-
else if (IS_ENABLED(CONFIG_ARM64_BTI_KERNEL))
287-
emit(A64_BTI_C, ctx);
288293

289294
/* Save FP and LR registers to stay align with ARM64 AAPCS */
290295
emit(A64_PUSH(A64_FP, A64_LR, A64_SP), ctx);
@@ -1529,3 +1534,87 @@ void bpf_jit_free_exec(void *addr)
15291534
{
15301535
return vfree(addr);
15311536
}
1537+
1538+
static int gen_branch_or_nop(enum aarch64_insn_branch_type type, void *ip,
1539+
void *addr, u32 *insn)
1540+
{
1541+
if (!addr)
1542+
*insn = aarch64_insn_gen_nop();
1543+
else
1544+
*insn = aarch64_insn_gen_branch_imm((unsigned long)ip,
1545+
(unsigned long)addr,
1546+
type);
1547+
1548+
return *insn != AARCH64_BREAK_FAULT ? 0 : -EFAULT;
1549+
}
1550+
1551+
int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type poke_type,
1552+
void *old_addr, void *new_addr)
1553+
{
1554+
int ret;
1555+
u32 old_insn;
1556+
u32 new_insn;
1557+
u32 replaced;
1558+
unsigned long offset = ~0UL;
1559+
enum aarch64_insn_branch_type branch_type;
1560+
char namebuf[KSYM_NAME_LEN];
1561+
1562+
if (!__bpf_address_lookup((unsigned long)ip, NULL, &offset, namebuf))
1563+
/* Only poking bpf text is supported. Since kernel function
1564+
* entry is set up by ftrace, we reply on ftrace to poke kernel
1565+
* functions.
1566+
*/
1567+
return -EINVAL;
1568+
1569+
/* bpf entry */
1570+
if (offset == 0UL)
1571+
/* skip to the nop instruction in bpf prog entry:
1572+
* bti c // if BTI enabled
1573+
* mov x9, x30
1574+
* nop
1575+
*/
1576+
ip = ip + POKE_OFFSET * AARCH64_INSN_SIZE;
1577+
1578+
if (poke_type == BPF_MOD_CALL)
1579+
branch_type = AARCH64_INSN_BRANCH_LINK;
1580+
else
1581+
branch_type = AARCH64_INSN_BRANCH_NOLINK;
1582+
1583+
if (gen_branch_or_nop(branch_type, ip, old_addr, &old_insn) < 0)
1584+
return -EFAULT;
1585+
1586+
if (gen_branch_or_nop(branch_type, ip, new_addr, &new_insn) < 0)
1587+
return -EFAULT;
1588+
1589+
mutex_lock(&text_mutex);
1590+
if (aarch64_insn_read(ip, &replaced)) {
1591+
ret = -EFAULT;
1592+
goto out;
1593+
}
1594+
1595+
if (replaced != old_insn) {
1596+
ret = -EFAULT;
1597+
goto out;
1598+
}
1599+
1600+
/* We call aarch64_insn_patch_text_nosync() to replace instruction
1601+
* atomically, so no other CPUs will fetch a half-new and half-old
1602+
* instruction. But there is chance that another CPU fetches the old
1603+
* instruction after bpf_arch_text_poke() finishes, that is, different
1604+
* CPUs may execute different versions of instructions at the same
1605+
* time before the icache is synchronized by hardware.
1606+
*
1607+
* 1. when a new trampoline is attached, it is not an issue for
1608+
* different CPUs to jump to different trampolines temporarily.
1609+
*
1610+
* 2. when an old trampoline is freed, we should wait for all other
1611+
* CPUs to exit the trampoline and make sure the trampoline is no
1612+
* longer reachable, since bpf_tramp_image_put() function already
1613+
* uses percpu_ref and rcu task to do the sync, no need to call the
1614+
* sync interface here.
1615+
*/
1616+
ret = aarch64_insn_patch_text_nosync(ip, new_insn);
1617+
out:
1618+
mutex_unlock(&text_mutex);
1619+
return ret;
1620+
}

0 commit comments

Comments
 (0)