fix: validate delta output and integration test skips #207
+12
-2
@@ -458,7 +458,8 @@ Delta* delta_deserialize(const Data* data) {
|
|||||||
|
|
||||||
void* delta_apply(const void* old_data, uint64_t old_size, const Delta* delta,
|
void* delta_apply(const void* old_data, uint64_t old_size, const Delta* delta,
|
||||||
uint32_t block_size) {
|
uint32_t block_size) {
|
||||||
if (!old_data || !delta)
|
if (!old_data || !delta || (delta->new_file_size > 0 && delta->instructions == NULL) ||
|
||||||
|
(delta->instruction_count > 0 && block_size == 0))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
void* output = malloc((size_t)delta->new_file_size);
|
void* output = malloc((size_t)delta->new_file_size);
|
||||||
@@ -472,10 +473,15 @@ void* delta_apply(const void* old_data, uint64_t old_size, const Delta* delta,
|
|||||||
for (uint32_t i = 0; i < delta->instruction_count; i++) {
|
for (uint32_t i = 0; i < delta->instruction_count; i++) {
|
||||||
if (delta->instructions[i].type == DELTA_INSTR_BLOCK_MATCH) {
|
if (delta->instructions[i].type == DELTA_INSTR_BLOCK_MATCH) {
|
||||||
uint64_t src_offset = (uint64_t)delta->instructions[i].match.block_index * block_size;
|
uint64_t src_offset = (uint64_t)delta->instructions[i].match.block_index * block_size;
|
||||||
|
if (src_offset > UINT64_MAX - delta->instructions[i].match.block_offset) {
|
||||||
|
free(output);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
src_offset += delta->instructions[i].match.block_offset;
|
src_offset += delta->instructions[i].match.block_offset;
|
||||||
uint32_t len = delta->instructions[i].match.length;
|
uint32_t len = delta->instructions[i].match.length;
|
||||||
|
|
||||||
if (src_offset + len > old_size) {
|
if (src_offset > old_size || (uint64_t)len > old_size - src_offset ||
|
||||||
|
out_pos > delta->new_file_size || (uint64_t)len > delta->new_file_size - out_pos) {
|
||||||
free(output);
|
free(output);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -483,6 +489,10 @@ void* delta_apply(const void* old_data, uint64_t old_size, const Delta* delta,
|
|||||||
out_pos += len;
|
out_pos += len;
|
||||||
} else {
|
} else {
|
||||||
uint32_t len = delta->instructions[i].literal.length;
|
uint32_t len = delta->instructions[i].literal.length;
|
||||||
|
if (out_pos > delta->new_file_size || (uint64_t)len > delta->new_file_size - out_pos) {
|
||||||
|
free(output);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
memcpy(out + out_pos, delta->instructions[i].literal.data, len);
|
memcpy(out + out_pos, delta->instructions[i].literal.data, len);
|
||||||
out_pos += len;
|
out_pos += len;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -238,7 +238,7 @@ class TestProgress:
|
|||||||
)
|
)
|
||||||
assert result.returncode == 0, f"Exit {result.returncode}: {result.stderr[:100]}"
|
assert result.returncode == 0, f"Exit {result.returncode}: {result.stderr[:100]}"
|
||||||
output = result.stdout + result.stderr
|
output = result.stdout + result.stderr
|
||||||
assert len(output) >= 0
|
assert output, "--progress produced no output"
|
||||||
|
|
||||||
|
|
||||||
class TestBandwidthLimit:
|
class TestBandwidthLimit:
|
||||||
|
|||||||
@@ -52,9 +52,11 @@ def _check_ssh():
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
_check_ssh()
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="module", autouse=True)
|
@pytest.fixture(scope="module", autouse=True)
|
||||||
def setup_test_data():
|
def setup_test_data():
|
||||||
_check_ssh()
|
|
||||||
if SSH_AVAILABLE:
|
if SSH_AVAILABLE:
|
||||||
generate_test_files(SOURCE_DIR, full=False)
|
generate_test_files(SOURCE_DIR, full=False)
|
||||||
clean_dir(DEST_DIR)
|
clean_dir(DEST_DIR)
|
||||||
@@ -84,8 +86,12 @@ def _run_ssh_test(name, flags, expected_missing=None):
|
|||||||
return make_result(name, True, duration)
|
return make_result(name, True, duration)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(not SSH_AVAILABLE, reason="SSH to localhost not available")
|
|
||||||
class TestSSHStandard:
|
class TestSSHStandard:
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def require_ssh(self):
|
||||||
|
if not SSH_AVAILABLE:
|
||||||
|
pytest.skip("SSH to localhost not available")
|
||||||
|
|
||||||
def test_standard(self):
|
def test_standard(self):
|
||||||
r = _run_ssh_test("SSH (localhost)", [])
|
r = _run_ssh_test("SSH (localhost)", [])
|
||||||
assert r["status"] == "Success", r["error"]
|
assert r["status"] == "Success", r["error"]
|
||||||
@@ -119,8 +125,12 @@ class TestSSHStandard:
|
|||||||
assert r["status"] == "Success", r["error"]
|
assert r["status"] == "Success", r["error"]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(not SSH_AVAILABLE, reason="SSH to localhost not available")
|
|
||||||
class TestSSHFeatures:
|
class TestSSHFeatures:
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def require_ssh(self):
|
||||||
|
if not SSH_AVAILABLE:
|
||||||
|
pytest.skip("SSH to localhost not available")
|
||||||
|
|
||||||
def test_archive(self):
|
def test_archive(self):
|
||||||
r = _run_ssh_test("SSH Archive (-a)", ["-a"])
|
r = _run_ssh_test("SSH Archive (-a)", ["-a"])
|
||||||
assert r["status"] == "Success", r["error"]
|
assert r["status"] == "Success", r["error"]
|
||||||
|
|||||||
@@ -323,6 +323,22 @@ static void test_large_file_delta() {
|
|||||||
free(new_data);
|
free(new_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_delta_apply_rejects_output_overflow() {
|
||||||
|
uint8_t old_data[8] = {0};
|
||||||
|
uint8_t literal_data[2] = {'x', 'y'};
|
||||||
|
DeltaInstruction instruction = {
|
||||||
|
.type = DELTA_INSTR_LITERAL,
|
||||||
|
.literal = {.data = literal_data, .length = sizeof(literal_data)},
|
||||||
|
};
|
||||||
|
Delta delta = {
|
||||||
|
.new_file_size = 1,
|
||||||
|
.instruction_count = 1,
|
||||||
|
.instructions = &instruction,
|
||||||
|
};
|
||||||
|
|
||||||
|
EXPECT_TRUE(delta_apply(old_data, sizeof(old_data), &delta, 1) == NULL);
|
||||||
|
}
|
||||||
|
|
||||||
void test_delta() {
|
void test_delta() {
|
||||||
test_adler32_basic();
|
test_adler32_basic();
|
||||||
test_adler32_different_data();
|
test_adler32_different_data();
|
||||||
@@ -338,4 +354,5 @@ void test_delta() {
|
|||||||
test_should_attempt();
|
test_should_attempt();
|
||||||
test_is_worthwhile();
|
test_is_worthwhile();
|
||||||
test_large_file_delta();
|
test_large_file_delta();
|
||||||
|
test_delta_apply_rejects_output_overflow();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user