Will, I finally managed to fix this nasty bug. I've pushed the patches to the master branch and am attaching an aggregated patch that you can apply on top of dev18.
Regards, Willy
diff --git a/src/peers.c b/src/peers.c index 789fa8c..5e4ff35 100644 --- a/src/peers.c +++ b/src/peers.c @@ -596,26 +596,19 @@ switchstate: } case PEER_SESSION_WAITMSG: { struct peer_session *ps = (struct peer_session *)si->conn->xprt_ctx; + struct stksess *ts, *newts = NULL; char c; int totl = 0; reql = bo_getblk(si->ob, (char *)&c, sizeof(c), totl); - if (reql <= 0) { /* closed or EOL not found */ - if (reql == 0) { - /* nothing to read */ - goto incomplete; - } - si->applet.st0 = PEER_SESSION_END; - goto switchstate; - } + if (reql <= 0) /* closed or EOL not found */ + goto incomplete; + totl += reql; if ((c & 0x80) || (c == 'D')) { /* Here we have data message */ unsigned int pushack; - struct stksess *ts; - struct stksess *newts; - struct stktable_key stkey; int srvid; uint32_t netinteger; @@ -625,90 +618,65 @@ switchstate: } else { reql = bo_getblk(si->ob, (char *)&netinteger, sizeof(netinteger), totl); - if (reql <= 0) { /* closed or EOL not found */ - if (reql == 0) { - goto incomplete; - } - si->applet.st0 = PEER_SESSION_END; - goto switchstate; - } + if (reql <= 0) /* closed or EOL not found */ + goto incomplete; + totl += reql; pushack = ntohl(netinteger); } - /* read key */ + /* read key. We try to read it directly + * to the target memory location so that + * we are certain there is always enough + * space. However, if the allocation fails, + * we fall back to trash and we ignore the + * input data not to block the sync of other + * tables (think about a full table with + * "nopurge" for example). + */ if (ps->table->table->type == STKTABLE_TYPE_STRING) { - /* type string */ - stkey.key = stkey.data.buf; - + /* read size first */ reql = bo_getblk(si->ob, (char *)&netinteger, sizeof(netinteger), totl); - if (reql <= 0) { /* closed or EOL not found */ - if (reql == 0) { - goto incomplete; - } - si->applet.st0 = PEER_SESSION_END; - goto switchstate; - } + if (reql <= 0) /* closed or EOL not found */ + goto incomplete; + totl += reql; - stkey.key_len = ntohl(netinteger); + newts = stksess_new(ps->table->table, NULL); + reql = bo_getblk(si->ob, newts ? (char *)newts->key.key : trash.str, ntohl(netinteger), totl); + if (reql <= 0) /* closed or EOL not found */ + goto incomplete; + + /* always truncate the string to the approprite size */ + if (newts) + newts->key.key[MIN(ntohl(netinteger), ps->table->table->key_size)] = 0; - reql = bo_getblk(si->ob, stkey.key, stkey.key_len, totl); - if (reql <= 0) { /* closed or EOL not found */ - if (reql == 0) { - goto incomplete; - } - si->applet.st0 = PEER_SESSION_END; - goto switchstate; - } totl += reql; } else if (ps->table->table->type == STKTABLE_TYPE_INTEGER) { - /* type integer */ - stkey.key_len = (size_t)-1; - stkey.key = &stkey.data.integer; - - reql = bo_getblk(si->ob, (char *)&netinteger, sizeof(netinteger), totl); - if (reql <= 0) { /* closed or EOL not found */ - if (reql == 0) { - goto incomplete; - } - si->applet.st0 = PEER_SESSION_END; - goto switchstate; - } + newts = stksess_new(ps->table->table, NULL); + reql = bo_getblk(si->ob, newts ? (char *)newts->key.key : trash.str, sizeof(netinteger), totl); + if (reql <= 0) /* closed or EOL not found */ + goto incomplete; totl += reql; - stkey.data.integer = ntohl(netinteger); } else { - /* type ip */ - stkey.key_len = (size_t)-1; - stkey.key = stkey.data.buf; - - reql = bo_getblk(si->ob, (char *)&stkey.data.buf, ps->table->table->key_size, totl); - if (reql <= 0) { /* closed or EOL not found */ - if (reql == 0) { - goto incomplete; - } - si->applet.st0 = PEER_SESSION_END; - goto switchstate; - } + /* type ip or binary */ + newts = stksess_new(ps->table->table, NULL); + reql = bo_getblk(si->ob, newts ? (char *)newts->key.key : trash.str, ps->table->table->key_size, totl); + if (reql <= 0) /* closed or EOL not found */ + goto incomplete; totl += reql; - } /* read server id */ reql = bo_getblk(si->ob, (char *)&netinteger, sizeof(netinteger), totl); - if (reql <= 0) { /* closed or EOL not found */ - if (reql == 0) { - goto incomplete; - } - si->applet.st0 = PEER_SESSION_END; - goto switchstate; - } + if (reql <= 0) /* closed or EOL not found */ + goto incomplete; + totl += reql; srvid = ntohl(netinteger); /* update entry */ - newts = stksess_new(ps->table->table, &stkey); if (newts) { /* lookup for existing entry */ ts = stktable_lookup(ps->table->table, newts); @@ -716,12 +684,15 @@ switchstate: /* the entry already exist, we can free ours */ stktable_touch(ps->table->table, ts, 0); stksess_free(ps->table->table, newts); + newts = NULL; } else { struct eb32_node *eb; /* create new entry */ ts = stktable_store(ps->table->table, newts, 0); + newts = NULL; /* don't reuse it */ + ts->upd.key= (++ps->table->table->update)+(2^31); eb = eb32_insert(&ps->table->table->updates, &ts->upd); if (eb != &ts->upd) { @@ -806,13 +777,9 @@ switchstate: uint32_t netinteger; reql = bo_getblk(si->ob, (char *)&netinteger, sizeof(netinteger), totl); - if (reql <= 0) { /* closed or EOL not found */ - if (reql == 0) { - goto incomplete; - } - si->applet.st0 = PEER_SESSION_END; - goto switchstate; - } + if (reql <= 0) /* closed or EOL not found */ + goto incomplete; + totl += reql; /* Consider remote is up to date with "acked" version */ @@ -828,8 +795,23 @@ switchstate: bo_skip(si->ob, totl); /* loop on that state to peek next message */ - continue; + goto switchstate; + incomplete: + /* we get here when a bo_getblk() returns <= 0 in reql */ + + /* first, we may have to release newts */ + if (newts) { + stksess_free(ps->table->table, newts); + newts = NULL; + } + + if (reql < 0) { + /* there was an error */ + si->applet.st0 = PEER_SESSION_END; + goto switchstate; + } + /* Nothing to read, now we start to write */ /* Confirm finished or partial messages */ diff --git a/src/stick_table.c b/src/stick_table.c index 52a5866..3097e66 100644 --- a/src/stick_table.c +++ b/src/stick_table.c @@ -152,7 +152,8 @@ static int stktable_trash_oldest(struct stktable *t, int to_batch) * Allocate and initialise a new sticky session. * The new sticky session is returned or NULL in case of lack of memory. * Sticky sessions should only be allocated this way, and must be freed using - * stksess_free(). Increase table <t> sticky session counter. + * stksess_free(). Table <t>'s sticky session counter is increased. If <key> + * is not NULL, it is assigned to the new session. */ struct stksess *stksess_new(struct stktable *t, struct stktable_key *key) { @@ -170,7 +171,8 @@ struct stksess *stksess_new(struct stktable *t, struct stktable_key *key) if (ts) { t->current++; stksess_init(t, ts); - stksess_setkey(t, ts, key); + if (key) + stksess_setkey(t, ts, key); } return ts;