Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 11 Apr 2008 20:33:41 +1200
From:      Matthew Luckie <mjl@luckie.org.nz>
To:        freebsd-net@freebsd.org
Subject:   BPF JIT compiler
Message-ID:  <47FF2265.2050308@luckie.org.nz>

next in thread | raw e-mail | index | archive | help
The existing intel BPF JIT compiler has one flaw.  sys/net/bpf_filter.c 
initialises the A and X registers to zero when called.  The just in time 
compiler does not.  This means the JIT compiler will behave differently 
to the interpreter on any filter that does not set the A or X registers 
before using them.

One approach is to put two additional ops in the procedure header of all 
compiled filters to zero these registers.  This is the easiest thing to 
do, though it does mean a slower JIT compiler.  Another approach is to 
reject any filter that might use A or X before setting it.  This 
approach is really no different to having code check the filter and 
conditionally include zero operations if necessary.

Below is a recursive function to check if A or X need to be set in the 
procedure header.  This function would go in bpf_jitter.c and set two 
additional struct members in bpf_jit_filter to allow the machdep code to 
do the right thing.

To my way of thinking, the bpf_insn_seta, bpf_insn_usea, bpf_insn_setx, 
bpf_insn_seta functions could perhaps be macros.  Of greater concern to 
me is any policy that may exist on recursion in the kernel.

Comments, please.  jkim@ is busy with other things, so if you're 
interested, please speak up.

static int bpf_insn_seta(const struct bpf_insn *ins)
{
   if(BPF_CLASS(ins->code) == BPF_LD || ins->code == (BPF_MISC|BPF_TXA))
     {
       return 1;
     }
   return 0;
}

static int bpf_insn_usea(const struct bpf_insn *ins)
{
   if(BPF_CLASS(ins->code) == BPF_ALU ||
      (BPF_CLASS(ins->code) == BPF_JMP && BPF_OP(ins->code) != BPF_JA) ||
      ins->code == (BPF_RET|BPF_A) ||
      ins->code == (BPF_ST) ||
      ins->code == (BPF_MISC|BPF_TAX))
     {
       return 1;
     }
   return 0;
}

static int bpf_insn_setx(const struct bpf_insn *ins)
{
   if(BPF_CLASS(ins->code) == BPF_LDX || ins->code == (BPF_MISC|BPF_TAX))
     {
       return 1;
     }
   return 0;
}

static int bpf_insn_usex(const struct bpf_insn *ins)
{
   if((BPF_CLASS(ins->code) == BPF_ALU && BPF_SRC(ins->code) == BPF_X) ||
      (BPF_CLASS(ins->code) == BPF_LD && BPF_MODE(ins->code) == BPF_IND) ||
      (BPF_CLASS(ins->code) == BPF_JMP && BPF_SRC(ins->code) == BPF_X) ||
      ins->code == (BPF_STX) ||
      ins->code == (BPF_MISC|BPF_TXA))
     {
       return 1;
     }
   return 0;
}

/*
  * bpf_ax
  *
  * determine if we need to initialise the accumulator and index
  * registers.
  */
static void bpf_ax(const struct bpf_insn *fp, int i, int nins, int *a, 
int *x)
{
   const struct bpf_insn *ins;
   int a1, a2, x1, x2;

   while(i<nins)
     {
       ins = &fp[i];

       if(*a == 0)
	{
	  if(bpf_insn_usea(ins) != 0)
	    *a = 1;
	  else if(bpf_insn_seta(ins) != 0)
	    *a = 2;
	}

       if(*x == 0)
	{
	  if(bpf_insn_usex(ins) != 0)
	    *x = 1;
	  else if(bpf_insn_setx(ins) != 0)
	    *x = 2;
	}

       if(*a != 0 && *x != 0)
	break;

       if(BPF_CLASS(ins->code) == BPF_JMP)
	{
	  if(BPF_OP(ins->code) == BPF_JA)
	    {
	      bpf_ax(fp, i+1+ins->k, nins, a, x);
	    }
	  else
	    {
	      a1 = a2 = *a;
	      x1 = x2 = *x;
	      bpf_ax(fp, i+1+ins->jt, nins, &a1, &x1);
	      bpf_ax(fp, i+1+ins->jf, nins, &a2, &x2);

	      if(a1 == 1 || a2 == 1)
		*a = 1;
	      else
		*a = 0;
	
	      if(x1 == 1 || x2 == 1)
		*x = 1;
	      else
		*x = 0;
	    }
	  break;
	}

       i++;
     }

   if(*a != 1)
     *a = 0;

   if(*x != 1)
     *x = 0;

   return;
}



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?47FF2265.2050308>