aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bpf/net/bpf_filter.c165
-rw-r--r--bpf_dump.c6
-rw-r--r--bpf_image.c4
-rw-r--r--optimize.c11
-rw-r--r--pcap-bpf.c60
-rw-r--r--pcap/bpf.h7
-rw-r--r--pcap/pcap.h10
7 files changed, 194 insertions, 69 deletions
diff --git a/bpf/net/bpf_filter.c b/bpf/net/bpf_filter.c
index 0487a6d5..ce100e15 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) {
+ p = &f[i];
+ switch (BPF_CLASS(p->code)) {
/*
- * Check that that jumps are forward, and within
- * the code block.
+ * Check that memory operations use valid addresses.
*/
- 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)
+ 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;
}
- else if (from + p->jt >= len || from + p->jf >= len)
+ break;
+ case BPF_ST:
+ case BPF_STX:
+ if (p->k >= BPF_MEMWORDS)
return 0;
- }
- /*
- * 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)
+ 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 ad096b5d..e4ff4a23 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 <stdio.h>
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 6485825b..2cf581a0 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 af798ef1..b147cf49 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
@@ -2292,6 +2292,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.
*/
pcap_freecode(&p->fcode);
diff --git a/pcap-bpf.c b/pcap-bpf.c
index db451246..65cbdf4e 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
@@ -1055,42 +1055,58 @@ 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.
+ * Free any user-mode filter we might happen to have installed.
+ */
+ pcap_freecode(&p->fcode);
+
+ /*
+ * Try to install the kernel filter.
*/
- if (no_optimize) {
+ if (ioctl(p->fd, BIOCSETF, (caddr_t)fp) == 0) {
/*
- * XXX - what if we already have a filter in the kernel?
+ * It worked.
*/
- if (install_bpf_program(p, fp) < 0)
- return (-1);
- p->md.use_bpf = 0; /* filtering in userland */
+ 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);
}
/*
- * Free any user-mode filter we might happen to have installed.
- */
- pcap_freecode(&p->fcode);
-
- /*
- * Try to install the kernel filter.
+ * 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 (ioctl(p->fd, BIOCSETF, (caddr_t)fp) < 0) {
+ 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 2c032b9d..10b77594 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 70dbddfb..cdbac4a1 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)