Skip to content

Commit

Permalink
filter: Optimize instruction revalidation code.
Browse files Browse the repository at this point in the history
Since repeating u16 value to u8 value conversion using switch() clause's
case statement is wasteful, this patch introduces u16 to u8 mapping table
and removes most of case statements. As a result, the size of net/core/filter.o
is reduced by about 29% on x86.

Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Acked-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Tetsuo Handa authored and David S. Miller committed Nov 18, 2010
1 parent 9e50e3a commit cba328f
Showing 1 changed file with 72 additions and 159 deletions.
231 changes: 72 additions & 159 deletions net/core/filter.c
Original file line number Diff line number Diff line change
Expand Up @@ -383,185 +383,97 @@ EXPORT_SYMBOL(sk_run_filter);
*/
int sk_chk_filter(struct sock_filter *filter, int flen)
{
struct sock_filter *ftest;
/*
* Valid instructions are initialized to non-0.
* Invalid instructions are initialized to 0.
*/
static const u8 codes[] = {
[BPF_ALU|BPF_ADD|BPF_K] = BPF_S_ALU_ADD_K + 1,
[BPF_ALU|BPF_ADD|BPF_X] = BPF_S_ALU_ADD_X + 1,
[BPF_ALU|BPF_SUB|BPF_K] = BPF_S_ALU_SUB_K + 1,
[BPF_ALU|BPF_SUB|BPF_X] = BPF_S_ALU_SUB_X + 1,
[BPF_ALU|BPF_MUL|BPF_K] = BPF_S_ALU_MUL_K + 1,
[BPF_ALU|BPF_MUL|BPF_X] = BPF_S_ALU_MUL_X + 1,
[BPF_ALU|BPF_DIV|BPF_X] = BPF_S_ALU_DIV_X + 1,
[BPF_ALU|BPF_AND|BPF_K] = BPF_S_ALU_AND_K + 1,
[BPF_ALU|BPF_AND|BPF_X] = BPF_S_ALU_AND_X + 1,
[BPF_ALU|BPF_OR|BPF_K] = BPF_S_ALU_OR_K + 1,
[BPF_ALU|BPF_OR|BPF_X] = BPF_S_ALU_OR_X + 1,
[BPF_ALU|BPF_LSH|BPF_K] = BPF_S_ALU_LSH_K + 1,
[BPF_ALU|BPF_LSH|BPF_X] = BPF_S_ALU_LSH_X + 1,
[BPF_ALU|BPF_RSH|BPF_K] = BPF_S_ALU_RSH_K + 1,
[BPF_ALU|BPF_RSH|BPF_X] = BPF_S_ALU_RSH_X + 1,
[BPF_ALU|BPF_NEG] = BPF_S_ALU_NEG + 1,
[BPF_LD|BPF_W|BPF_ABS] = BPF_S_LD_W_ABS + 1,
[BPF_LD|BPF_H|BPF_ABS] = BPF_S_LD_H_ABS + 1,
[BPF_LD|BPF_B|BPF_ABS] = BPF_S_LD_B_ABS + 1,
[BPF_LD|BPF_W|BPF_LEN] = BPF_S_LD_W_LEN + 1,
[BPF_LD|BPF_W|BPF_IND] = BPF_S_LD_W_IND + 1,
[BPF_LD|BPF_H|BPF_IND] = BPF_S_LD_H_IND + 1,
[BPF_LD|BPF_B|BPF_IND] = BPF_S_LD_B_IND + 1,
[BPF_LD|BPF_IMM] = BPF_S_LD_IMM + 1,
[BPF_LDX|BPF_W|BPF_LEN] = BPF_S_LDX_W_LEN + 1,
[BPF_LDX|BPF_B|BPF_MSH] = BPF_S_LDX_B_MSH + 1,
[BPF_LDX|BPF_IMM] = BPF_S_LDX_IMM + 1,
[BPF_MISC|BPF_TAX] = BPF_S_MISC_TAX + 1,
[BPF_MISC|BPF_TXA] = BPF_S_MISC_TXA + 1,
[BPF_RET|BPF_K] = BPF_S_RET_K + 1,
[BPF_RET|BPF_A] = BPF_S_RET_A + 1,
[BPF_ALU|BPF_DIV|BPF_K] = BPF_S_ALU_DIV_K + 1,
[BPF_LD|BPF_MEM] = BPF_S_LD_MEM + 1,
[BPF_LDX|BPF_MEM] = BPF_S_LDX_MEM + 1,
[BPF_ST] = BPF_S_ST + 1,
[BPF_STX] = BPF_S_STX + 1,
[BPF_JMP|BPF_JA] = BPF_S_JMP_JA + 1,
[BPF_JMP|BPF_JEQ|BPF_K] = BPF_S_JMP_JEQ_K + 1,
[BPF_JMP|BPF_JEQ|BPF_X] = BPF_S_JMP_JEQ_X + 1,
[BPF_JMP|BPF_JGE|BPF_K] = BPF_S_JMP_JGE_K + 1,
[BPF_JMP|BPF_JGE|BPF_X] = BPF_S_JMP_JGE_X + 1,
[BPF_JMP|BPF_JGT|BPF_K] = BPF_S_JMP_JGT_K + 1,
[BPF_JMP|BPF_JGT|BPF_X] = BPF_S_JMP_JGT_X + 1,
[BPF_JMP|BPF_JSET|BPF_K] = BPF_S_JMP_JSET_K + 1,
[BPF_JMP|BPF_JSET|BPF_X] = BPF_S_JMP_JSET_X + 1,
};
int pc;

if (flen == 0 || flen > BPF_MAXINSNS)
return -EINVAL;

/* check the filter code now */
for (pc = 0; pc < flen; pc++) {
ftest = &filter[pc];

/* Only allow valid instructions */
switch (ftest->code) {
case BPF_ALU|BPF_ADD|BPF_K:
ftest->code = BPF_S_ALU_ADD_K;
break;
case BPF_ALU|BPF_ADD|BPF_X:
ftest->code = BPF_S_ALU_ADD_X;
break;
case BPF_ALU|BPF_SUB|BPF_K:
ftest->code = BPF_S_ALU_SUB_K;
break;
case BPF_ALU|BPF_SUB|BPF_X:
ftest->code = BPF_S_ALU_SUB_X;
break;
case BPF_ALU|BPF_MUL|BPF_K:
ftest->code = BPF_S_ALU_MUL_K;
break;
case BPF_ALU|BPF_MUL|BPF_X:
ftest->code = BPF_S_ALU_MUL_X;
break;
case BPF_ALU|BPF_DIV|BPF_X:
ftest->code = BPF_S_ALU_DIV_X;
break;
case BPF_ALU|BPF_AND|BPF_K:
ftest->code = BPF_S_ALU_AND_K;
break;
case BPF_ALU|BPF_AND|BPF_X:
ftest->code = BPF_S_ALU_AND_X;
break;
case BPF_ALU|BPF_OR|BPF_K:
ftest->code = BPF_S_ALU_OR_K;
break;
case BPF_ALU|BPF_OR|BPF_X:
ftest->code = BPF_S_ALU_OR_X;
break;
case BPF_ALU|BPF_LSH|BPF_K:
ftest->code = BPF_S_ALU_LSH_K;
break;
case BPF_ALU|BPF_LSH|BPF_X:
ftest->code = BPF_S_ALU_LSH_X;
break;
case BPF_ALU|BPF_RSH|BPF_K:
ftest->code = BPF_S_ALU_RSH_K;
break;
case BPF_ALU|BPF_RSH|BPF_X:
ftest->code = BPF_S_ALU_RSH_X;
break;
case BPF_ALU|BPF_NEG:
ftest->code = BPF_S_ALU_NEG;
break;
case BPF_LD|BPF_W|BPF_ABS:
ftest->code = BPF_S_LD_W_ABS;
break;
case BPF_LD|BPF_H|BPF_ABS:
ftest->code = BPF_S_LD_H_ABS;
break;
case BPF_LD|BPF_B|BPF_ABS:
ftest->code = BPF_S_LD_B_ABS;
break;
case BPF_LD|BPF_W|BPF_LEN:
ftest->code = BPF_S_LD_W_LEN;
break;
case BPF_LD|BPF_W|BPF_IND:
ftest->code = BPF_S_LD_W_IND;
break;
case BPF_LD|BPF_H|BPF_IND:
ftest->code = BPF_S_LD_H_IND;
break;
case BPF_LD|BPF_B|BPF_IND:
ftest->code = BPF_S_LD_B_IND;
break;
case BPF_LD|BPF_IMM:
ftest->code = BPF_S_LD_IMM;
break;
case BPF_LDX|BPF_W|BPF_LEN:
ftest->code = BPF_S_LDX_W_LEN;
break;
case BPF_LDX|BPF_B|BPF_MSH:
ftest->code = BPF_S_LDX_B_MSH;
break;
case BPF_LDX|BPF_IMM:
ftest->code = BPF_S_LDX_IMM;
break;
case BPF_MISC|BPF_TAX:
ftest->code = BPF_S_MISC_TAX;
break;
case BPF_MISC|BPF_TXA:
ftest->code = BPF_S_MISC_TXA;
break;
case BPF_RET|BPF_K:
ftest->code = BPF_S_RET_K;
break;
case BPF_RET|BPF_A:
ftest->code = BPF_S_RET_A;
break;
struct sock_filter *ftest = &filter[pc];
u16 code = ftest->code;

if (code >= ARRAY_SIZE(codes))
return -EINVAL;
code = codes[code];
/* Undo the '+ 1' in codes[] after validation. */
if (!code--)
return -EINVAL;
/* Some instructions need special checks */

switch (code) {
case BPF_S_ALU_DIV_K:
/* check for division by zero */
case BPF_ALU|BPF_DIV|BPF_K:
if (ftest->k == 0)
return -EINVAL;
ftest->code = BPF_S_ALU_DIV_K;
break;

/* check for invalid memory addresses */
case BPF_LD|BPF_MEM:
if (ftest->k >= BPF_MEMWORDS)
return -EINVAL;
ftest->code = BPF_S_LD_MEM;
break;
case BPF_LDX|BPF_MEM:
if (ftest->k >= BPF_MEMWORDS)
return -EINVAL;
ftest->code = BPF_S_LDX_MEM;
break;
case BPF_ST:
if (ftest->k >= BPF_MEMWORDS)
return -EINVAL;
ftest->code = BPF_S_ST;
break;
case BPF_STX:
case BPF_S_LD_MEM:
case BPF_S_LDX_MEM:
case BPF_S_ST:
case BPF_S_STX:
/* check for invalid memory addresses */
if (ftest->k >= BPF_MEMWORDS)
return -EINVAL;
ftest->code = BPF_S_STX;
break;

case BPF_JMP|BPF_JA:
case BPF_S_JMP_JA:
/*
* Note, the large ftest->k might cause loops.
* Compare this with conditional jumps below,
* where offsets are limited. --ANK (981016)
*/
if (ftest->k >= (unsigned)(flen-pc-1))
return -EINVAL;
ftest->code = BPF_S_JMP_JA;
break;

case BPF_JMP|BPF_JEQ|BPF_K:
ftest->code = BPF_S_JMP_JEQ_K;
break;
case BPF_JMP|BPF_JEQ|BPF_X:
ftest->code = BPF_S_JMP_JEQ_X;
break;
case BPF_JMP|BPF_JGE|BPF_K:
ftest->code = BPF_S_JMP_JGE_K;
break;
case BPF_JMP|BPF_JGE|BPF_X:
ftest->code = BPF_S_JMP_JGE_X;
break;
case BPF_JMP|BPF_JGT|BPF_K:
ftest->code = BPF_S_JMP_JGT_K;
break;
case BPF_JMP|BPF_JGT|BPF_X:
ftest->code = BPF_S_JMP_JGT_X;
break;
case BPF_JMP|BPF_JSET|BPF_K:
ftest->code = BPF_S_JMP_JSET_K;
break;
case BPF_JMP|BPF_JSET|BPF_X:
ftest->code = BPF_S_JMP_JSET_X;
break;

default:
return -EINVAL;
}

/* for conditionals both must be safe */
switch (ftest->code) {
case BPF_S_JMP_JEQ_K:
case BPF_S_JMP_JEQ_X:
case BPF_S_JMP_JGE_K:
Expand All @@ -570,21 +482,22 @@ int sk_chk_filter(struct sock_filter *filter, int flen)
case BPF_S_JMP_JGT_X:
case BPF_S_JMP_JSET_X:
case BPF_S_JMP_JSET_K:
/* for conditionals both must be safe */
if (pc + ftest->jt + 1 >= flen ||
pc + ftest->jf + 1 >= flen)
return -EINVAL;
break;
}
ftest->code = code;
}

/* last instruction must be a RET code */
switch (filter[flen - 1].code) {
case BPF_S_RET_K:
case BPF_S_RET_A:
return 0;
break;
default:
return -EINVAL;
}
}
return -EINVAL;
}
EXPORT_SYMBOL(sk_chk_filter);

Expand Down

0 comments on commit cba328f

Please sign in to comment.