Skip to content

Commit 06c6e81

Browse files
normanrzdaniel-wer
andauthored
add checks for corrupt jumptables (#83)
* Fix typo in README * add checks for corrupt jumptables * fix ci? * ci * ci * include file path in error message --------- Co-authored-by: Daniel <[email protected]>
1 parent cee9b72 commit 06c6e81

File tree

6 files changed

+86
-26
lines changed

6 files changed

+86
-26
lines changed

.github/workflows/python-module.yml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212
cargo test
1313
1414
build_lin:
15-
runs-on: ubuntu-20.04
15+
runs-on: ubuntu-22.04
1616
steps:
1717
- uses: actions/checkout@v1
1818
- name: Build testing docker container
@@ -66,7 +66,7 @@ jobs:
6666
bash -c "\$PYBIN/python setup.py sdist bdist_wheel -p manylinux1_x86_64 && \$PYBIN/twine upload dist/*.whl dist/*.tar.gz"
6767
6868
build_mac:
69-
runs-on: macos-11
69+
runs-on: macos-12
7070
strategy:
7171
max-parallel: 4
7272
matrix:
@@ -89,7 +89,7 @@ jobs:
8989
cd python
9090
export PATH=$PATH:$HOME/.cargo/bin
9191
pip install twine wheel
92-
python setup.py bdist_wheel -p $(python -c "import distutils.util; print(distutils.util.get_platform().replace('x86_64', 'universal2'))")
92+
python setup.py bdist_wheel -p macosx-10.9-universal2
9393
pip install dist/*.whl
9494
- name: Test
9595
run: |
@@ -110,7 +110,10 @@ jobs:
110110
twine upload dist/*.whl
111111
112112
build_win:
113-
runs-on: windows-2019
113+
runs-on: windows-2022
114+
defaults:
115+
run:
116+
shell: bash
114117
strategy:
115118
max-parallel: 4
116119
matrix:
@@ -123,14 +126,12 @@ jobs:
123126
python-version: ${{ matrix.python-version }}
124127
architecture: 'x64' # (x64 or x86)
125128
- name: Build
126-
shell: bash
127129
run: |
128130
cd python
129131
pip install twine wheel
130132
python setup.py bdist_wheel -p $(python -c "import distutils.util; print(distutils.util.get_platform())")
131133
pip install dist/*.whl
132134
- name: Test
133-
shell: bash
134135
run: |
135136
cd python
136137
python -c "import wkw"
@@ -142,7 +143,6 @@ jobs:
142143
python -c "import wkw"
143144
pytest tests -k "not big_read"
144145
- name: Publish
145-
shell: bash
146146
if: startsWith(github.event.ref, 'refs/tags') && matrix.python-version == '3.8'
147147
env:
148148
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ If the file header indicates that the blocks were compressed using LZ4 (by
9898
having a blockType value of either 0x02 or 0x03), the file header is immediately
9999
followed by the jump table.
100100

101-
The jump table is an array of N unsigned 64-bit intergers, where N is the number
101+
The jump table is an array of N unsigned 64-bit integers, where N is the number
102102
of blocks in the file. The n-th entry of the jump table contains the absolute
103103
address (relative to the beginning of the file) of the first byte after the data
104104
of block n.

python/tests/test_wkw.py

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from pathlib import Path
12
import wkw
23
import numpy as np
34
import shutil
@@ -50,14 +51,12 @@ def test_non_negative_offsets():
5051

5152
def test_empty_read():
5253
with wkw.Dataset.create("tests/tmp", wkw.Header(np.uint8)) as dataset:
53-
5454
data = dataset.read((1, 1, 1), (0, 0, 0))
5555
assert data.shape == (1, 0, 0, 0)
5656

5757

5858
def test_readwrite():
5959
with wkw.Dataset.create("tests/tmp", wkw.Header(np.uint8)) as dataset:
60-
6160
header_size = path.getsize(path.join("tests/tmp", "header.wkw"))
6261
test_data = generate_test_data(dataset.header.voxel_type)
6362

@@ -96,7 +95,6 @@ def test_readwrite_live_compression_should_enforce_full_file_write():
9695
with wkw.Dataset.create(
9796
"tests/tmp", wkw.Header(np.uint8, block_type=BLOCK_TYPE_LZ4)
9897
) as dataset:
99-
10098
test_data = generate_test_data(dataset.header.voxel_type)
10199
dataset.write(POSITION, test_data)
102100

@@ -145,7 +143,6 @@ def test_readwrite_live_compression_should_truncate():
145143

146144
def test_compress():
147145
with wkw.Dataset.create("tests/tmp", wkw.Header(np.uint8)) as dataset:
148-
149146
test_data = generate_test_data(dataset.header.voxel_type)
150147
dataset.write(POSITION, test_data)
151148

@@ -269,7 +266,6 @@ def write_and_test_in_given_order(wkw_path, order):
269266

270267

271268
def test_multiple_writes_and_reads():
272-
273269
mem_buffer = np.zeros((200, 200, 200), dtype=np.uint8, order="F")
274270
with wkw.Dataset.create("tests/tmp", wkw.Header(np.uint8)) as dataset:
275271
for i in range(10):
@@ -289,7 +285,6 @@ def test_multiple_writes_and_reads():
289285

290286

291287
def test_multi_channel_column_major_order():
292-
293288
with wkw.Dataset.create(
294289
"tests/tmp", wkw.Header(np.uint8, num_channels=3)
295290
) as dataset:
@@ -340,6 +335,31 @@ def test_invalid_dataset():
340335
print(excinfo.value)
341336

342337

338+
def test_dataset_with_invalid_jumptable():
339+
with wkw.Dataset.create(
340+
"tests/tmp",
341+
wkw.Header(np.uint8, block_type=wkw.Header.BLOCK_TYPE_LZ4HC, file_len=4),
342+
) as dataset:
343+
test_data = generate_test_data(
344+
dataset.header.voxel_type, (32 * 4, 32 * 4, 32 * 4)
345+
)
346+
dataset.write((0, 0, 0), test_data)
347+
348+
# reset jumptable with zeros
349+
data_path = Path("tests/tmp") / "z0" / "y0" / "x0.wkw"
350+
wkw_file_bytes = bytearray(data_path.read_bytes())
351+
zeros = b"\x00" * (4**3 * 8)
352+
wkw_file_bytes[16 : (16 + (4**3) * 8)] = zeros
353+
data_path.write_bytes(wkw_file_bytes)
354+
355+
with pytest.raises(wkw.wkw.WKWException) as excinfo:
356+
with wkw.Dataset.open("tests/tmp") as dataset:
357+
dataset.read((0, 0, 0), (32 * 4,) * 3)
358+
359+
print(excinfo)
360+
assert "Corrupt jump table" in str(excinfo)
361+
362+
343363
def generate_test_data(dtype, size=SIZE, order="C"):
344364
return np.array(
345365
np.random.uniform(np.iinfo(dtype).min, np.iinfo(dtype).max, size).astype(dtype),

rust/src/dataset.rs

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,12 @@ impl Dataset {
5555
}
5656

5757
// create header file
58-
let mut file = fs::File::create(&header_path).or(Err(format!(
59-
"Could not create header file {:?}",
60-
&header_path
61-
)))?;
58+
let mut file = fs::File::create(&header_path).or_else(|err| {
59+
Err(format!(
60+
"Could not create header file {:?}: {}",
61+
&header_path, err
62+
))
63+
})?;
6264

6365
header.write(&mut file)
6466
}
@@ -104,7 +106,15 @@ impl Dataset {
104106

105107
// try to open file
106108
if let Ok(mut file) = File::open(&cur_path) {
107-
file.read_mat(cur_src_pos, mat, cur_dst_pos)?;
109+
match file.read_mat(cur_src_pos, mat, cur_dst_pos) {
110+
Ok(_) => {}
111+
Err(err) => {
112+
return Err(format!(
113+
"Error while reading from file {:?}: {}",
114+
&cur_path, err
115+
));
116+
}
117+
}
108118
}
109119
}
110120
}
@@ -176,8 +186,24 @@ impl Dataset {
176186
let cur_src_pos = cur_box.min() - dst_pos;
177187
let cur_dst_pos = cur_box.min() - cur_file_box.min();
178188

179-
let mut file = File::open_or_create(&cur_path, &self.header)?;
180-
file.write_mat(cur_dst_pos, mat, cur_src_pos)?;
189+
let mut file = match File::open_or_create(&cur_path, &self.header) {
190+
Ok(file) => file,
191+
Err(err) => {
192+
return Err(format!(
193+
"Error while open file {:?} for writing: {}",
194+
&cur_path, err
195+
));
196+
}
197+
};
198+
match file.write_mat(cur_dst_pos, mat, cur_src_pos) {
199+
Ok(_) => {}
200+
Err(err) => {
201+
return Err(format!(
202+
"Error while writing to file {:?}: {}",
203+
&cur_path, err
204+
));
205+
}
206+
}
181207
}
182208
}
183209
}

rust/src/file.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,15 @@ impl File {
357357
let block_size_raw = self.header.block_size();
358358

359359
let buf_lz4_orig = &mut *self.disk_block_buf.as_mut().unwrap();
360-
let buf_lz4 = &mut buf_lz4_orig[..block_size_lz4];
360+
let buf_lz4 = match buf_lz4_orig.get_mut(..block_size_lz4) {
361+
Some(buf_lz4) => buf_lz4,
362+
None => {
363+
return Err(format!(
364+
"Unexpected compressed block length {}",
365+
block_size_lz4
366+
))
367+
}
368+
};
361369

362370
// read compressed block
363371
self.file

rust/src/header.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -208,12 +208,18 @@ impl Header {
208208
let jump_table = &*self.jump_table.as_ref().unwrap();
209209

210210
if block_idx == 0 {
211-
let block_size = jump_table[0] - self.data_offset;
212-
Ok(block_size as usize)
211+
let block_size = jump_table[0].checked_sub(self.data_offset);
212+
match block_size {
213+
Some(block_size) => Ok(block_size as usize),
214+
None => Err(String::from("Corrupt jump table")),
215+
}
213216
} else if block_idx < self.file_vol() {
214217
let block_idx = block_idx as usize;
215-
let block_size = jump_table[block_idx] - jump_table[block_idx - 1];
216-
Ok(block_size as usize)
218+
let block_size = jump_table[block_idx].checked_sub(jump_table[block_idx - 1]);
219+
match block_size {
220+
Some(block_size) => Ok(block_size as usize),
221+
None => Err(String::from("Corrupt jump table")),
222+
}
217223
} else {
218224
Err(String::from("Block index out of bounds"))
219225
}

0 commit comments

Comments
 (0)