diff --git a/receiver.c b/receiver.c index 8cf8366b..a487ad5a 100644 --- a/receiver.c +++ b/receiver.c @@ -318,7 +318,12 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r, } } - 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); @@ -326,6 +331,10 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r, 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)); diff --git a/token.c b/token.c index 773f14a2..62ffae15 100644 --- a/token.c +++ b/token.c @@ -292,6 +292,14 @@ static int32 simple_recv_token(int f, char **data) 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; } @@ -494,9 +502,52 @@ static char *cbuf; 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) { @@ -587,22 +638,7 @@ 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; @@ -622,10 +658,7 @@ static int32 recv_deflated_token(int f, char **data) break; case r_running: - ++rx_token; - if (--rx_run == 0) - recv_state = r_idle; - return -1 - rx_token; + return recv_compressed_token_run(); } } } @@ -836,22 +869,7 @@ static int32 recv_zstd_token(int f, char **data) 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; @@ -882,10 +900,7 @@ static int32 recv_zstd_token(int f, char **data) break; case r_running: - ++rx_token; - if (--rx_run == 0) - recv_state = r_idle; - return -1 - rx_token; + return recv_compressed_token_run(); } } } @@ -1005,22 +1020,7 @@ static int32 recv_compressed_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: avail_out = LZ4_decompress_safe(next_in, dbuf, avail_in, size); @@ -1036,10 +1036,7 @@ static int32 recv_compressed_token(int f, char **data) break; case r_running: - ++rx_token; - if (--rx_run == 0) - recv_state = r_idle; - return -1 - rx_token; + return recv_compressed_token_run(); } } }