Skip to content

Commit 049599a

Browse files
authored
Merge pull request #97 from exarkun/96.decoder-crash
Fix decoding with k == m == 256
2 parents 12a1e95 + 050a77d commit 049599a

File tree

4 files changed

+51
-15
lines changed

4 files changed

+51
-15
lines changed

changelog

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
Wed Sep 20 10:35:00 EST 2023 exarkun@twistedmatrix.com
2+
* zfec: fix incorrect results, memory corruption, and a sometimes-crash when decoding with k = n = 256.
3+
14
Tue Jan 25 13:45:00 EST 2022 exarkun@twistedmatrix.com
25
* zfec: setup: remove support for python < 3.7
36

setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858
url="https://github.com/tahoe-lafs/zfec",
5959
extras_require={
6060
"bench": ["pyutil >= 3.0.0"],
61-
"test": ["twisted", "pyutil >= 3.0.0"],
61+
"test": ["twisted", "pyutil >= 3.0.0", "hypothesis"],
6262
},
6363
ext_modules=extensions,
6464
cmdclass=versioneer.get_cmdclass(),

zfec/fec.c

+16-3
Original file line numberDiff line numberDiff line change
@@ -511,7 +511,7 @@ fec_encode(const fec_t* code, const gf*restrict const*restrict const src, gf*res
511511
*/
512512
void
513513
build_decode_matrix_into_space(const fec_t*restrict const code, const unsigned*const restrict index, const unsigned k, gf*restrict const matrix) {
514-
unsigned char i;
514+
unsigned short i;
515515
gf* p;
516516
for (i=0, p=matrix; i < k; i++, p += k) {
517517
if (index[i] < k) {
@@ -527,9 +527,22 @@ build_decode_matrix_into_space(const fec_t*restrict const code, const unsigned*c
527527
void
528528
fec_decode(const fec_t* code, const gf*restrict const*restrict const inpkts, gf*restrict const*restrict const outpkts, const unsigned*restrict const index, size_t sz) {
529529
gf* m_dec = (gf*)alloca(code->k * code->k);
530+
531+
/* char is large enough for outix - it counts the number of primary blocks
532+
we are decoding for return. the most primary blocks we might have to
533+
decode is for k == 128, m == 256. in this case we might be given 128
534+
secondary blocks and have to decode 128 primary blocks. if k decreases
535+
then the number of total blocks we might have to return decreases. if
536+
k increases then the number of secondary blocks that exist decreases so
537+
we will be passed some primary blocks and the number of primary blocks
538+
we have to decode decreases. */
530539
unsigned char outix=0;
531-
unsigned char row=0;
532-
unsigned char col=0;
540+
541+
/* row and col are compared directly to k, which could be 256, so make
542+
them large enough to represent 256.
543+
*/
544+
unsigned short row=0;
545+
unsigned short col=0;
533546
build_decode_matrix_into_space(code, index, code->k, m_dec);
534547

535548
for (row=0; row<code->k; row++) {

zfec/test/test_zfec.py

+31-11
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
from io import BytesIO
1111

1212
import unittest
13+
from hypothesis import given
14+
from hypothesis.strategies import integers, binary, lists, just
1315

1416
global VERBOSE
1517
VERBOSE=False
@@ -52,12 +54,6 @@ def _help_test_random():
5254
ss = [ randstr(l//k) for x in range(k) ]
5355
_h(k, m, ss)
5456

55-
def _help_test_random_with_l(l):
56-
m = random.randrange(1, 257)
57-
k = random.randrange(1, m+1)
58-
ss = [ randstr(l//k) for x in range(k) ]
59-
_h(k, m, ss)
60-
6157
def _h_easy(k, m, s):
6258
encer = zfec.easyfec.Encoder(k, m)
6359
nums_and_blocks = list(enumerate(encer.encode(s)))
@@ -127,11 +123,35 @@ def test_from_agl_py(self):
127123
# print "after decoding:"
128124
# print "b0: %s, b1: %s" % tuple(base64.b16encode(x) for x in [b0, b1])
129125

130-
def test_small(self):
131-
for i in range(16):
132-
_help_test_random_with_l(i)
133-
if VERBOSE:
134-
print("%d randomized tests pass." % (i+1))
126+
@given(
127+
integers(min_value=0, max_value=15).flatmap(
128+
lambda l:
129+
integers(min_value=1, max_value=256).flatmap(
130+
lambda m:
131+
integers(min_value=1, max_value=m).flatmap(
132+
lambda k:
133+
lists(
134+
binary(min_size=l//k, max_size=l//k),
135+
min_size=k,
136+
max_size=k,
137+
).flatmap(
138+
lambda ss: just((k, m, ss)),
139+
),
140+
),
141+
),
142+
),
143+
)
144+
def test_small(self, kmss):
145+
"""
146+
Short primary blocks (length between 0 and 15) round-trip through
147+
Encoder / Decoder for all values of k, m, such that:
148+
149+
* 1 <= m <= 256
150+
* 1 <= k <= m
151+
152+
"""
153+
(k, m, ss) = kmss
154+
_h(k, m, ss)
135155

136156
def test_random(self):
137157
for i in range(3):

0 commit comments

Comments
 (0)