tahoe-lafs / zfec

zfec -- an efficient, portable erasure coding tool
Other
373 stars 44 forks source link

zfec.Decoder.decode segfaults for k == m == 256 #96

Closed exarkun closed 9 months ago

exarkun commented 9 months ago

Reproducer:

import zfec
k = m = 256
blocks = [b''] * k
nums = list(range(k))
decer = zfec.Decoder(k, m)
print("Decoding")
decer.decode(blocks, nums)
print("Decoded")

Results:

❯ python repro.py 
Decoding
Segmentation fault (core dumped)

stack trace:

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff775a1cd in __memmove_avx_unaligned_erms () from /nix/store/7fz6dhhriwv6n4kdg05qi8cwf7mfqza9-glibc-2.35-163/lib/libc.so.6
(gdb) bt
#0  0x00007ffff775a1cd in __memmove_avx_unaligned_erms () from /nix/store/7fz6dhhriwv6n4kdg05qi8cwf7mfqza9-glibc-2.35-163/lib/libc.so.6
#1  0x00007ffff7e1a392 in memcpy (__len=256, __src=<optimized out>, __dest=<optimized out>)
    at /nix/store/4pqv2mwdn88h7xvsm7a5zplrd8sxzvw0-glibc-2.35-163-dev/include/bits/string_fortified.h:29
#2  build_decode_matrix_into_space (code=0x4f0910, index=0x7fffffff52e0, k=256, matrix=0x7ffffffe4240 "\001") at zfec/fec.c:521
#3  0x00007ffff7e1abce in fec_decode (code=0x4f0910, inpkts=0x0, outpkts=0x0, index=0x7fffffff52e0, sz=0) at zfec/fec.c:533
#4  0x0000000000000000 in ?? ()

Apparent source line in fec.c:

memcpy(p, &(code->enc_matrix[index[i] * code->k]), k);
exarkun commented 9 months ago

enc_matrix should have k * n == 65536 elements (as allocated by NEW_GF_MATRIX in fec_new). index[i] * code->k is 16777216 which is impossibly far beyond the end the allocated space for enc_matrix, which it is used to index.

Since code->k is effectively constant I suppose that index[i] has been mis-computed at some point.

exarkun commented 9 months ago

valgrind confirms this invalid read:

==1535306== Invalid read of size 8                                                       
==1535306==    at 0x484FDED: memmove (in /nix/store/zxw3x6cmhpw4x09lhb2aim3zkynr9mld-valgrind-3.19.0/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)                          
==1535306==    by 0x13062391: memcpy (string_fortified.h:29)                                                                                                                      
==1535306==    by 0x13062391: build_decode_matrix_into_space (fec.c:521)                 
==1535306==    by 0x13062BCD: fec_decode (fec.c:533)         
==1535306==  Address 0x12f81370 is 0 bytes after a block of size 65,536 alloc'd                                                                                                   
==1535306==    at 0x484679B: malloc (in /nix/store/zxw3x6cmhpw4x09lhb2aim3zkynr9mld-valgrind-3.19.0/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==1535306==    by 0x13061E99: fec_new (fec.c:449)                                                                                                                                 
==1535306==    by 0x130603E1: Decoder_init (_fecmodule.c:363)                                                                                                                     
==1535306==    by 0x4977AD6: type_call (in /nix/store/rc9cz7z4qlgmsbwvpw2acig5g2rdws46-python3-3.10.5/lib/libpython3.10.so.1.0)                                                   
==1535306==    by 0x4914363: _PyObject_MakeTpCall (in /nix/store/rc9cz7z4qlgmsbwvpw2acig5g2rdws46-python3-3.10.5/lib/libpython3.10.so.1.0)                                        
==1535306==    by 0x48C7509: _PyEval_EvalFrameDefault (in /nix/store/rc9cz7z4qlgmsbwvpw2acig5g2rdws46-python3-3.10.5/lib/libpython3.10.so.1.0)                                    
==1535306==    by 0x4A4205E: _PyEval_Vector (in /nix/store/rc9cz7z4qlgmsbwvpw2acig5g2rdws46-python3-3.10.5/lib/libpython3.10.so.1.0)                                              
==1535306==    by 0x4A426C7: PyEval_EvalCode (in /nix/store/rc9cz7z4qlgmsbwvpw2acig5g2rdws46-python3-3.10.5/lib/libpython3.10.so.1.0)                                             
==1535306==    by 0x4AC6D8C: run_mod (in /nix/store/rc9cz7z4qlgmsbwvpw2acig5g2rdws46-python3-3.10.5/lib/libpython3.10.so.1.0)                                                     
==1535306==    by 0x4AD3741: _PyRun_SimpleFileObject (in /nix/store/rc9cz7z4qlgmsbwvpw2acig5g2rdws46-python3-3.10.5/lib/libpython3.10.so.1.0)                                     
==1535306==    by 0x4AD3D1A: _PyRun_AnyFileObject (in /nix/store/rc9cz7z4qlgmsbwvpw2acig5g2rdws46-python3-3.10.5/lib/libpython3.10.so.1.0)                                        
==1535306==    by 0x4AD7EDE: Py_RunMain (in /nix/store/rc9cz7z4qlgmsbwvpw2acig5g2rdws46-python3-3.10.5/lib/libpython3.10.so.1.0)                                                  

and the idea that enc_matrix is 65536 bytes long.

exarkun commented 9 months ago

Huh, index is a parameter to fec_decode - not a value computed in fec.c. So these values seem to come from the Python bindings.

exarkun commented 9 months ago

The Python code computes index correctly as far as I can tell, though.

exarkun commented 9 months ago

The loop variable i in build_decode_matrix_into_space is unsigned char. k is unsigned [int]. The loop condition is i < k which will never be false for k == 256. The loop runs and runs until it incorrectly overwrites some memory that leads to the corruption of index.