sparc: bpf_jit: fix support for ldx/stx mem and SKF_AD_VLAN_TAG
authorAlexei Starovoitov <ast@plumgrid.com>
Wed, 17 Sep 2014 01:32:58 +0000 (18:32 -0700)
committerDavid S. Miller <davem@davemloft.net>
Fri, 19 Sep 2014 20:01:18 +0000 (16:01 -0400)
fix several issues in sparc BPF JIT compiler.

ldx/stx related:
. classic BPF instructions that access mem[] slots were not setting
  SEEN_MEM flag, so stack wasn't allocated. Fix that by advertising
  correct flags

. LDX/STX instructions were missing SEEN_XREG, so register value
  could have leaked to user space. Fix it.

. since stack for mem[] slots is allocated with 'sub %sp' instead
  of 'save %sp', use %sp as base register instead of %fp.

. ldx mem[0] means first slot in classic BPF which should have
  -4 offset instead of 0.

. sparc64 needs 2047 stack bias as per ABI to access stack

. emit_stmem() was using LD32I macro instead of ST32I

SKF_AD_VLAN_TAG* related:
. SKF_AD_VLAN_TAG_PRESENT must return 1 or 0 instead of '> 0' or 0
  as per classic BPF de facto standard

. SKF_AD_VLAN_TAG needs to mask the field correctly

Fixes: 2809a2087cc4 ("net: filter: Just In Time compiler for sparc")
Signed-off-by: Alexei Starovoitov <ast@plumgrid.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
arch/sparc/net/bpf_jit_comp.c

index 1f76c22a6a75d64a5dafd6f24da19eec6c816e96..51ae87b483e0ec5884bb0bae14a2740f5674277e 100644 (file)
@@ -234,12 +234,18 @@ do {      BUILD_BUG_ON(FIELD_SIZEOF(STRUCT, FIELD) != sizeof(u8));        \
        __emit_load8(BASE, STRUCT, FIELD, DEST);                        \
 } while (0)
 
-#define emit_ldmem(OFF, DEST)                                  \
-do {   *prog++ = LD32I | RS1(FP) | S13(-(OFF)) | RD(DEST);     \
+#ifdef CONFIG_SPARC64
+#define BIAS (STACK_BIAS - 4)
+#else
+#define BIAS (-4)
+#endif
+
+#define emit_ldmem(OFF, DEST)                                          \
+do {   *prog++ = LD32I | RS1(SP) | S13(BIAS - (OFF)) | RD(DEST);       \
 } while (0)
 
-#define emit_stmem(OFF, SRC)                                   \
-do {   *prog++ = LD32I | RS1(FP) | S13(-(OFF)) | RD(SRC);      \
+#define emit_stmem(OFF, SRC)                                           \
+do {   *prog++ = ST32I | RS1(SP) | S13(BIAS - (OFF)) | RD(SRC);        \
 } while (0)
 
 #ifdef CONFIG_SMP
@@ -615,10 +621,11 @@ void bpf_jit_compile(struct bpf_prog *fp)
                        case BPF_ANC | SKF_AD_VLAN_TAG:
                        case BPF_ANC | SKF_AD_VLAN_TAG_PRESENT:
                                emit_skb_load16(vlan_tci, r_A);
-                               if (code == (BPF_ANC | SKF_AD_VLAN_TAG)) {
-                                       emit_andi(r_A, VLAN_VID_MASK, r_A);
+                               if (code != (BPF_ANC | SKF_AD_VLAN_TAG)) {
+                                       emit_alu_K(SRL, 12);
+                                       emit_andi(r_A, 1, r_A);
                                } else {
-                                       emit_loadimm(VLAN_TAG_PRESENT, r_TMP);
+                                       emit_loadimm(~VLAN_TAG_PRESENT, r_TMP);
                                        emit_and(r_A, r_TMP, r_A);
                                }
                                break;
@@ -630,15 +637,19 @@ void bpf_jit_compile(struct bpf_prog *fp)
                                emit_loadimm(K, r_X);
                                break;
                        case BPF_LD | BPF_MEM:
+                               seen |= SEEN_MEM;
                                emit_ldmem(K * 4, r_A);
                                break;
                        case BPF_LDX | BPF_MEM:
+                               seen |= SEEN_MEM | SEEN_XREG;
                                emit_ldmem(K * 4, r_X);
                                break;
                        case BPF_ST:
+                               seen |= SEEN_MEM;
                                emit_stmem(K * 4, r_A);
                                break;
                        case BPF_STX:
+                               seen |= SEEN_MEM | SEEN_XREG;
                                emit_stmem(K * 4, r_X);
                                break;