Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: bpf: arm: make hole-faulting more robust

Will Deacon pointed out, that the currently used opcode for filling holes,
that is 0xe7ffffff, seems not robust enough ...

$ echo 0xffffffe7 | xxd -r > test.bin
$ arm-linux-gnueabihf-objdump -m arm -D -b binary test.bin
...
0: e7ffffff udf #65535 ; 0xffff

... while for Thumb, it ends up as ...

0: ffff e7ff vqshl.u64 q15, <illegal reg q15.5>, #63

... which is a bit fragile. The ARM specification defines some *permanently*
guaranteed undefined instruction (UDF) space, for example for ARM in ARMv7-AR,
section A5.4 and for Thumb in ARMv7-M, section A5.2.6.

Similarly, ptrace, kprobes, kgdb, bug and uprobes make use of such instruction
as well to trap. Given mentioned section from the specification, we can find
such a universe as (where 'x' denotes 'don't care'):

ARM: xxxx 0111 1111 xxxx xxxx xxxx 1111 xxxx
Thumb: 1101 1110 xxxx xxxx

We therefore should use a more robust opcode that fits both. Russell King
suggested that we can even reuse a single 32-bit word, that is, 0xe7fddef1
which will fault if executed in ARM *or* Thumb mode as done in f928d4f2a86f
("ARM: poison the vectors page"). That will still hold our requirements:

$ echo 0xf1defde7 | xxd -r > test.bin
$ arm-unknown-linux-gnueabi-objdump -m arm -D -b binary test.bin
...
0: e7fddef1 udf #56801 ; 0xdde1
$ echo 0xf1defde7f1defde7f1defde7 | xxd -r > test.bin
$ arm-unknown-linux-gnueabi-objdump -marm -Mforce-thumb -D -b binary test.bin
...
0: def1 udf #241 ; 0xf1
2: e7fd b.n 0x0
4: def1 udf #241 ; 0xf1
6: e7fd b.n 0x4
8: def1 udf #241 ; 0xf1
a: e7fd b.n 0x8

So on ARM 0xe7fddef1 conforms to the above UDF pattern, and the low 16 bit
likewise correspond to UDF in Thumb case. The 0xe7fd part is an unconditional
branch back to the UDF instruction.

Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
Cc: Russell King <linux@arm.linux.org.uk>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Mircea Gherzan <mgherzan@gmail.com>
Cc: Alexei Starovoitov <ast@plumgrid.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Daniel Borkmann and committed by
David S. Miller
e8b56d55 1f6d8035

+17 -3
+3 -3
arch/arm/net/bpf_jit_32.c
··· 16 16 #include <linux/string.h> 17 17 #include <linux/slab.h> 18 18 #include <linux/if_vlan.h> 19 + 19 20 #include <asm/cacheflush.h> 20 21 #include <asm/hwcap.h> 21 22 #include <asm/opcodes.h> ··· 176 175 177 176 static void jit_fill_hole(void *area, unsigned int size) 178 177 { 179 - /* Insert illegal UND instructions. */ 180 - u32 *ptr, fill_ins = 0xe7ffffff; 178 + u32 *ptr; 181 179 /* We are guaranteed to have aligned memory. */ 182 180 for (ptr = area; size >= sizeof(u32); size -= sizeof(u32)) 183 - *ptr++ = fill_ins; 181 + *ptr++ = __opcode_to_mem_arm(ARM_INST_UDF); 184 182 } 185 183 186 184 static void build_prologue(struct jit_ctx *ctx)
+14
arch/arm/net/bpf_jit_32.h
··· 114 114 115 115 #define ARM_INST_UMULL 0x00800090 116 116 117 + /* 118 + * Use a suitable undefined instruction to use for ARM/Thumb2 faulting. 119 + * We need to be careful not to conflict with those used by other modules 120 + * (BUG, kprobes, etc) and the register_undef_hook() system. 121 + * 122 + * The ARM architecture reference manual guarantees that the following 123 + * instruction space will produce an undefined instruction exception on 124 + * all CPUs: 125 + * 126 + * ARM: xxxx 0111 1111 xxxx xxxx xxxx 1111 xxxx ARMv7-AR, section A5.4 127 + * Thumb: 1101 1110 xxxx xxxx ARMv7-M, section A5.2.6 128 + */ 129 + #define ARM_INST_UDF 0xe7fddef1 130 + 117 131 /* register */ 118 132 #define _AL3_R(op, rd, rn, rm) ((op ## _R) | (rd) << 12 | (rn) << 16 | (rm)) 119 133 /* immediate */