diff options
author | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2018-04-23 08:31:36 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2018-05-07 15:12:43 -0400 |
commit | de5cb6eb514ebe241e3edeb290cb41deb380b81d (patch) | |
tree | 67d7c8cfa87e8ea7cfcd35e43efa325815f27106 | |
parent | 6deaa3bbca804b2a3627fd685f75de64da7be535 (diff) |
s390: use expoline thunks in the BPF JIT
The BPF JIT need safe guarding against spectre v2 in the sk_load_xxx
assembler stubs and the indirect branches generated by the JIT itself
need to be converted to expolines.
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
-rw-r--r-- | arch/s390/net/bpf_jit.S | 16 | ||||
-rw-r--r-- | arch/s390/net/bpf_jit_comp.c | 63 |
2 files changed, 71 insertions, 8 deletions
diff --git a/arch/s390/net/bpf_jit.S b/arch/s390/net/bpf_jit.S index 25bb4643c4f4..9f794869c1b0 100644 --- a/arch/s390/net/bpf_jit.S +++ b/arch/s390/net/bpf_jit.S | |||
@@ -9,6 +9,7 @@ | |||
9 | */ | 9 | */ |
10 | 10 | ||
11 | #include <linux/linkage.h> | 11 | #include <linux/linkage.h> |
12 | #include <asm/nospec-insn.h> | ||
12 | #include "bpf_jit.h" | 13 | #include "bpf_jit.h" |
13 | 14 | ||
14 | /* | 15 | /* |
@@ -54,7 +55,7 @@ ENTRY(sk_load_##NAME##_pos); \ | |||
54 | clg %r3,STK_OFF_HLEN(%r15); /* Offset + SIZE > hlen? */ \ | 55 | clg %r3,STK_OFF_HLEN(%r15); /* Offset + SIZE > hlen? */ \ |
55 | jh sk_load_##NAME##_slow; \ | 56 | jh sk_load_##NAME##_slow; \ |
56 | LOAD %r14,-SIZE(%r3,%r12); /* Get data from skb */ \ | 57 | LOAD %r14,-SIZE(%r3,%r12); /* Get data from skb */ \ |
57 | b OFF_OK(%r6); /* Return */ \ | 58 | B_EX OFF_OK,%r6; /* Return */ \ |
58 | \ | 59 | \ |
59 | sk_load_##NAME##_slow:; \ | 60 | sk_load_##NAME##_slow:; \ |
60 | lgr %r2,%r7; /* Arg1 = skb pointer */ \ | 61 | lgr %r2,%r7; /* Arg1 = skb pointer */ \ |
@@ -64,11 +65,14 @@ sk_load_##NAME##_slow:; \ | |||
64 | brasl %r14,skb_copy_bits; /* Get data from skb */ \ | 65 | brasl %r14,skb_copy_bits; /* Get data from skb */ \ |
65 | LOAD %r14,STK_OFF_TMP(%r15); /* Load from temp bufffer */ \ | 66 | LOAD %r14,STK_OFF_TMP(%r15); /* Load from temp bufffer */ \ |
66 | ltgr %r2,%r2; /* Set cc to (%r2 != 0) */ \ | 67 | ltgr %r2,%r2; /* Set cc to (%r2 != 0) */ \ |
67 | br %r6; /* Return */ | 68 | BR_EX %r6; /* Return */ |
68 | 69 | ||
69 | sk_load_common(word, 4, llgf) /* r14 = *(u32 *) (skb->data+offset) */ | 70 | sk_load_common(word, 4, llgf) /* r14 = *(u32 *) (skb->data+offset) */ |
70 | sk_load_common(half, 2, llgh) /* r14 = *(u16 *) (skb->data+offset) */ | 71 | sk_load_common(half, 2, llgh) /* r14 = *(u16 *) (skb->data+offset) */ |
71 | 72 | ||
73 | GEN_BR_THUNK %r6 | ||
74 | GEN_B_THUNK OFF_OK,%r6 | ||
75 | |||
72 | /* | 76 | /* |
73 | * Load 1 byte from SKB (optimized version) | 77 | * Load 1 byte from SKB (optimized version) |
74 | */ | 78 | */ |
@@ -80,7 +84,7 @@ ENTRY(sk_load_byte_pos) | |||
80 | clg %r3,STK_OFF_HLEN(%r15) # Offset >= hlen? | 84 | clg %r3,STK_OFF_HLEN(%r15) # Offset >= hlen? |
81 | jnl sk_load_byte_slow | 85 | jnl sk_load_byte_slow |
82 | llgc %r14,0(%r3,%r12) # Get byte from skb | 86 | llgc %r14,0(%r3,%r12) # Get byte from skb |
83 | b OFF_OK(%r6) # Return OK | 87 | B_EX OFF_OK,%r6 # Return OK |
84 | 88 | ||
85 | sk_load_byte_slow: | 89 | sk_load_byte_slow: |
86 | lgr %r2,%r7 # Arg1 = skb pointer | 90 | lgr %r2,%r7 # Arg1 = skb pointer |
@@ -90,7 +94,7 @@ sk_load_byte_slow: | |||
90 | brasl %r14,skb_copy_bits # Get data from skb | 94 | brasl %r14,skb_copy_bits # Get data from skb |
91 | llgc %r14,STK_OFF_TMP(%r15) # Load result from temp buffer | 95 | llgc %r14,STK_OFF_TMP(%r15) # Load result from temp buffer |
92 | ltgr %r2,%r2 # Set cc to (%r2 != 0) | 96 | ltgr %r2,%r2 # Set cc to (%r2 != 0) |
93 | br %r6 # Return cc | 97 | BR_EX %r6 # Return cc |
94 | 98 | ||
95 | #define sk_negative_common(NAME, SIZE, LOAD) \ | 99 | #define sk_negative_common(NAME, SIZE, LOAD) \ |
96 | sk_load_##NAME##_slow_neg:; \ | 100 | sk_load_##NAME##_slow_neg:; \ |
@@ -104,7 +108,7 @@ sk_load_##NAME##_slow_neg:; \ | |||
104 | jz bpf_error; \ | 108 | jz bpf_error; \ |
105 | LOAD %r14,0(%r2); /* Get data from pointer */ \ | 109 | LOAD %r14,0(%r2); /* Get data from pointer */ \ |
106 | xr %r3,%r3; /* Set cc to zero */ \ | 110 | xr %r3,%r3; /* Set cc to zero */ \ |
107 | br %r6; /* Return cc */ | 111 | BR_EX %r6; /* Return cc */ |
108 | 112 | ||
109 | sk_negative_common(word, 4, llgf) | 113 | sk_negative_common(word, 4, llgf) |
110 | sk_negative_common(half, 2, llgh) | 114 | sk_negative_common(half, 2, llgh) |
@@ -113,4 +117,4 @@ sk_negative_common(byte, 1, llgc) | |||
113 | bpf_error: | 117 | bpf_error: |
114 | # force a return 0 from jit handler | 118 | # force a return 0 from jit handler |
115 | ltgr %r15,%r15 # Set condition code | 119 | ltgr %r15,%r15 # Set condition code |
116 | br %r6 | 120 | BR_EX %r6 |
diff --git a/arch/s390/net/bpf_jit_comp.c b/arch/s390/net/bpf_jit_comp.c index 78a19c93b380..dd2bcf0e7d00 100644 --- a/arch/s390/net/bpf_jit_comp.c +++ b/arch/s390/net/bpf_jit_comp.c | |||
@@ -25,6 +25,8 @@ | |||
25 | #include <linux/bpf.h> | 25 | #include <linux/bpf.h> |
26 | #include <asm/cacheflush.h> | 26 | #include <asm/cacheflush.h> |
27 | #include <asm/dis.h> | 27 | #include <asm/dis.h> |
28 | #include <asm/facility.h> | ||
29 | #include <asm/nospec-branch.h> | ||
28 | #include <asm/set_memory.h> | 30 | #include <asm/set_memory.h> |
29 | #include "bpf_jit.h" | 31 | #include "bpf_jit.h" |
30 | 32 | ||
@@ -41,6 +43,8 @@ struct bpf_jit { | |||
41 | int base_ip; /* Base address for literal pool */ | 43 | int base_ip; /* Base address for literal pool */ |
42 | int ret0_ip; /* Address of return 0 */ | 44 | int ret0_ip; /* Address of return 0 */ |
43 | int exit_ip; /* Address of exit */ | 45 | int exit_ip; /* Address of exit */ |
46 | int r1_thunk_ip; /* Address of expoline thunk for 'br %r1' */ | ||
47 | int r14_thunk_ip; /* Address of expoline thunk for 'br %r14' */ | ||
44 | int tail_call_start; /* Tail call start offset */ | 48 | int tail_call_start; /* Tail call start offset */ |
45 | int labels[1]; /* Labels for local jumps */ | 49 | int labels[1]; /* Labels for local jumps */ |
46 | }; | 50 | }; |
@@ -250,6 +254,19 @@ static inline void reg_set_seen(struct bpf_jit *jit, u32 b1) | |||
250 | REG_SET_SEEN(b2); \ | 254 | REG_SET_SEEN(b2); \ |
251 | }) | 255 | }) |
252 | 256 | ||
257 | #define EMIT6_PCREL_RILB(op, b, target) \ | ||
258 | ({ \ | ||
259 | int rel = (target - jit->prg) / 2; \ | ||
260 | _EMIT6(op | reg_high(b) << 16 | rel >> 16, rel & 0xffff); \ | ||
261 | REG_SET_SEEN(b); \ | ||
262 | }) | ||
263 | |||
264 | #define EMIT6_PCREL_RIL(op, target) \ | ||
265 | ({ \ | ||
266 | int rel = (target - jit->prg) / 2; \ | ||
267 | _EMIT6(op | rel >> 16, rel & 0xffff); \ | ||
268 | }) | ||
269 | |||
253 | #define _EMIT6_IMM(op, imm) \ | 270 | #define _EMIT6_IMM(op, imm) \ |
254 | ({ \ | 271 | ({ \ |
255 | unsigned int __imm = (imm); \ | 272 | unsigned int __imm = (imm); \ |
@@ -469,8 +486,45 @@ static void bpf_jit_epilogue(struct bpf_jit *jit, u32 stack_depth) | |||
469 | EMIT4(0xb9040000, REG_2, BPF_REG_0); | 486 | EMIT4(0xb9040000, REG_2, BPF_REG_0); |
470 | /* Restore registers */ | 487 | /* Restore registers */ |
471 | save_restore_regs(jit, REGS_RESTORE, stack_depth); | 488 | save_restore_regs(jit, REGS_RESTORE, stack_depth); |
489 | if (IS_ENABLED(CC_USING_EXPOLINE) && !nospec_disable) { | ||
490 | jit->r14_thunk_ip = jit->prg; | ||
491 | /* Generate __s390_indirect_jump_r14 thunk */ | ||
492 | if (test_facility(35)) { | ||
493 | /* exrl %r0,.+10 */ | ||
494 | EMIT6_PCREL_RIL(0xc6000000, jit->prg + 10); | ||
495 | } else { | ||
496 | /* larl %r1,.+14 */ | ||
497 | EMIT6_PCREL_RILB(0xc0000000, REG_1, jit->prg + 14); | ||
498 | /* ex 0,0(%r1) */ | ||
499 | EMIT4_DISP(0x44000000, REG_0, REG_1, 0); | ||
500 | } | ||
501 | /* j . */ | ||
502 | EMIT4_PCREL(0xa7f40000, 0); | ||
503 | } | ||
472 | /* br %r14 */ | 504 | /* br %r14 */ |
473 | _EMIT2(0x07fe); | 505 | _EMIT2(0x07fe); |
506 | |||
507 | if (IS_ENABLED(CC_USING_EXPOLINE) && !nospec_disable && | ||
508 | (jit->seen & SEEN_FUNC)) { | ||
509 | jit->r1_thunk_ip = jit->prg; | ||
510 | /* Generate __s390_indirect_jump_r1 thunk */ | ||
511 | if (test_facility(35)) { | ||
512 | /* exrl %r0,.+10 */ | ||
513 | EMIT6_PCREL_RIL(0xc6000000, jit->prg + 10); | ||
514 | /* j . */ | ||
515 | EMIT4_PCREL(0xa7f40000, 0); | ||
516 | /* br %r1 */ | ||
517 | _EMIT2(0x07f1); | ||
518 | } else { | ||
519 | /* larl %r1,.+14 */ | ||
520 | EMIT6_PCREL_RILB(0xc0000000, REG_1, jit->prg + 14); | ||
521 | /* ex 0,S390_lowcore.br_r1_tampoline */ | ||
522 | EMIT4_DISP(0x44000000, REG_0, REG_0, | ||
523 | offsetof(struct lowcore, br_r1_trampoline)); | ||
524 | /* j . */ | ||
525 | EMIT4_PCREL(0xa7f40000, 0); | ||
526 | } | ||
527 | } | ||
474 | } | 528 | } |
475 | 529 | ||
476 | /* | 530 | /* |
@@ -966,8 +1020,13 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp, int i | |||
966 | /* lg %w1,<d(imm)>(%l) */ | 1020 | /* lg %w1,<d(imm)>(%l) */ |
967 | EMIT6_DISP_LH(0xe3000000, 0x0004, REG_W1, REG_0, REG_L, | 1021 | EMIT6_DISP_LH(0xe3000000, 0x0004, REG_W1, REG_0, REG_L, |
968 | EMIT_CONST_U64(func)); | 1022 | EMIT_CONST_U64(func)); |
969 | /* basr %r14,%w1 */ | 1023 | if (IS_ENABLED(CC_USING_EXPOLINE) && !nospec_disable) { |
970 | EMIT2(0x0d00, REG_14, REG_W1); | 1024 | /* brasl %r14,__s390_indirect_jump_r1 */ |
1025 | EMIT6_PCREL_RILB(0xc0050000, REG_14, jit->r1_thunk_ip); | ||
1026 | } else { | ||
1027 | /* basr %r14,%w1 */ | ||
1028 | EMIT2(0x0d00, REG_14, REG_W1); | ||
1029 | } | ||
971 | /* lgr %b0,%r2: load return value into %b0 */ | 1030 | /* lgr %b0,%r2: load return value into %b0 */ |
972 | EMIT4(0xb9040000, BPF_REG_0, REG_2); | 1031 | EMIT4(0xb9040000, BPF_REG_0, REG_2); |
973 | if ((jit->seen & SEEN_SKB) && | 1032 | if ((jit->seen & SEEN_SKB) && |