Module Name: src Committed By: christos Date: Sat Feb 19 01:12:40 UTC 2011
Modified Files: src/sys/net: bpf_filter.c Log Message: Avoid stack memory disclosure by keeping track during filter validation time of initialized memory. Idea taken from linux. To generate a diff of this commit: cvs rdiff -u -r1.41 -r1.42 src/sys/net/bpf_filter.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/net/bpf_filter.c diff -u src/sys/net/bpf_filter.c:1.41 src/sys/net/bpf_filter.c:1.42 --- src/sys/net/bpf_filter.c:1.41 Sun Dec 5 17:40:56 2010 +++ src/sys/net/bpf_filter.c Fri Feb 18 20:12:39 2011 @@ -1,4 +1,4 @@ -/* $NetBSD: bpf_filter.c,v 1.41 2010/12/05 22:40:56 mrg Exp $ */ +/* $NetBSD: bpf_filter.c,v 1.42 2011/02/19 01:12:39 christos Exp $ */ /*- * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997 @@ -37,7 +37,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: bpf_filter.c,v 1.41 2010/12/05 22:40:56 mrg Exp $"); +__KERNEL_RCSID(0, "$NetBSD: bpf_filter.c,v 1.42 2011/02/19 01:12:39 christos Exp $"); #if 0 #if !(defined(lint) || defined(KERNEL)) @@ -461,11 +461,16 @@ * The kernel needs to be able to verify an application's filter code. * Otherwise, a bogus program could easily crash the system. */ +CTASSERT(BPF_MEMWORDS == sizeof(uint16_t) * NBBY); + int bpf_validate(const struct bpf_insn *f, int signed_len) { - u_int i, from, len; + u_int i, from, len, ok = 0; const struct bpf_insn *p; +#if defined(KERNEL) || defined(_KERNEL) + uint16_t *mem, invalid; +#endif len = (u_int)signed_len; if (len < 1) @@ -474,8 +479,19 @@ if (len > BPF_MAXINSNS) return 0; #endif + if (BPF_CLASS(f[len - 1].code) != BPF_RET) + return 0; + +#if defined(KERNEL) || defined(_KERNEL) + mem = malloc(sizeof(*mem) * len, M_TEMP, M_WAITOK|M_ZERO); + invalid = ~0; /* All is invalid on startup */ +#endif for (i = 0; i < len; ++i) { +#if defined(KERNEL) || defined(_KERNEL) + /* blend in any invalid bits for current pc */ + invalid |= mem[i]; +#endif p = &f[i]; switch (BPF_CLASS(p->code)) { /* @@ -496,7 +512,10 @@ * is done runtime. */ if (p->k >= BPF_MEMWORDS) - return 0; + goto out; + /* check for current memory invalid */ + if (invalid & (1 << p->k)) + goto out; #endif break; case BPF_ABS: @@ -506,13 +525,17 @@ case BPF_LEN: break; default: - return 0; + goto out; } break; case BPF_ST: case BPF_STX: if (p->k >= BPF_MEMWORDS) - return 0; + goto out; +#if defined(KERNEL) || defined(_KERNEL) + /* validate the memory word */ + invalid &= ~(1 << p->k); +#endif break; case BPF_ALU: switch (BPF_OP(p->code)) { @@ -530,10 +553,10 @@ * Check for constant division by 0. */ if (BPF_SRC(p->code) == BPF_K && p->k == 0) - return 0; + goto out; break; default: - return 0; + goto out; } break; case BPF_JMP: @@ -566,22 +589,37 @@ from = i + 1; switch (BPF_OP(p->code)) { case BPF_JA: -#if defined(KERNEL) || defined(_KERNEL) - if (from + p->k < from || from + p->k >= len) -#else if (from + p->k >= len) + goto out; +#if defined(KERNEL) || defined(_KERNEL) + if (from + p->k < from) + goto out; + /* + * mark the currently invalid bits for the + * destination + */ + mem[from + p->k] |= invalid; + invalid = 0; #endif - return 0; break; case BPF_JEQ: case BPF_JGT: case BPF_JGE: case BPF_JSET: if (from + p->jt >= len || from + p->jf >= len) - return 0; + goto out; +#if defined(KERNEL) || defined(_KERNEL) + /* + * mark the currently invalid bits for both + * possible jump destinations + */ + mem[from + p->jt] |= invalid; + mem[from + p->jf] |= invalid; + invalid = 0; +#endif break; default: - return 0; + goto out; } break; case BPF_RET: @@ -589,9 +627,13 @@ case BPF_MISC: break; default: - return 0; + goto out; } } - - return BPF_CLASS(f[len - 1].code) == BPF_RET; + ok = 1; +out: +#if defined(KERNEL) || defined(_KERNEL) + free(mem, M_TEMP); +#endif + return ok; }