aboutsummaryrefslogtreecommitdiff
path: root/contrib/libpcap/optimize.c
diff options
context:
space:
mode:
authorJoseph Mingrone <jrm@FreeBSD.org>2023-02-13 22:06:51 +0000
committerJoseph Mingrone <jrm@FreeBSD.org>2025-01-29 19:29:28 +0000
commit7aedea8685359fa5e914a7341ef0ef33b8227d7c (patch)
tree83671936f5bbf82e0d1bb675bb6853d313a31e33 /contrib/libpcap/optimize.c
parent73e80499b948269f83ee95cc2a310cc04ae092d3 (diff)
libpcap: Update to 1.10.3
Prior to this MFC, the stable/13 libpcap was at version 1.9.1, which was released over five years ago. Several CVEs have been identified in that version. For a detailed list, see https://www.tcpdump.org/public-cve-list.txt. Reviewed by: kp (pf changes) Sponsored by: The FreeBSD Foundation (cherry picked from commit 6d1c946e916845d01062e146aad5395410da1e54) (cherry picked from commit 0c59e0b4e5817f139ce28486085b2f196351c7a3) (cherry picked from commit 512c553671bef26e93342d4ae1ec5f60d8ae112b) (cherry picked from commit 6f9cba8f8b5efd16249633e52483ea351876b67b) (cherry picked from commit 437f05885047c533cadf15b564729cfd0273d2fe) Commit 437f05885047c533cadf15b564729cfd0273d2fe has been adapted to only include the changes for the libpcap 1.10.3 update.
Diffstat (limited to 'contrib/libpcap/optimize.c')
-rw-r--r--contrib/libpcap/optimize.c677
1 files changed, 556 insertions, 121 deletions
diff --git a/contrib/libpcap/optimize.c b/contrib/libpcap/optimize.c
index 448452d2831b..0ad0c4163bb0 100644
--- a/contrib/libpcap/optimize.c
+++ b/contrib/libpcap/optimize.c
@@ -32,13 +32,14 @@
#include <memory.h>
#include <setjmp.h>
#include <string.h>
-
+#include <limits.h> /* for SIZE_MAX */
#include <errno.h>
#include "pcap-int.h"
#include "gencode.h"
#include "optimize.h"
+#include "diag-control.h"
#ifdef HAVE_OS_PROTO_H
#include "os-proto.h"
@@ -103,7 +104,7 @@ pcap_set_print_dot_graph(int value)
* Takes a 32-bit integer as an argument.
*
* If handed a non-zero value, returns the index of the lowest set bit,
- * counting upwards fro zero.
+ * counting upwards from zero.
*
* If handed zero, the results are platform- and compiler-dependent.
* Keep it out of the light, don't give it any water, don't feed it
@@ -115,7 +116,7 @@ pcap_set_print_dot_graph(int value)
/*
* GCC 3.4 and later; we have __builtin_ctz().
*/
- #define lowest_set_bit(mask) __builtin_ctz(mask)
+ #define lowest_set_bit(mask) ((u_int)__builtin_ctz(mask))
#elif defined(_MSC_VER)
/*
* Visual Studio; we support only 2005 and later, so use
@@ -127,7 +128,7 @@ pcap_set_print_dot_graph(int value)
#pragma intrinsic(_BitScanForward)
#endif
-static __forceinline int
+static __forceinline u_int
lowest_set_bit(int mask)
{
unsigned long bit;
@@ -137,15 +138,15 @@ lowest_set_bit(int mask)
* (It's currently not, in MSVC, even on 64-bit platforms, but....)
*/
if (_BitScanForward(&bit, (unsigned int)mask) == 0)
- return -1; /* mask is zero */
- return (int)bit;
+ abort(); /* mask is zero */
+ return (u_int)bit;
}
#elif defined(MSDOS) && defined(__DJGPP__)
/*
* MS-DOS with DJGPP, which declares ffs() in <string.h>, which
* we've already included.
*/
- #define lowest_set_bit(mask) (ffs((mask)) - 1)
+ #define lowest_set_bit(mask) ((u_int)(ffs((mask)) - 1))
#elif (defined(MSDOS) && defined(__WATCOMC__)) || defined(STRINGS_H_DECLARES_FFS)
/*
* MS-DOS with Watcom C, which has <strings.h> and declares ffs() there,
@@ -153,18 +154,18 @@ lowest_set_bit(int mask)
* of the Single UNIX Specification).
*/
#include <strings.h>
- #define lowest_set_bit(mask) (ffs((mask)) - 1)
+ #define lowest_set_bit(mask) (u_int)((ffs((mask)) - 1))
#else
/*
* None of the above.
* Use a perfect-hash-function-based function.
*/
-static int
+static u_int
lowest_set_bit(int mask)
{
unsigned int v = (unsigned int)mask;
- static const int MultiplyDeBruijnBitPosition[32] = {
+ static const u_int MultiplyDeBruijnBitPosition[32] = {
0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
};
@@ -213,17 +214,17 @@ lowest_set_bit(int mask)
*/
struct valnode {
int code;
- int v0, v1;
- int val;
+ bpf_u_int32 v0, v1;
+ int val; /* the value number */
struct valnode *next;
};
/* Integer constants mapped with the load immediate opcode. */
-#define K(i) F(opt_state, BPF_LD|BPF_IMM|BPF_W, i, 0L)
+#define K(i) F(opt_state, BPF_LD|BPF_IMM|BPF_W, i, 0U)
struct vmapinfo {
int is_const;
- bpf_int32 const_val;
+ bpf_u_int32 const_val;
};
typedef struct {
@@ -240,21 +241,34 @@ typedef struct {
/*
* A flag to indicate that further optimization is needed.
* Iterative passes are continued until a given pass yields no
- * branch movement.
+ * code simplification or branch movement.
*/
int done;
- int n_blocks;
+ /*
+ * XXX - detect loops that do nothing but repeated AND/OR pullups
+ * and edge moves.
+ * If 100 passes in a row do nothing but that, treat that as a
+ * sign that we're in a loop that just shuffles in a cycle in
+ * which each pass just shuffles the code and we eventually
+ * get back to the original configuration.
+ *
+ * XXX - we need a non-heuristic way of detecting, or preventing,
+ * such a cycle.
+ */
+ int non_branch_movement_performed;
+
+ u_int n_blocks; /* number of blocks in the CFG; guaranteed to be > 0, as it's a RET instruction at a minimum */
struct block **blocks;
- int n_edges;
+ u_int n_edges; /* twice n_blocks, so guaranteed to be > 0 */
struct edge **edges;
/*
* A bit vector set representation of the dominators.
* We round up the set size to the next power of two.
*/
- int nodewords;
- int edgewords;
+ u_int nodewords; /* number of 32-bit words for a bit vector of "number of nodes" bits; guaranteed to be > 0 */
+ u_int edgewords; /* number of 32-bit words for a bit vector of "number of edges" bits; guaranteed to be > 0 */
struct block **levels;
bpf_u_int32 *space;
@@ -279,32 +293,35 @@ typedef struct {
/*
* a := a intersect b
+ * n must be guaranteed to be > 0
*/
#define SET_INTERSECT(a, b, n)\
{\
register bpf_u_int32 *_x = a, *_y = b;\
- register int _n = n;\
- while (--_n >= 0) *_x++ &= *_y++;\
+ register u_int _n = n;\
+ do *_x++ &= *_y++; while (--_n != 0);\
}
/*
* a := a - b
+ * n must be guaranteed to be > 0
*/
#define SET_SUBTRACT(a, b, n)\
{\
register bpf_u_int32 *_x = a, *_y = b;\
- register int _n = n;\
- while (--_n >= 0) *_x++ &=~ *_y++;\
+ register u_int _n = n;\
+ do *_x++ &=~ *_y++; while (--_n != 0);\
}
/*
* a := a union b
+ * n must be guaranteed to be > 0
*/
#define SET_UNION(a, b, n)\
{\
register bpf_u_int32 *_x = a, *_y = b;\
- register int _n = n;\
- while (--_n >= 0) *_x++ |= *_y++;\
+ register u_int _n = n;\
+ do *_x++ |= *_y++; while (--_n != 0);\
}
uset all_dom_sets;
@@ -313,8 +330,8 @@ typedef struct {
#define MODULUS 213
struct valnode *hashtbl[MODULUS];
- int curval;
- int maxval;
+ bpf_u_int32 curval;
+ bpf_u_int32 maxval;
struct vmapinfo *vmap;
struct valnode *vnode_base;
@@ -401,7 +418,8 @@ find_levels(opt_state_t *opt_state, struct icode *ic)
static void
find_dom(opt_state_t *opt_state, struct block *root)
{
- int i;
+ u_int i;
+ int level;
struct block *b;
bpf_u_int32 *x;
@@ -409,16 +427,23 @@ find_dom(opt_state_t *opt_state, struct block *root)
* Initialize sets to contain all nodes.
*/
x = opt_state->all_dom_sets;
+ /*
+ * In opt_init(), we've made sure the product doesn't overflow.
+ */
i = opt_state->n_blocks * opt_state->nodewords;
- while (--i >= 0)
+ while (i != 0) {
+ --i;
*x++ = 0xFFFFFFFFU;
+ }
/* Root starts off empty. */
- for (i = opt_state->nodewords; --i >= 0;)
+ for (i = opt_state->nodewords; i != 0;) {
+ --i;
root->dom[i] = 0;
+ }
/* root->level is the highest level no found. */
- for (i = root->level; i >= 0; --i) {
- for (b = opt_state->levels[i]; b; b = b->link) {
+ for (level = root->level; level >= 0; --level) {
+ for (b = opt_state->levels[level]; b; b = b->link) {
SET_INSERT(b->dom, b->id);
if (JT(b) == 0)
continue;
@@ -445,19 +470,25 @@ propedom(opt_state_t *opt_state, struct edge *ep)
static void
find_edom(opt_state_t *opt_state, struct block *root)
{
- int i;
+ u_int i;
uset x;
+ int level;
struct block *b;
x = opt_state->all_edge_sets;
- for (i = opt_state->n_edges * opt_state->edgewords; --i >= 0; )
+ /*
+ * In opt_init(), we've made sure the product doesn't overflow.
+ */
+ for (i = opt_state->n_edges * opt_state->edgewords; i != 0; ) {
+ --i;
x[i] = 0xFFFFFFFFU;
+ }
/* root->level is the highest level no found. */
memset(root->et.edom, 0, opt_state->edgewords * sizeof(*(uset)0));
memset(root->ef.edom, 0, opt_state->edgewords * sizeof(*(uset)0));
- for (i = root->level; i >= 0; --i) {
- for (b = opt_state->levels[i]; b != 0; b = b->link) {
+ for (level = root->level; level >= 0; --level) {
+ for (b = opt_state->levels[level]; b != 0; b = b->link) {
propedom(opt_state, &b->et);
propedom(opt_state, &b->ef);
}
@@ -474,7 +505,7 @@ find_edom(opt_state_t *opt_state, struct block *root)
static void
find_closure(opt_state_t *opt_state, struct block *root)
{
- int i;
+ int level;
struct block *b;
/*
@@ -484,8 +515,8 @@ find_closure(opt_state_t *opt_state, struct block *root)
opt_state->n_blocks * opt_state->nodewords * sizeof(*opt_state->all_closure_sets));
/* root->level is the highest level no found. */
- for (i = root->level; i >= 0; --i) {
- for (b = opt_state->levels[i]; b; b = b->link) {
+ for (level = root->level; level >= 0; --level) {
+ for (b = opt_state->levels[level]; b; b = b->link) {
SET_INSERT(b->closure, b->id);
if (JT(b) == 0)
continue;
@@ -496,8 +527,11 @@ find_closure(opt_state_t *opt_state, struct block *root)
}
/*
- * Return the register number that is used by s. If A and X are both
- * used, return AX_ATOM. If no register is used, return -1.
+ * Return the register number that is used by s.
+ *
+ * Returns ATOM_A if A is used, ATOM_X if X is used, AX_ATOM if both A and X
+ * are used, the scratch memory location's number if a scratch memory
+ * location is used (e.g., 0 for M[0]), or -1 if none of those are used.
*
* The implementation should probably change to an array access.
*/
@@ -517,8 +551,12 @@ atomuse(struct stmt *s)
case BPF_LD:
case BPF_LDX:
+ /*
+ * As there are fewer than 2^31 memory locations,
+ * s->k should be convertible to int without problems.
+ */
return (BPF_MODE(c) == BPF_IND) ? X_ATOM :
- (BPF_MODE(c) == BPF_MEM) ? s->k : -1;
+ (BPF_MODE(c) == BPF_MEM) ? (int)s->k : -1;
case BPF_ST:
return A_ATOM;
@@ -676,21 +714,40 @@ init_val(opt_state_t *opt_state)
memset((char *)opt_state->hashtbl, 0, sizeof opt_state->hashtbl);
}
-/* Because we really don't have an IR, this stuff is a little messy. */
-static int
-F(opt_state_t *opt_state, int code, int v0, int v1)
+/*
+ * Because we really don't have an IR, this stuff is a little messy.
+ *
+ * This routine looks in the table of existing value number for a value
+ * with generated from an operation with the specified opcode and
+ * the specified values. If it finds it, it returns its value number,
+ * otherwise it makes a new entry in the table and returns the
+ * value number of that entry.
+ */
+static bpf_u_int32
+F(opt_state_t *opt_state, int code, bpf_u_int32 v0, bpf_u_int32 v1)
{
u_int hash;
- int val;
+ bpf_u_int32 val;
struct valnode *p;
- hash = (u_int)code ^ ((u_int)v0 << 4) ^ ((u_int)v1 << 8);
+ hash = (u_int)code ^ (v0 << 4) ^ (v1 << 8);
hash %= MODULUS;
for (p = opt_state->hashtbl[hash]; p; p = p->next)
if (p->code == code && p->v0 == v0 && p->v1 == v1)
return p->val;
+ /*
+ * Not found. Allocate a new value, and assign it a new
+ * value number.
+ *
+ * opt_state->curval starts out as 0, which means VAL_UNKNOWN; we
+ * increment it before using it as the new value number, which
+ * means we never assign VAL_UNKNOWN.
+ *
+ * XXX - unless we overflow, but we probably won't have 2^32-1
+ * values; we treat 32 bits as effectively infinite.
+ */
val = ++opt_state->curval;
if (BPF_MODE(code) == BPF_IMM &&
(BPF_CLASS(code) == BPF_LD || BPF_CLASS(code) == BPF_LDX)) {
@@ -709,7 +766,7 @@ F(opt_state_t *opt_state, int code, int v0, int v1)
}
static inline void
-vstore(struct stmt *s, int *valp, int newval, int alter)
+vstore(struct stmt *s, bpf_u_int32 *valp, bpf_u_int32 newval, int alter)
{
if (alter && newval != VAL_UNKNOWN && *valp == newval)
s->code = NOP;
@@ -722,7 +779,7 @@ vstore(struct stmt *s, int *valp, int newval, int alter)
* (Unary operators are handled elsewhere.)
*/
static void
-fold_op(opt_state_t *opt_state, struct stmt *s, int v0, int v1)
+fold_op(opt_state_t *opt_state, struct stmt *s, bpf_u_int32 v0, bpf_u_int32 v1)
{
bpf_u_int32 a, b;
@@ -807,6 +864,10 @@ fold_op(opt_state_t *opt_state, struct stmt *s, int v0, int v1)
}
s->k = a;
s->code = BPF_LD|BPF_IMM;
+ /*
+ * XXX - optimizer loop detection.
+ */
+ opt_state->non_branch_movement_performed = 1;
opt_state->done = 0;
}
@@ -832,7 +893,7 @@ opt_peep(opt_state_t *opt_state, struct block *b)
{
struct slist *s;
struct slist *next, *last;
- int val;
+ bpf_u_int32 val;
s = b->stmts;
if (s == 0)
@@ -863,6 +924,10 @@ opt_peep(opt_state_t *opt_state, struct block *b)
if (s->s.code == BPF_ST &&
next->s.code == (BPF_LDX|BPF_MEM) &&
s->s.k == next->s.k) {
+ /*
+ * XXX - optimizer loop detection.
+ */
+ opt_state->non_branch_movement_performed = 1;
opt_state->done = 0;
next->s.code = BPF_MISC|BPF_TAX;
}
@@ -874,6 +939,10 @@ opt_peep(opt_state_t *opt_state, struct block *b)
next->s.code == (BPF_MISC|BPF_TAX)) {
s->s.code = BPF_LDX|BPF_IMM;
next->s.code = BPF_MISC|BPF_TXA;
+ /*
+ * XXX - optimizer loop detection.
+ */
+ opt_state->non_branch_movement_performed = 1;
opt_state->done = 0;
}
/*
@@ -953,6 +1022,10 @@ opt_peep(opt_state_t *opt_state, struct block *b)
s->s.code = NOP;
add->s.code = NOP;
tax->s.code = NOP;
+ /*
+ * XXX - optimizer loop detection.
+ */
+ opt_state->non_branch_movement_performed = 1;
opt_state->done = 0;
}
}
@@ -965,10 +1038,10 @@ opt_peep(opt_state_t *opt_state, struct block *b)
*/
if (b->s.code == (BPF_JMP|BPF_JEQ|BPF_K) &&
!ATOMELEM(b->out_use, A_ATOM)) {
- /*
- * We can optimize away certain subtractions of the
- * X register.
- */
+ /*
+ * We can optimize away certain subtractions of the
+ * X register.
+ */
if (last->s.code == (BPF_ALU|BPF_SUB|BPF_X)) {
val = b->val[X_ATOM];
if (opt_state->vmap[val].is_const) {
@@ -983,6 +1056,10 @@ opt_peep(opt_state_t *opt_state, struct block *b)
*/
b->s.k += opt_state->vmap[val].const_val;
last->s.code = NOP;
+ /*
+ * XXX - optimizer loop detection.
+ */
+ opt_state->non_branch_movement_performed = 1;
opt_state->done = 0;
} else if (b->s.k == 0) {
/*
@@ -996,6 +1073,10 @@ opt_peep(opt_state_t *opt_state, struct block *b)
*/
last->s.code = NOP;
b->s.code = BPF_JMP|BPF_JEQ|BPF_X;
+ /*
+ * XXX - optimizer loop detection.
+ */
+ opt_state->non_branch_movement_performed = 1;
opt_state->done = 0;
}
}
@@ -1008,6 +1089,10 @@ opt_peep(opt_state_t *opt_state, struct block *b)
else if (last->s.code == (BPF_ALU|BPF_SUB|BPF_K)) {
last->s.code = NOP;
b->s.k += last->s.k;
+ /*
+ * XXX - optimizer loop detection.
+ */
+ opt_state->non_branch_movement_performed = 1;
opt_state->done = 0;
}
/*
@@ -1022,6 +1107,10 @@ opt_peep(opt_state_t *opt_state, struct block *b)
b->s.k = last->s.k;
b->s.code = BPF_JMP|BPF_K|BPF_JSET;
last->s.code = NOP;
+ /*
+ * XXX - optimizer loop detection.
+ */
+ opt_state->non_branch_movement_performed = 1;
opt_state->done = 0;
opt_not(b);
}
@@ -1033,7 +1122,7 @@ opt_peep(opt_state_t *opt_state, struct block *b)
if (b->s.code == (BPF_JMP|BPF_K|BPF_JSET)) {
if (b->s.k == 0)
JT(b) = JF(b);
- if ((u_int)b->s.k == 0xffffffffU)
+ if (b->s.k == 0xffffffffU)
JF(b) = JT(b);
}
/*
@@ -1043,7 +1132,7 @@ opt_peep(opt_state_t *opt_state, struct block *b)
*/
val = b->val[X_ATOM];
if (opt_state->vmap[val].is_const && BPF_SRC(b->s.code) == BPF_X) {
- bpf_int32 v = opt_state->vmap[val].const_val;
+ bpf_u_int32 v = opt_state->vmap[val].const_val;
b->s.code &= ~BPF_X;
b->s.k = v;
}
@@ -1053,7 +1142,7 @@ opt_peep(opt_state_t *opt_state, struct block *b)
*/
val = b->val[A_ATOM];
if (opt_state->vmap[val].is_const && BPF_SRC(b->s.code) == BPF_K) {
- bpf_int32 v = opt_state->vmap[val].const_val;
+ bpf_u_int32 v = opt_state->vmap[val].const_val;
switch (BPF_OP(b->s.code)) {
case BPF_JEQ:
@@ -1061,11 +1150,11 @@ opt_peep(opt_state_t *opt_state, struct block *b)
break;
case BPF_JGT:
- v = (unsigned)v > (unsigned)b->s.k;
+ v = v > b->s.k;
break;
case BPF_JGE:
- v = (unsigned)v >= (unsigned)b->s.k;
+ v = v >= b->s.k;
break;
case BPF_JSET:
@@ -1075,8 +1164,13 @@ opt_peep(opt_state_t *opt_state, struct block *b)
default:
abort();
}
- if (JF(b) != JT(b))
+ if (JF(b) != JT(b)) {
+ /*
+ * XXX - optimizer loop detection.
+ */
+ opt_state->non_branch_movement_performed = 1;
opt_state->done = 0;
+ }
if (v)
JF(b) = JT(b);
else
@@ -1091,10 +1185,10 @@ opt_peep(opt_state_t *opt_state, struct block *b)
* evaluation and code transformations weren't folded together.
*/
static void
-opt_stmt(opt_state_t *opt_state, struct stmt *s, int val[], int alter)
+opt_stmt(opt_state_t *opt_state, struct stmt *s, bpf_u_int32 val[], int alter)
{
int op;
- int v;
+ bpf_u_int32 v;
switch (s->code) {
@@ -1113,6 +1207,10 @@ opt_stmt(opt_state_t *opt_state, struct stmt *s, int val[], int alter)
s->code = BPF_LD|BPF_ABS|BPF_SIZE(s->code);
s->k += opt_state->vmap[v].const_val;
v = F(opt_state, s->code, s->k, 0L);
+ /*
+ * XXX - optimizer loop detection.
+ */
+ opt_state->non_branch_movement_performed = 1;
opt_state->done = 0;
}
else
@@ -1159,7 +1257,7 @@ opt_stmt(opt_state_t *opt_state, struct stmt *s, int val[], int alter)
* about the result of negating 0x80000000 being
* undefined.
*/
- s->k = 0U - (bpf_u_int32)(opt_state->vmap[val[A_ATOM]].const_val);
+ s->k = 0U - opt_state->vmap[val[A_ATOM]].const_val;
val[A_ATOM] = K(s->k);
}
else
@@ -1236,16 +1334,14 @@ opt_stmt(opt_state_t *opt_state, struct stmt *s, int val[], int alter)
else {
s->code = BPF_ALU|BPF_K|op;
s->k = opt_state->vmap[val[X_ATOM]].const_val;
- /*
- * XXX - we need to make up our minds
- * as to what integers are signed and
- * what integers are unsigned in BPF
- * programs and in our IR.
- */
if ((op == BPF_LSH || op == BPF_RSH) &&
- (s->k < 0 || s->k > 31))
+ s->k > 31)
opt_error(opt_state,
"shift by more than 31 bits");
+ /*
+ * XXX - optimizer loop detection.
+ */
+ opt_state->non_branch_movement_performed = 1;
opt_state->done = 0;
val[A_ATOM] =
F(opt_state, s->code, val[A_ATOM], K(s->k));
@@ -1290,6 +1386,10 @@ opt_stmt(opt_state_t *opt_state, struct stmt *s, int val[], int alter)
if (alter && opt_state->vmap[v].is_const) {
s->code = BPF_LD|BPF_IMM;
s->k = opt_state->vmap[v].const_val;
+ /*
+ * XXX - optimizer loop detection.
+ */
+ opt_state->non_branch_movement_performed = 1;
opt_state->done = 0;
}
vstore(s, &val[A_ATOM], v, alter);
@@ -1304,6 +1404,10 @@ opt_stmt(opt_state_t *opt_state, struct stmt *s, int val[], int alter)
if (alter && opt_state->vmap[v].is_const) {
s->code = BPF_LDX|BPF_IMM;
s->k = opt_state->vmap[v].const_val;
+ /*
+ * XXX - optimizer loop detection.
+ */
+ opt_state->non_branch_movement_performed = 1;
opt_state->done = 0;
}
vstore(s, &val[X_ATOM], v, alter);
@@ -1336,6 +1440,10 @@ deadstmt(opt_state_t *opt_state, register struct stmt *s, register struct stmt *
atom = atomdef(s);
if (atom >= 0) {
if (last[atom]) {
+ /*
+ * XXX - optimizer loop detection.
+ */
+ opt_state->non_branch_movement_performed = 1;
opt_state->done = 0;
last[atom]->code = NOP;
}
@@ -1359,6 +1467,10 @@ opt_deadstores(opt_state_t *opt_state, register struct block *b)
for (atom = 0; atom < N_ATOMS; ++atom)
if (last[atom] && !ATOMELEM(b->out_use, atom)) {
last[atom]->code = NOP;
+ /*
+ * XXX - optimizer loop detection.
+ */
+ opt_state->non_branch_movement_performed = 1;
opt_state->done = 0;
}
}
@@ -1369,7 +1481,7 @@ opt_blk(opt_state_t *opt_state, struct block *b, int do_stmts)
struct slist *s;
struct edge *p;
int i;
- bpf_int32 aval, xval;
+ bpf_u_int32 aval, xval;
#if 0
for (s = b->stmts; s && s->next; s = s->next)
@@ -1422,7 +1534,10 @@ opt_blk(opt_state_t *opt_state, struct block *b, int do_stmts)
* value that is already there, or if this block is a return,
* eliminate all the statements.
*
- * XXX - what if it does a store?
+ * XXX - what if it does a store? Presumably that falls under
+ * the heading of "if we don't use anything from this block",
+ * i.e., if we use any memory location set to a different
+ * value by this block, then we use something from this block.
*
* XXX - why does it matter whether we use anything from this
* block? If the accumulator or index register doesn't change
@@ -1446,6 +1561,10 @@ opt_blk(opt_state_t *opt_state, struct block *b, int do_stmts)
BPF_CLASS(b->s.code) == BPF_RET)) {
if (b->stmts != 0) {
b->stmts = 0;
+ /*
+ * XXX - optimizer loop detection.
+ */
+ opt_state->non_branch_movement_performed = 1;
opt_state->done = 0;
}
} else {
@@ -1484,19 +1603,41 @@ use_conflict(struct block *b, struct block *succ)
return 0;
}
+/*
+ * Given a block that is the successor of an edge, and an edge that
+ * dominates that edge, return either a pointer to a child of that
+ * block (a block to which that block jumps) if that block is a
+ * candidate to replace the successor of the latter edge or NULL
+ * if neither of the children of the first block are candidates.
+ */
static struct block *
fold_edge(struct block *child, struct edge *ep)
{
int sense;
- int aval0, aval1, oval0, oval1;
+ bpf_u_int32 aval0, aval1, oval0, oval1;
int code = ep->code;
if (code < 0) {
+ /*
+ * This edge is a "branch if false" edge.
+ */
code = -code;
sense = 0;
- } else
+ } else {
+ /*
+ * This edge is a "branch if true" edge.
+ */
sense = 1;
+ }
+ /*
+ * If the opcode for the branch at the end of the block we
+ * were handed isn't the same as the opcode for the branch
+ * to which the edge we were handed corresponds, the tests
+ * for those branches aren't testing the same conditions,
+ * so the blocks to which the first block branches aren't
+ * candidates to replace the successor of the edge.
+ */
if (child->s.code != code)
return 0;
@@ -1505,13 +1646,21 @@ fold_edge(struct block *child, struct edge *ep)
aval1 = ep->pred->val[A_ATOM];
oval1 = ep->pred->oval;
+ /*
+ * If the A register value on exit from the successor block
+ * isn't the same as the A register value on exit from the
+ * predecessor of the edge, the blocks to which the first
+ * block branches aren't candidates to replace the successor
+ * of the edge.
+ */
if (aval0 != aval1)
return 0;
if (oval0 == oval1)
/*
* The operands of the branch instructions are
- * identical, so the result is true if a true
+ * identical, so the branches are testing the
+ * same condition, and the result is true if a true
* branch was taken to get here, otherwise false.
*/
return sense ? JT(child) : JF(child);
@@ -1536,21 +1685,58 @@ fold_edge(struct block *child, struct edge *ep)
return 0;
}
+/*
+ * If we can make this edge go directly to a child of the edge's current
+ * successor, do so.
+ */
static void
opt_j(opt_state_t *opt_state, struct edge *ep)
{
- register int i, k;
+ register u_int i, k;
register struct block *target;
+ /*
+ * Does this edge go to a block where, if the test
+ * at the end of it succeeds, it goes to a block
+ * that's a leaf node of the DAG, i.e. a return
+ * statement?
+ * If so, there's nothing to optimize.
+ */
if (JT(ep->succ) == 0)
return;
+ /*
+ * Does this edge go to a block that goes, in turn, to
+ * the same block regardless of whether the test at the
+ * end succeeds or fails?
+ */
if (JT(ep->succ) == JF(ep->succ)) {
/*
* Common branch targets can be eliminated, provided
* there is no data dependency.
+ *
+ * Check whether any register used on exit from the
+ * block to which the successor of this edge goes
+ * has a value at that point that's different from
+ * the value it has on exit from the predecessor of
+ * this edge. If not, the predecessor of this edge
+ * can just go to the block to which the successor
+ * of this edge goes, bypassing the successor of this
+ * edge, as the successor of this edge isn't doing
+ * any calculations whose results are different
+ * from what the blocks before it did and isn't
+ * doing any tests the results of which matter.
*/
- if (!use_conflict(ep->pred, ep->succ->et.succ)) {
+ if (!use_conflict(ep->pred, JT(ep->succ))) {
+ /*
+ * No, there isn't.
+ * Make this edge go to the block to
+ * which the successor of that edge
+ * goes.
+ *
+ * XXX - optimizer loop detection.
+ */
+ opt_state->non_branch_movement_performed = 1;
opt_state->done = 0;
ep->succ = JT(ep->succ);
}
@@ -1564,19 +1750,38 @@ opt_j(opt_state_t *opt_state, struct edge *ep)
*/
top:
for (i = 0; i < opt_state->edgewords; ++i) {
+ /* i'th word in the bitset of dominators */
register bpf_u_int32 x = ep->edom[i];
while (x != 0) {
+ /* Find the next dominator in that word and mark it as found */
k = lowest_set_bit(x);
x &=~ ((bpf_u_int32)1 << k);
k += i * BITS_PER_WORD;
target = fold_edge(ep->succ, opt_state->edges[k]);
/*
+ * We have a candidate to replace the successor
+ * of ep.
+ *
* Check that there is no data dependency between
- * nodes that will be violated if we move the edge.
+ * nodes that will be violated if we move the edge;
+ * i.e., if any register used on exit from the
+ * candidate has a value at that point different
+ * from the value it has when we exit the
+ * predecessor of that edge, there's a data
+ * dependency that will be violated.
*/
if (target != 0 && !use_conflict(ep->pred, target)) {
+ /*
+ * It's safe to replace the successor of
+ * ep; do so, and note that we've made
+ * at least one change.
+ *
+ * XXX - this is one of the operations that
+ * happens when the optimizer gets into
+ * one of those infinite loops.
+ */
opt_state->done = 0;
ep->succ = target;
if (JT(target) != 0)
@@ -1590,11 +1795,30 @@ opt_j(opt_state_t *opt_state, struct edge *ep)
}
}
-
+/*
+ * XXX - is this, and and_pullup(), what's described in section 6.1.2
+ * "Predicate Assertion Propagation" in the BPF+ paper?
+ *
+ * Note that this looks at block dominators, not edge dominators.
+ * Don't think so.
+ *
+ * "A or B" compiles into
+ *
+ * A
+ * t / \ f
+ * / B
+ * / t / \ f
+ * \ /
+ * \ /
+ * X
+ *
+ *
+ */
static void
or_pullup(opt_state_t *opt_state, struct block *b)
{
- int val, at_top;
+ bpf_u_int32 val;
+ int at_top;
struct block *pull;
struct block **diffp, **samep;
struct edge *ep;
@@ -1612,39 +1836,106 @@ or_pullup(opt_state_t *opt_state, struct block *b)
if (val != ep->pred->val[A_ATOM])
return;
+ /*
+ * For the first edge in the list of edges coming into this block,
+ * see whether the predecessor of that edge comes here via a true
+ * branch or a false branch.
+ */
if (JT(b->in_edges->pred) == b)
- diffp = &JT(b->in_edges->pred);
+ diffp = &JT(b->in_edges->pred); /* jt */
else
- diffp = &JF(b->in_edges->pred);
+ diffp = &JF(b->in_edges->pred); /* jf */
+ /*
+ * diffp is a pointer to a pointer to the block.
+ *
+ * Go down the false chain looking as far as you can,
+ * making sure that each jump-compare is doing the
+ * same as the original block.
+ *
+ * If you reach the bottom before you reach a
+ * different jump-compare, just exit. There's nothing
+ * to do here. XXX - no, this version is checking for
+ * the value leaving the block; that's from the BPF+
+ * pullup routine.
+ */
at_top = 1;
for (;;) {
+ /*
+ * Done if that's not going anywhere XXX
+ */
if (*diffp == 0)
return;
+ /*
+ * Done if that predecessor blah blah blah isn't
+ * going the same place we're going XXX
+ *
+ * Does the true edge of this block point to the same
+ * location as the true edge of b?
+ */
if (JT(*diffp) != JT(b))
return;
+ /*
+ * Done if this node isn't a dominator of that
+ * node blah blah blah XXX
+ *
+ * Does b dominate diffp?
+ */
if (!SET_MEMBER((*diffp)->dom, b->id))
return;
+ /*
+ * Break out of the loop if that node's value of A
+ * isn't the value of A above XXX
+ */
if ((*diffp)->val[A_ATOM] != val)
break;
+ /*
+ * Get the JF for that node XXX
+ * Go down the false path.
+ */
diffp = &JF(*diffp);
at_top = 0;
}
+
+ /*
+ * Now that we've found a different jump-compare in a chain
+ * below b, search further down until we find another
+ * jump-compare that looks at the original value. This
+ * jump-compare should get pulled up. XXX again we're
+ * comparing values not jump-compares.
+ */
samep = &JF(*diffp);
for (;;) {
+ /*
+ * Done if that's not going anywhere XXX
+ */
if (*samep == 0)
return;
+ /*
+ * Done if that predecessor blah blah blah isn't
+ * going the same place we're going XXX
+ */
if (JT(*samep) != JT(b))
return;
+ /*
+ * Done if this node isn't a dominator of that
+ * node blah blah blah XXX
+ *
+ * Does b dominate samep?
+ */
if (!SET_MEMBER((*samep)->dom, b->id))
return;
+ /*
+ * Break out of the loop if that node's value of A
+ * is the value of A above XXX
+ */
if ((*samep)->val[A_ATOM] == val)
break;
@@ -1680,13 +1971,18 @@ or_pullup(opt_state_t *opt_state, struct block *b)
else
*diffp = pull;
+ /*
+ * XXX - this is one of the operations that happens when the
+ * optimizer gets into one of those infinite loops.
+ */
opt_state->done = 0;
}
static void
and_pullup(opt_state_t *opt_state, struct block *b)
{
- int val, at_top;
+ bpf_u_int32 val;
+ int at_top;
struct block *pull;
struct block **diffp, **samep;
struct edge *ep;
@@ -1771,6 +2067,10 @@ and_pullup(opt_state_t *opt_state, struct block *b)
else
*diffp = pull;
+ /*
+ * XXX - this is one of the operations that happens when the
+ * optimizer gets into one of those infinite loops.
+ */
opt_state->done = 0;
}
@@ -1792,9 +2092,22 @@ opt_blks(opt_state_t *opt_state, struct icode *ic, int do_stmts)
/*
* No point trying to move branches; it can't possibly
* make a difference at this point.
+ *
+ * XXX - this might be after we detect a loop where
+ * we were just looping infinitely moving branches
+ * in such a fashion that we went through two or more
+ * versions of the machine code, eventually returning
+ * to the first version. (We're really not doing a
+ * full loop detection, we're just testing for two
+ * passes in a row where we do nothing but
+ * move branches.)
*/
return;
+ /*
+ * Is this what the BPF+ paper describes in sections 6.1.1,
+ * 6.1.2, and 6.1.3?
+ */
for (i = 1; i <= maxlevel; ++i) {
for (p = opt_state->levels[i]; p; p = p->link) {
opt_j(opt_state, &p->et);
@@ -1821,7 +2134,8 @@ link_inedge(struct edge *parent, struct block *child)
static void
find_inedges(opt_state_t *opt_state, struct block *root)
{
- int i;
+ u_int i;
+ int level;
struct block *b;
for (i = 0; i < opt_state->n_blocks; ++i)
@@ -1831,8 +2145,8 @@ find_inedges(opt_state_t *opt_state, struct block *root)
* Traverse the graph, adding each edge to the predecessor
* list of its successors. Skip the leaves (i.e. level 0).
*/
- for (i = root->level; i > 0; --i) {
- for (b = opt_state->levels[i]; b != 0; b = b->link) {
+ for (level = root->level; level > 0; --level) {
+ for (b = opt_state->levels[level]; b != 0; b = b->link) {
link_inedge(&b->et, JT(b));
link_inedge(&b->ef, JF(b));
}
@@ -1873,8 +2187,17 @@ opt_loop(opt_state_t *opt_state, struct icode *ic, int do_stmts)
opt_dump(opt_state, ic);
}
#endif
- do {
+
+ /*
+ * XXX - optimizer loop detection.
+ */
+ int loop_count = 0;
+ for (;;) {
opt_state->done = 1;
+ /*
+ * XXX - optimizer loop detection.
+ */
+ opt_state->non_branch_movement_performed = 0;
find_levels(opt_state, ic);
find_dom(opt_state, ic->root);
find_closure(opt_state, ic->root);
@@ -1887,7 +2210,51 @@ opt_loop(opt_state_t *opt_state, struct icode *ic, int do_stmts)
opt_dump(opt_state, ic);
}
#endif
- } while (!opt_state->done);
+
+ /*
+ * Was anything done in this optimizer pass?
+ */
+ if (opt_state->done) {
+ /*
+ * No, so we've reached a fixed point.
+ * We're done.
+ */
+ break;
+ }
+
+ /*
+ * XXX - was anything done other than branch movement
+ * in this pass?
+ */
+ if (opt_state->non_branch_movement_performed) {
+ /*
+ * Yes. Clear any loop-detection counter;
+ * we're making some form of progress (assuming
+ * we can't get into a cycle doing *other*
+ * optimizations...).
+ */
+ loop_count = 0;
+ } else {
+ /*
+ * No - increment the counter, and quit if
+ * it's up to 100.
+ */
+ loop_count++;
+ if (loop_count >= 100) {
+ /*
+ * We've done nothing but branch movement
+ * for 100 passes; we're probably
+ * in a cycle and will never reach a
+ * fixed point.
+ *
+ * XXX - yes, we really need a non-
+ * heuristic way of detecting a cycle.
+ */
+ opt_state->done = 1;
+ break;
+ }
+ }
+ }
}
/*
@@ -1901,6 +2268,7 @@ bpf_optimize(struct icode *ic, char *errbuf)
memset(&opt_state, 0, sizeof(opt_state));
opt_state.errbuf = errbuf;
+ opt_state.non_branch_movement_performed = 0;
if (setjmp(opt_state.top_ctx)) {
opt_cleanup(&opt_state);
return -1;
@@ -1987,7 +2355,7 @@ static void
intern_blocks(opt_state_t *opt_state, struct icode *ic)
{
struct block *p;
- int i, j;
+ u_int i, j;
int done1; /* don't shadow global */
top:
done1 = 1;
@@ -1996,7 +2364,8 @@ intern_blocks(opt_state_t *opt_state, struct icode *ic)
mark_code(ic);
- for (i = opt_state->n_blocks - 1; --i >= 0; ) {
+ for (i = opt_state->n_blocks - 1; i != 0; ) {
+ --i;
if (!isMarked(ic, opt_state->blocks[i]))
continue;
for (j = i + 1; j < opt_state->n_blocks; ++j) {
@@ -2047,12 +2416,15 @@ opt_error(opt_state_t *opt_state, const char *fmt, ...)
if (opt_state->errbuf != NULL) {
va_start(ap, fmt);
- (void)pcap_vsnprintf(opt_state->errbuf,
+ (void)vsnprintf(opt_state->errbuf,
PCAP_ERRBUF_SIZE, fmt, ap);
va_end(ap);
}
longjmp(opt_state->top_ctx, 1);
/* NOTREACHED */
+#ifdef _AIX
+ PCAP_UNREACHABLE
+#endif /* _AIX */
}
/*
@@ -2089,13 +2461,19 @@ count_blocks(struct icode *ic, struct block *p)
static void
number_blks_r(opt_state_t *opt_state, struct icode *ic, struct block *p)
{
- int n;
+ u_int n;
if (p == 0 || isMarked(ic, p))
return;
Mark(ic, p);
n = opt_state->n_blocks++;
+ if (opt_state->n_blocks == 0) {
+ /*
+ * Overflow.
+ */
+ opt_error(opt_state, "filter is too complex to optimize");
+ }
p->id = n;
opt_state->blocks[n] = p;
@@ -2143,6 +2521,8 @@ opt_init(opt_state_t *opt_state, struct icode *ic)
{
bpf_u_int32 *p;
int i, n, max_stmts;
+ u_int product;
+ size_t block_memsize, edge_memsize;
/*
* First, count the blocks, so we can malloc an array to map
@@ -2157,7 +2537,19 @@ opt_init(opt_state_t *opt_state, struct icode *ic)
opt_state->n_blocks = 0;
number_blks_r(opt_state, ic, ic->root);
+ /*
+ * This "should not happen".
+ */
+ if (opt_state->n_blocks == 0)
+ opt_error(opt_state, "filter has no instructions; please report this as a libpcap issue");
+
opt_state->n_edges = 2 * opt_state->n_blocks;
+ if ((opt_state->n_edges / 2) != opt_state->n_blocks) {
+ /*
+ * Overflow.
+ */
+ opt_error(opt_state, "filter is too complex to optimize");
+ }
opt_state->edges = (struct edge **)calloc(opt_state->n_edges, sizeof(*opt_state->edges));
if (opt_state->edges == NULL) {
opt_error(opt_state, "malloc");
@@ -2171,12 +2563,62 @@ opt_init(opt_state_t *opt_state, struct icode *ic)
opt_error(opt_state, "malloc");
}
- opt_state->edgewords = opt_state->n_edges / (8 * sizeof(bpf_u_int32)) + 1;
- opt_state->nodewords = opt_state->n_blocks / (8 * sizeof(bpf_u_int32)) + 1;
+ opt_state->edgewords = opt_state->n_edges / BITS_PER_WORD + 1;
+ opt_state->nodewords = opt_state->n_blocks / BITS_PER_WORD + 1;
+
+ /*
+ * Make sure opt_state->n_blocks * opt_state->nodewords fits
+ * in a u_int; we use it as a u_int number-of-iterations
+ * value.
+ */
+ product = opt_state->n_blocks * opt_state->nodewords;
+ if ((product / opt_state->n_blocks) != opt_state->nodewords) {
+ /*
+ * XXX - just punt and don't try to optimize?
+ * In practice, this is unlikely to happen with
+ * a normal filter.
+ */
+ opt_error(opt_state, "filter is too complex to optimize");
+ }
+
+ /*
+ * Make sure the total memory required for that doesn't
+ * overflow.
+ */
+ block_memsize = (size_t)2 * product * sizeof(*opt_state->space);
+ if ((block_memsize / product) != 2 * sizeof(*opt_state->space)) {
+ opt_error(opt_state, "filter is too complex to optimize");
+ }
+
+ /*
+ * Make sure opt_state->n_edges * opt_state->edgewords fits
+ * in a u_int; we use it as a u_int number-of-iterations
+ * value.
+ */
+ product = opt_state->n_edges * opt_state->edgewords;
+ if ((product / opt_state->n_edges) != opt_state->edgewords) {
+ opt_error(opt_state, "filter is too complex to optimize");
+ }
+
+ /*
+ * Make sure the total memory required for that doesn't
+ * overflow.
+ */
+ edge_memsize = (size_t)product * sizeof(*opt_state->space);
+ if (edge_memsize / product != sizeof(*opt_state->space)) {
+ opt_error(opt_state, "filter is too complex to optimize");
+ }
+
+ /*
+ * Make sure the total memory required for both of them doesn't
+ * overflow.
+ */
+ if (block_memsize > SIZE_MAX - edge_memsize) {
+ opt_error(opt_state, "filter is too complex to optimize");
+ }
/* XXX */
- opt_state->space = (bpf_u_int32 *)malloc(2 * opt_state->n_blocks * opt_state->nodewords * sizeof(*opt_state->space)
- + opt_state->n_edges * opt_state->edgewords * sizeof(*opt_state->space));
+ opt_state->space = (bpf_u_int32 *)malloc(block_memsize + edge_memsize);
if (opt_state->space == NULL) {
opt_error(opt_state, "malloc");
}
@@ -2250,7 +2692,6 @@ convert_code_r(conv_state_t *conv_state, struct icode *ic, struct block *p)
struct slist *src;
u_int slen;
u_int off;
- u_int extrajmps; /* number of extra jumps inserted */
struct slist **offset = NULL;
if (p == 0 || isMarked(ic, p))
@@ -2374,21 +2815,17 @@ filled:
dst->code = (u_short)p->s.code;
dst->k = p->s.k;
if (JT(p)) {
- extrajmps = 0;
+ /* number of extra jumps inserted */
+ u_char extrajmps = 0;
off = JT(p)->offset - (p->offset + slen) - 1;
if (off >= 256) {
/* offset too large for branch, must add a jump */
if (p->longjt == 0) {
- /* mark this instruction and retry */
+ /* mark this instruction and retry */
p->longjt++;
return(0);
}
- /* branch if T to following jump */
- if (extrajmps >= 256) {
- conv_error(conv_state, "too many extra jumps");
- /*NOTREACHED*/
- }
- dst->jt = (u_char)extrajmps;
+ dst->jt = extrajmps;
extrajmps++;
dst[extrajmps].code = BPF_JMP|BPF_JA;
dst[extrajmps].k = off - extrajmps;
@@ -2399,17 +2836,13 @@ filled:
if (off >= 256) {
/* offset too large for branch, must add a jump */
if (p->longjf == 0) {
- /* mark this instruction and retry */
+ /* mark this instruction and retry */
p->longjf++;
return(0);
}
/* branch if F to following jump */
/* if two jumps are inserted, F goes to second one */
- if (extrajmps >= 256) {
- conv_error(conv_state, "too many extra jumps");
- /*NOTREACHED*/
- }
- dst->jf = (u_char)extrajmps;
+ dst->jf = extrajmps;
extrajmps++;
dst[extrajmps].code = BPF_JMP|BPF_JA;
dst[extrajmps].k = off - extrajmps;
@@ -2440,7 +2873,7 @@ filled:
* done with the filter program. See the pcap man page.
*/
struct bpf_insn *
-icode_to_fcode(struct icode *ic, struct block *root, u_int *lenp,
+icode_to_fcode(struct icode *ic, struct block *root, u_int *lenp,
char *errbuf)
{
u_int n;
@@ -2464,9 +2897,8 @@ icode_to_fcode(struct icode *ic, struct block *root, u_int *lenp,
fp = (struct bpf_insn *)malloc(sizeof(*fp) * n);
if (fp == NULL) {
- (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
+ (void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
"malloc");
- free(fp);
return NULL;
}
memset((char *)fp, 0, sizeof(*fp) * n);
@@ -2491,11 +2923,14 @@ conv_error(conv_state_t *conv_state, const char *fmt, ...)
va_list ap;
va_start(ap, fmt);
- (void)pcap_vsnprintf(conv_state->errbuf,
+ (void)vsnprintf(conv_state->errbuf,
PCAP_ERRBUF_SIZE, fmt, ap);
va_end(ap);
longjmp(conv_state->top_ctx, 1);
/* NOTREACHED */
+#ifdef _AIX
+ PCAP_UNREACHABLE
+#endif /* _AIX */
}
/*
@@ -2514,8 +2949,8 @@ install_bpf_program(pcap_t *p, struct bpf_program *fp)
/*
* Validate the program.
*/
- if (!bpf_validate(fp->bf_insns, fp->bf_len)) {
- pcap_snprintf(p->errbuf, sizeof(p->errbuf),
+ if (!pcap_validate_filter(fp->bf_insns, fp->bf_len)) {
+ snprintf(p->errbuf, sizeof(p->errbuf),
"BPF program is not valid");
return (-1);
}
@@ -2552,7 +2987,7 @@ dot_dump_node(struct icode *ic, struct block *block, struct bpf_program *prog,
icount = slength(block->stmts) + 1 + block->longjt + block->longjf;
noffset = min(block->offset + icount, (int)prog->bf_len);
- fprintf(out, "\tblock%d [shape=ellipse, id=\"block-%d\" label=\"BLOCK%d\\n", block->id, block->id, block->id);
+ fprintf(out, "\tblock%u [shape=ellipse, id=\"block-%u\" label=\"BLOCK%u\\n", block->id, block->id, block->id);
for (i = block->offset; i < noffset; i++) {
fprintf(out, "\\n%s", bpf_image(prog->bf_insns + i, i));
}
@@ -2579,9 +3014,9 @@ dot_dump_edge(struct icode *ic, struct block *block, FILE *out)
Mark(ic, block);
if (JT(block)) {
- fprintf(out, "\t\"block%d\":se -> \"block%d\":n [label=\"T\"]; \n",
+ fprintf(out, "\t\"block%u\":se -> \"block%u\":n [label=\"T\"]; \n",
block->id, JT(block)->id);
- fprintf(out, "\t\"block%d\":sw -> \"block%d\":n [label=\"F\"]; \n",
+ fprintf(out, "\t\"block%u\":sw -> \"block%u\":n [label=\"F\"]; \n",
block->id, JF(block)->id);
}
dot_dump_edge(ic, JT(block), out);
@@ -2604,7 +3039,7 @@ dot_dump_edge(struct icode *ic, struct block *block, FILE *out)
"block1":sw -> "block3":n [label="F"];
}
*
- * After install graphviz on http://www.graphviz.org/, save it as bpf.dot
+ * After install graphviz on https://www.graphviz.org/, save it as bpf.dot
* and run `dot -Tpng -O bpf.dot' to draw the graph.
*/
static int