diff --git a/bpf/net/bpf_filter.c b/bpf/net/bpf_filter.c index 0487a6d..ce100e1 100644 --- a/bpf/net/bpf_filter.c +++ b/bpf/net/bpf_filter.c @@ -40,7 +40,7 @@ #if !(defined(lint) || defined(KERNEL) || defined(_KERNEL)) static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/bpf/net/bpf_filter.c,v 1.45 2006-10-04 18:09:22 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/bpf/net/bpf_filter.c,v 1.46 2008-01-02 04:16:46 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -200,8 +200,8 @@ m_xhalf(m, k, err) */ u_int bpf_filter(pc, p, wirelen, buflen) - register struct bpf_insn *pc; - register u_char *p; + register const struct bpf_insn *pc; + register const u_char *p; u_int wirelen; register u_int buflen; { @@ -512,54 +512,155 @@ bpf_filter(pc, p, wirelen, buflen) } } - /* * Return true if the 'fcode' is a valid filter program. * The constraints are that each jump be forward and to a valid - * code. The code must terminate with either an accept or reject. - * 'valid' is an array for use by the routine (it must be at least - * 'len' bytes long). + * code, that memory accesses are within valid ranges (to the + * extent that this can be checked statically; loads of packet + * data have to be, and are, also checked at run time), and that + * the code terminates with either an accept or reject. * * The kernel needs to be able to verify an application's filter code. * Otherwise, a bogus program could easily crash the system. */ int bpf_validate(f, len) - struct bpf_insn *f; + const struct bpf_insn *f; int len; { - register int i; - register struct bpf_insn *p; + u_int i, from; + const struct bpf_insn *p; + + if (len < 1) + return 0; + /* + * There's no maximum program length in userland. + */ +#if defined(KERNEL) || defined(_KERNEL) + if (len > BPF_MAXINSNS) + return 0; +#endif for (i = 0; i < len; ++i) { - /* - * Check that that jumps are forward, and within - * the code block. - */ p = &f[i]; - if (BPF_CLASS(p->code) == BPF_JMP) { - register int from = i + 1; - - if (BPF_OP(p->code) == BPF_JA) { - if (from + p->k >= (unsigned)len) - return 0; - } - else if (from + p->jt >= len || from + p->jf >= len) - return 0; - } + switch (BPF_CLASS(p->code)) { /* * Check that memory operations use valid addresses. */ - if ((BPF_CLASS(p->code) == BPF_ST || - (BPF_CLASS(p->code) == BPF_LD && - (p->code & 0xe0) == BPF_MEM)) && - (p->k >= BPF_MEMWORDS || p->k < 0)) - return 0; - /* - * Check for constant division by 0. - */ - if (p->code == (BPF_ALU|BPF_DIV|BPF_K) && p->k == 0) + case BPF_LD: + case BPF_LDX: + switch (BPF_MODE(p->code)) { + case BPF_IMM: + break; + case BPF_ABS: + case BPF_IND: + case BPF_MSH: + /* + * There's no maximum packet data size + * in userland. The runtime packet length + * check suffices. + */ +#if defined(KERNEL) || defined(_KERNEL) + /* + * More strict check with actual packet length + * is done runtime. + */ + if (p->k >= bpf_maxbufsize) + return 0; +#endif + break; + case BPF_MEM: + if (p->k >= BPF_MEMWORDS) + return 0; + break; + case BPF_LEN: + break; + default: + return 0; + } + break; + case BPF_ST: + case BPF_STX: + if (p->k >= BPF_MEMWORDS) + return 0; + break; + case BPF_ALU: + switch (BPF_OP(p->code)) { + case BPF_ADD: + case BPF_SUB: + case BPF_MUL: + case BPF_OR: + case BPF_AND: + case BPF_LSH: + case BPF_RSH: + case BPF_NEG: + break; + case BPF_DIV: + /* + * Check for constant division by 0. + */ + if (BPF_RVAL(p->code) == BPF_K && p->k == 0) + return 0; + break; + default: + return 0; + } + break; + case BPF_JMP: + /* + * Check that jumps are within the code block, + * and that unconditional branches don't go + * backwards as a result of an overflow. + * Unconditional branches have a 32-bit offset, + * so they could overflow; we check to make + * sure they don't. Conditional branches have + * an 8-bit offset, and the from address is <= + * BPF_MAXINSNS, and we assume that BPF_MAXINSNS + * is sufficiently small that adding 255 to it + * won't overflow. + * + * We know that len is <= BPF_MAXINSNS, and we + * assume that BPF_MAXINSNS is < the maximum size + * of a u_int, so that i + 1 doesn't overflow. + * + * For userland, we don't know that the from + * or len are <= BPF_MAXINSNS, but we know that + * from <= len, and, except on a 64-bit system, + * it's unlikely that len, if it truly reflects + * the size of the program we've been handed, + * will be anywhere near the maximum size of + * a u_int. We also don't check for backward + * branches, as we currently support them in + * userland for the protochain operation. + */ + 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) +#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; + break; + default: + return 0; + } + break; + case BPF_RET: + break; + case BPF_MISC: + break; + default: return 0; + } } return BPF_CLASS(f[len - 1].code) == BPF_RET; } diff --git a/bpf_dump.c b/bpf_dump.c index ad096b5..e4ff4a2 100644 --- a/bpf_dump.c +++ b/bpf_dump.c @@ -20,7 +20,7 @@ */ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/bpf_dump.c,v 1.14 2003-11-15 23:23:57 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/bpf_dump.c,v 1.15 2008-01-02 04:16:46 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -31,9 +31,9 @@ static const char rcsid[] _U_ = #include void -bpf_dump(struct bpf_program *p, int option) +bpf_dump(const struct bpf_program *p, int option) { - struct bpf_insn *insn; + const struct bpf_insn *insn; int i; int n = p->bf_len; diff --git a/bpf_image.c b/bpf_image.c index 6485825..2cf581a 100644 --- a/bpf_image.c +++ b/bpf_image.c @@ -21,7 +21,7 @@ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/bpf_image.c,v 1.27 2007-06-11 10:04:24 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/bpf_image.c,v 1.28 2008-01-02 04:16:46 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -39,7 +39,7 @@ static const char rcsid[] _U_ = char * bpf_image(p, n) - struct bpf_insn *p; + const struct bpf_insn *p; int n; { int v; diff --git a/optimize.c b/optimize.c index af798ef..b147cf4 100644 --- a/optimize.c +++ b/optimize.c @@ -22,7 +22,7 @@ */ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/optimize.c,v 1.90 2007-09-27 18:01:51 gianluca Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/optimize.c,v 1.91 2008-01-02 04:16:46 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -2291,6 +2291,15 @@ install_bpf_program(pcap_t *p, struct bpf_program *fp) { size_t prog_size; + /* + * Validate the program. + */ + if (!bpf_validate(fp->bf_insns, fp->bf_len)) { + snprintf(p->errbuf, sizeof(p->errbuf), + "BPF program is not valid"); + return (-1); + } + /* * Free up any already installed program. */ diff --git a/pcap-bpf.c b/pcap-bpf.c index db45124..65cbdf4 100644 --- a/pcap-bpf.c +++ b/pcap-bpf.c @@ -20,7 +20,7 @@ */ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap-bpf.c,v 1.100 2007-12-05 23:37:26 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/pcap-bpf.c,v 1.101 2008-01-02 04:16:46 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -1054,21 +1054,6 @@ pcap_platform_finddevs(pcap_if_t **alldevsp, char *errbuf) static int pcap_setfilter_bpf(pcap_t *p, struct bpf_program *fp) { - /* - * It looks that BPF code generated by gen_protochain() is not - * compatible with some of kernel BPF code (for example BSD/OS 3.1). - * Take a safer side for now. - */ - if (no_optimize) { - /* - * XXX - what if we already have a filter in the kernel? - */ - if (install_bpf_program(p, fp) < 0) - return (-1); - p->md.use_bpf = 0; /* filtering in userland */ - return (0); - } - /* * Free any user-mode filter we might happen to have installed. */ @@ -1077,20 +1062,51 @@ pcap_setfilter_bpf(pcap_t *p, struct bpf_program *fp) /* * Try to install the kernel filter. */ - if (ioctl(p->fd, BIOCSETF, (caddr_t)fp) < 0) { + if (ioctl(p->fd, BIOCSETF, (caddr_t)fp) == 0) { + /* + * It worked. + */ + p->md.use_bpf = 1; /* filtering in the kernel */ + + /* + * Discard any previously-received packets, as they might + * have passed whatever filter was formerly in effect, but + * might not pass this filter (BIOCSETF discards packets + * buffered in the kernel, so you can lose packets in any + * case). + */ + p->cc = 0; + return (0); + } + + /* + * We failed. + * + * If it failed with EINVAL, that's probably because the program + * is invalid or too big. Validate it ourselves; if we like it + * (we currently allow backward branches, to support protochain), + * run it in userland. (There's no notion of "too big" for + * userland.) + * + * Otherwise, just give up. + * XXX - if the copy of the program into the kernel failed, + * we will get EINVAL rather than, say, EFAULT on at least + * some kernels. + */ + if (errno != EINVAL) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCSETF: %s", pcap_strerror(errno)); return (-1); } - p->md.use_bpf = 1; /* filtering in the kernel */ /* - * Discard any previously-received packets, as they might have - * passed whatever filter was formerly in effect, but might - * not pass this filter (BIOCSETF discards packets buffered - * in the kernel, so you can lose packets in any case). + * install_bpf_program() validates the program. + * + * XXX - what if we already have a filter in the kernel? */ - p->cc = 0; + if (install_bpf_program(p, fp) < 0) + return (-1); + p->md.use_bpf = 0; /* filtering in userland */ return (0); } diff --git a/pcap/bpf.h b/pcap/bpf.h index 2c032b9..10b7759 100644 --- a/pcap/bpf.h +++ b/pcap/bpf.h @@ -37,7 +37,7 @@ * * @(#)bpf.h 7.1 (Berkeley) 5/7/91 * - * @(#) $Header: /tcpdump/master/libpcap/pcap/bpf.h,v 1.22 2007-12-23 04:40:45 guy Exp $ (LBL) + * @(#) $Header: /tcpdump/master/libpcap/pcap/bpf.h,v 1.23 2008-01-02 04:16:46 guy Exp $ (LBL) */ /* @@ -79,7 +79,6 @@ typedef u_int bpf_u_int32; #endif #define BPF_WORDALIGN(x) (((x)+(BPF_ALIGNMENT-1))&~(BPF_ALIGNMENT-1)) -#define BPF_MAXINSNS 512 #define BPF_MAXBUFSIZE 0x8000 #define BPF_MINBUFSIZE 32 @@ -873,8 +872,8 @@ struct bpf_insn { #define BPF_JUMP(code, k, jt, jf) { (u_short)(code), jt, jf, k } #if __STDC__ || defined(__cplusplus) -extern int bpf_validate(struct bpf_insn *, int); -extern u_int bpf_filter(struct bpf_insn *, u_char *, u_int, u_int); +extern int bpf_validate(const struct bpf_insn *, int); +extern u_int bpf_filter(const struct bpf_insn *, const u_char *, u_int, u_int); #else extern int bpf_validate(); extern u_int bpf_filter(); diff --git a/pcap/pcap.h b/pcap/pcap.h index 70dbddf..cdbac4a 100644 --- a/pcap/pcap.h +++ b/pcap/pcap.h @@ -31,7 +31,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#) $Header: /tcpdump/master/libpcap/pcap/pcap.h,v 1.4 2007-10-04 23:03:02 guy Exp $ (LBL) + * @(#) $Header: /tcpdump/master/libpcap/pcap/pcap.h,v 1.5 2008-01-02 04:16:46 guy Exp $ (LBL) */ #ifndef lib_pcap_pcap_h @@ -284,10 +284,10 @@ void pcap_freealldevs(pcap_if_t *); const char *pcap_lib_version(void); /* XXX this guy lives in the bpf tree */ -u_int bpf_filter(struct bpf_insn *, u_char *, u_int, u_int); -int bpf_validate(struct bpf_insn *f, int len); -char *bpf_image(struct bpf_insn *, int); -void bpf_dump(struct bpf_program *, int); +u_int bpf_filter(const struct bpf_insn *, const u_char *, u_int, u_int); +int bpf_validate(const struct bpf_insn *f, int len); +char *bpf_image(const struct bpf_insn *, int); +void bpf_dump(const struct bpf_program *, int); #if defined(WIN32)