}
}
- while ((i = recv_token(f_in, &data)) != 0) {
+ while (1) {
+ data = NULL;
+ i = recv_token(f_in, &data);
+ if (i == 0)
+ break;
+
if (INFO_GTE(PROGRESS, 1))
show_progress(offset, total_size);
maybe_send_keepalive(time(NULL), MSK_ALLOW_FLUSH | MSK_ACTIVE_RECEIVER);
if (i > 0) {
+ if (!data) {
+ rprintf(FERROR, "Invalid literal token with no data [%s]\n", who_am_i());
+ exit_cleanup(RERR_PROTOCOL);
+ }
if (DEBUG_GTE(DELTASUM, 3)) {
rprintf(FINFO,"data recv %d at %s\n",
i, big_num(offset));
int32 i = read_int(f);
if (i <= 0)
return i;
+ /* simple_send_token caps each literal chunk at CHUNK_SIZE;
+ * reject anything larger so a hostile peer cannot drive the
+ * read_buf below past our static CHUNK_SIZE buffer. */
+ if (i > CHUNK_SIZE) {
+ rprintf(FERROR, "invalid uncompressed token length %ld [%s]\n",
+ (long)i, who_am_i());
+ exit_cleanup(RERR_PROTOCOL);
+ }
residue = i;
}
static char *dbuf;
/* for decoding runs of tokens */
+#define MAX_TOKEN_INDEX ((int32)0x7ffffffe)
+
static int32 rx_token;
static int32 rx_run;
+static NORETURN void invalid_compressed_token(void)
+{
+ rprintf(FERROR, "invalid token number in compressed stream\n");
+ exit_cleanup(RERR_PROTOCOL);
+}
+
+static int32 recv_compressed_token_num(int f, int32 flag)
+{
+ if (flag & TOKEN_REL) {
+ int32 incr = flag & 0x3f;
+ if (rx_token > MAX_TOKEN_INDEX - incr)
+ invalid_compressed_token();
+ rx_token += incr;
+ flag >>= 6;
+ } else {
+ rx_token = read_int(f);
+ if (rx_token < 0 || rx_token > MAX_TOKEN_INDEX)
+ invalid_compressed_token();
+ }
+
+ if (flag & 1) {
+ rx_run = read_byte(f);
+ rx_run += read_byte(f) << 8;
+ if (rx_run <= 0 || rx_token > MAX_TOKEN_INDEX - rx_run)
+ invalid_compressed_token();
+ recv_state = r_running;
+ }
+
+ return -1 - rx_token;
+}
+
+static int32 recv_compressed_token_run(void)
+{
+ if (rx_run <= 0 || rx_token >= MAX_TOKEN_INDEX)
+ invalid_compressed_token();
+ ++rx_token;
+ if (--rx_run == 0)
+ recv_state = r_idle;
+ return -1 - rx_token;
+}
+
/* Receive a deflated token and inflate it */
static int32 recv_deflated_token(int f, char **data)
{
}
/* here we have a token of some kind */
- if (flag & TOKEN_REL) {
- rx_token += flag & 0x3f;
- flag >>= 6;
- } else {
- rx_token = read_int(f);
- if (rx_token < 0) {
- rprintf(FERROR, "invalid token number in compressed stream\n");
- exit_cleanup(RERR_PROTOCOL);
- }
- }
- if (flag & 1) {
- rx_run = read_byte(f);
- rx_run += read_byte(f) << 8;
- recv_state = r_running;
- }
- return -1 - rx_token;
+ return recv_compressed_token_num(f, flag);
case r_inflating:
rx_strm.next_out = (Bytef *)dbuf;
break;
case r_running:
- ++rx_token;
- if (--rx_run == 0)
- recv_state = r_idle;
- return -1 - rx_token;
+ return recv_compressed_token_run();
}
}
}
return 0;
}
/* here we have a token of some kind */
- if (flag & TOKEN_REL) {
- rx_token += flag & 0x3f;
- flag >>= 6;
- } else {
- rx_token = read_int(f);
- if (rx_token < 0) {
- rprintf(FERROR, "invalid token number in compressed stream\n");
- exit_cleanup(RERR_PROTOCOL);
- }
- }
- if (flag & 1) {
- rx_run = read_byte(f);
- rx_run += read_byte(f) << 8;
- recv_state = r_running;
- }
- return -1 - rx_token;
+ return recv_compressed_token_num(f, flag);
case r_inflated: /* zstd doesn't get into this state */
break;
break;
case r_running:
- ++rx_token;
- if (--rx_run == 0)
- recv_state = r_idle;
- return -1 - rx_token;
+ return recv_compressed_token_run();
}
}
}
}
/* here we have a token of some kind */
- if (flag & TOKEN_REL) {
- rx_token += flag & 0x3f;
- flag >>= 6;
- } else {
- rx_token = read_int(f);
- if (rx_token < 0) {
- rprintf(FERROR, "invalid token number in compressed stream\n");
- exit_cleanup(RERR_PROTOCOL);
- }
- }
- if (flag & 1) {
- rx_run = read_byte(f);
- rx_run += read_byte(f) << 8;
- recv_state = r_running;
- }
- return -1 - rx_token;
+ return recv_compressed_token_num(f, flag);
case r_inflating:
avail_out = LZ4_decompress_safe(next_in, dbuf, avail_in, size);
break;
case r_running:
- ++rx_token;
- if (--rx_run == 0)
- recv_state = r_idle;
- return -1 - rx_token;
+ return recv_compressed_token_run();
}
}
}