mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2025-01-19 03:06:33 +00:00
repairer: add deterministic test for #819, mark as TODO
This commit is contained in:
parent
d0c6aa569d
commit
876c4a153b
@ -1140,21 +1140,21 @@ def corrupt_field(data, offset, size, debug=False):
|
|||||||
log.msg("testing: corrupting offset %d, size %d randomizing field, orig: %r, newval: %r" % (offset, size, data[offset:offset+size], newval))
|
log.msg("testing: corrupting offset %d, size %d randomizing field, orig: %r, newval: %r" % (offset, size, data[offset:offset+size], newval))
|
||||||
return data[:offset]+newval+data[offset+size:]
|
return data[:offset]+newval+data[offset+size:]
|
||||||
|
|
||||||
def _corrupt_nothing(data):
|
def _corrupt_nothing(data, debug=False):
|
||||||
"""Leave the data pristine. """
|
"""Leave the data pristine. """
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def _corrupt_file_version_number(data):
|
def _corrupt_file_version_number(data, debug=False):
|
||||||
"""Scramble the file data -- the share file version number have one bit
|
"""Scramble the file data -- the share file version number have one bit
|
||||||
flipped or else will be changed to a random value."""
|
flipped or else will be changed to a random value."""
|
||||||
return corrupt_field(data, 0x00, 4)
|
return corrupt_field(data, 0x00, 4)
|
||||||
|
|
||||||
def _corrupt_size_of_file_data(data):
|
def _corrupt_size_of_file_data(data, debug=False):
|
||||||
"""Scramble the file data -- the field showing the size of the share data
|
"""Scramble the file data -- the field showing the size of the share data
|
||||||
within the file will be set to one smaller."""
|
within the file will be set to one smaller."""
|
||||||
return corrupt_field(data, 0x04, 4)
|
return corrupt_field(data, 0x04, 4)
|
||||||
|
|
||||||
def _corrupt_sharedata_version_number(data):
|
def _corrupt_sharedata_version_number(data, debug=False):
|
||||||
"""Scramble the file data -- the share data version number will have one
|
"""Scramble the file data -- the share data version number will have one
|
||||||
bit flipped or else will be changed to a random value, but not 1 or 2."""
|
bit flipped or else will be changed to a random value, but not 1 or 2."""
|
||||||
return corrupt_field(data, 0x0c, 4)
|
return corrupt_field(data, 0x0c, 4)
|
||||||
@ -1166,7 +1166,7 @@ def _corrupt_sharedata_version_number(data):
|
|||||||
newsharevernumbytes = struct.pack(">L", newsharevernum)
|
newsharevernumbytes = struct.pack(">L", newsharevernum)
|
||||||
return data[:0x0c] + newsharevernumbytes + data[0x0c+4:]
|
return data[:0x0c] + newsharevernumbytes + data[0x0c+4:]
|
||||||
|
|
||||||
def _corrupt_sharedata_version_number_to_plausible_version(data):
|
def _corrupt_sharedata_version_number_to_plausible_version(data, debug=False):
|
||||||
"""Scramble the file data -- the share data version number will be
|
"""Scramble the file data -- the share data version number will be
|
||||||
changed to 2 if it is 1 or else to 1 if it is 2."""
|
changed to 2 if it is 1 or else to 1 if it is 2."""
|
||||||
sharevernum = struct.unpack(">L", data[0x0c:0x0c+4])[0]
|
sharevernum = struct.unpack(">L", data[0x0c:0x0c+4])[0]
|
||||||
@ -1178,7 +1178,7 @@ def _corrupt_sharedata_version_number_to_plausible_version(data):
|
|||||||
newsharevernumbytes = struct.pack(">L", newsharevernum)
|
newsharevernumbytes = struct.pack(">L", newsharevernum)
|
||||||
return data[:0x0c] + newsharevernumbytes + data[0x0c+4:]
|
return data[:0x0c] + newsharevernumbytes + data[0x0c+4:]
|
||||||
|
|
||||||
def _corrupt_segment_size(data):
|
def _corrupt_segment_size(data, debug=False):
|
||||||
"""Scramble the file data -- the field showing the size of the segment
|
"""Scramble the file data -- the field showing the size of the segment
|
||||||
will have one bit flipped or else be changed to a random value."""
|
will have one bit flipped or else be changed to a random value."""
|
||||||
sharevernum = struct.unpack(">L", data[0x0c:0x0c+4])[0]
|
sharevernum = struct.unpack(">L", data[0x0c:0x0c+4])[0]
|
||||||
@ -1188,7 +1188,7 @@ def _corrupt_segment_size(data):
|
|||||||
else:
|
else:
|
||||||
return corrupt_field(data, 0x0c+0x04, 8, debug=False)
|
return corrupt_field(data, 0x0c+0x04, 8, debug=False)
|
||||||
|
|
||||||
def _corrupt_size_of_sharedata(data):
|
def _corrupt_size_of_sharedata(data, debug=False):
|
||||||
"""Scramble the file data -- the field showing the size of the data
|
"""Scramble the file data -- the field showing the size of the data
|
||||||
within the share data will have one bit flipped or else will be changed
|
within the share data will have one bit flipped or else will be changed
|
||||||
to a random value."""
|
to a random value."""
|
||||||
@ -1199,7 +1199,7 @@ def _corrupt_size_of_sharedata(data):
|
|||||||
else:
|
else:
|
||||||
return corrupt_field(data, 0x0c+0x0c, 8)
|
return corrupt_field(data, 0x0c+0x0c, 8)
|
||||||
|
|
||||||
def _corrupt_offset_of_sharedata(data):
|
def _corrupt_offset_of_sharedata(data, debug=False):
|
||||||
"""Scramble the file data -- the field showing the offset of the data
|
"""Scramble the file data -- the field showing the offset of the data
|
||||||
within the share data will have one bit flipped or else be changed to a
|
within the share data will have one bit flipped or else be changed to a
|
||||||
random value."""
|
random value."""
|
||||||
@ -1210,7 +1210,7 @@ def _corrupt_offset_of_sharedata(data):
|
|||||||
else:
|
else:
|
||||||
return corrupt_field(data, 0x0c+0x14, 8)
|
return corrupt_field(data, 0x0c+0x14, 8)
|
||||||
|
|
||||||
def _corrupt_offset_of_ciphertext_hash_tree(data):
|
def _corrupt_offset_of_ciphertext_hash_tree(data, debug=False):
|
||||||
"""Scramble the file data -- the field showing the offset of the
|
"""Scramble the file data -- the field showing the offset of the
|
||||||
ciphertext hash tree within the share data will have one bit flipped or
|
ciphertext hash tree within the share data will have one bit flipped or
|
||||||
else be changed to a random value.
|
else be changed to a random value.
|
||||||
@ -1222,7 +1222,7 @@ def _corrupt_offset_of_ciphertext_hash_tree(data):
|
|||||||
else:
|
else:
|
||||||
return corrupt_field(data, 0x0c+0x24, 8, debug=False)
|
return corrupt_field(data, 0x0c+0x24, 8, debug=False)
|
||||||
|
|
||||||
def _corrupt_offset_of_block_hashes(data):
|
def _corrupt_offset_of_block_hashes(data, debug=False):
|
||||||
"""Scramble the file data -- the field showing the offset of the block
|
"""Scramble the file data -- the field showing the offset of the block
|
||||||
hash tree within the share data will have one bit flipped or else will be
|
hash tree within the share data will have one bit flipped or else will be
|
||||||
changed to a random value."""
|
changed to a random value."""
|
||||||
@ -1233,7 +1233,7 @@ def _corrupt_offset_of_block_hashes(data):
|
|||||||
else:
|
else:
|
||||||
return corrupt_field(data, 0x0c+0x2c, 8)
|
return corrupt_field(data, 0x0c+0x2c, 8)
|
||||||
|
|
||||||
def _corrupt_offset_of_block_hashes_to_truncate_crypttext_hashes(data):
|
def _corrupt_offset_of_block_hashes_to_truncate_crypttext_hashes(data, debug=False):
|
||||||
"""Scramble the file data -- the field showing the offset of the block
|
"""Scramble the file data -- the field showing the offset of the block
|
||||||
hash tree within the share data will have a multiple of hash size
|
hash tree within the share data will have a multiple of hash size
|
||||||
subtracted from it, thus causing the downloader to download an incomplete
|
subtracted from it, thus causing the downloader to download an incomplete
|
||||||
@ -1251,7 +1251,7 @@ def _corrupt_offset_of_block_hashes_to_truncate_crypttext_hashes(data):
|
|||||||
newvalstr = struct.pack(">Q", newval)
|
newvalstr = struct.pack(">Q", newval)
|
||||||
return data[:0x0c+0x2c]+newvalstr+data[0x0c+0x2c+8:]
|
return data[:0x0c+0x2c]+newvalstr+data[0x0c+0x2c+8:]
|
||||||
|
|
||||||
def _corrupt_offset_of_share_hashes(data):
|
def _corrupt_offset_of_share_hashes(data, debug=False):
|
||||||
"""Scramble the file data -- the field showing the offset of the share
|
"""Scramble the file data -- the field showing the offset of the share
|
||||||
hash tree within the share data will have one bit flipped or else will be
|
hash tree within the share data will have one bit flipped or else will be
|
||||||
changed to a random value."""
|
changed to a random value."""
|
||||||
@ -1262,7 +1262,7 @@ def _corrupt_offset_of_share_hashes(data):
|
|||||||
else:
|
else:
|
||||||
return corrupt_field(data, 0x0c+0x34, 8)
|
return corrupt_field(data, 0x0c+0x34, 8)
|
||||||
|
|
||||||
def _corrupt_offset_of_uri_extension(data):
|
def _corrupt_offset_of_uri_extension(data, debug=False):
|
||||||
"""Scramble the file data -- the field showing the offset of the uri
|
"""Scramble the file data -- the field showing the offset of the uri
|
||||||
extension will have one bit flipped or else will be changed to a random
|
extension will have one bit flipped or else will be changed to a random
|
||||||
value."""
|
value."""
|
||||||
@ -1292,7 +1292,7 @@ def _corrupt_offset_of_uri_extension_to_force_short_read(data, debug=False):
|
|||||||
log.msg("testing: corrupting offset %d, size %d, changing %d to %d (len(data) == %d)" % (0x48, 8, struct.unpack(">Q", data[0x48:0x48+8])[0], len(data)-0x0c-3, len(data)))
|
log.msg("testing: corrupting offset %d, size %d, changing %d to %d (len(data) == %d)" % (0x48, 8, struct.unpack(">Q", data[0x48:0x48+8])[0], len(data)-0x0c-3, len(data)))
|
||||||
return data[:0x48] + struct.pack(">Q", len(data)-0x0c-3) + data[0x48+8:]
|
return data[:0x48] + struct.pack(">Q", len(data)-0x0c-3) + data[0x48+8:]
|
||||||
|
|
||||||
def _corrupt_mutable_share_data(data):
|
def _corrupt_mutable_share_data(data, debug=False):
|
||||||
prefix = data[:32]
|
prefix = data[:32]
|
||||||
assert prefix == MutableShareFile.MAGIC, "This function is designed to corrupt mutable shares of v1, and the magic number doesn't look right: %r vs %r" % (prefix, MutableShareFile.MAGIC)
|
assert prefix == MutableShareFile.MAGIC, "This function is designed to corrupt mutable shares of v1, and the magic number doesn't look right: %r vs %r" % (prefix, MutableShareFile.MAGIC)
|
||||||
data_offset = MutableShareFile.DATA_OFFSET
|
data_offset = MutableShareFile.DATA_OFFSET
|
||||||
@ -1305,7 +1305,7 @@ def _corrupt_mutable_share_data(data):
|
|||||||
length = data_offset + offsets["enc_privkey"] - start
|
length = data_offset + offsets["enc_privkey"] - start
|
||||||
return corrupt_field(data, start, length)
|
return corrupt_field(data, start, length)
|
||||||
|
|
||||||
def _corrupt_share_data(data):
|
def _corrupt_share_data(data, debug=False):
|
||||||
"""Scramble the file data -- the field containing the share data itself
|
"""Scramble the file data -- the field containing the share data itself
|
||||||
will have one bit flipped or else will be changed to a random value."""
|
will have one bit flipped or else will be changed to a random value."""
|
||||||
sharevernum = struct.unpack(">L", data[0x0c:0x0c+4])[0]
|
sharevernum = struct.unpack(">L", data[0x0c:0x0c+4])[0]
|
||||||
@ -1319,7 +1319,7 @@ def _corrupt_share_data(data):
|
|||||||
|
|
||||||
return corrupt_field(data, 0x0c+0x44, sharedatasize)
|
return corrupt_field(data, 0x0c+0x44, sharedatasize)
|
||||||
|
|
||||||
def _corrupt_crypttext_hash_tree(data):
|
def _corrupt_crypttext_hash_tree(data, debug=False):
|
||||||
"""Scramble the file data -- the field containing the crypttext hash tree
|
"""Scramble the file data -- the field containing the crypttext hash tree
|
||||||
will have one bit flipped or else will be changed to a random value.
|
will have one bit flipped or else will be changed to a random value.
|
||||||
"""
|
"""
|
||||||
@ -1332,9 +1332,26 @@ def _corrupt_crypttext_hash_tree(data):
|
|||||||
crypttexthashtreeoffset = struct.unpack(">Q", data[0x0c+0x24:0x0c+0x24+8])[0]
|
crypttexthashtreeoffset = struct.unpack(">Q", data[0x0c+0x24:0x0c+0x24+8])[0]
|
||||||
blockhashesoffset = struct.unpack(">Q", data[0x0c+0x2c:0x0c+0x2c+8])[0]
|
blockhashesoffset = struct.unpack(">Q", data[0x0c+0x2c:0x0c+0x2c+8])[0]
|
||||||
|
|
||||||
return corrupt_field(data, crypttexthashtreeoffset, blockhashesoffset-crypttexthashtreeoffset)
|
return corrupt_field(data, crypttexthashtreeoffset, blockhashesoffset-crypttexthashtreeoffset, debug=debug)
|
||||||
|
|
||||||
def _corrupt_block_hashes(data):
|
def _corrupt_crypttext_hash_tree_byte_9_bit_7(data, debug=False):
|
||||||
|
"""Scramble the file data -- the field containing the crypttext hash tree
|
||||||
|
will have the 7th bit of the 9th byte flipped.
|
||||||
|
"""
|
||||||
|
sharevernum = struct.unpack(">L", data[0x0c:0x0c+4])[0]
|
||||||
|
assert sharevernum in (1, 2), "This test is designed to corrupt immutable shares of v1 or v2 in specific ways."
|
||||||
|
if sharevernum == 1:
|
||||||
|
crypttexthashtreeoffset = struct.unpack(">L", data[0x0c+0x14:0x0c+0x14+4])[0]
|
||||||
|
blockhashesoffset = struct.unpack(">L", data[0x0c+0x18:0x0c+0x18+4])[0]
|
||||||
|
else:
|
||||||
|
crypttexthashtreeoffset = struct.unpack(">Q", data[0x0c+0x24:0x0c+0x24+8])[0]
|
||||||
|
blockhashesoffset = struct.unpack(">Q", data[0x0c+0x2c:0x0c+0x2c+8])[0]
|
||||||
|
|
||||||
|
if debug:
|
||||||
|
log.msg("original data: %r" % (data,))
|
||||||
|
return data[:9] + chr(ord(data[9])^0x02) + data[10:]
|
||||||
|
|
||||||
|
def _corrupt_block_hashes(data, debug=False):
|
||||||
"""Scramble the file data -- the field containing the block hash tree
|
"""Scramble the file data -- the field containing the block hash tree
|
||||||
will have one bit flipped or else will be changed to a random value.
|
will have one bit flipped or else will be changed to a random value.
|
||||||
"""
|
"""
|
||||||
@ -1349,7 +1366,7 @@ def _corrupt_block_hashes(data):
|
|||||||
|
|
||||||
return corrupt_field(data, blockhashesoffset, sharehashesoffset-blockhashesoffset)
|
return corrupt_field(data, blockhashesoffset, sharehashesoffset-blockhashesoffset)
|
||||||
|
|
||||||
def _corrupt_share_hashes(data):
|
def _corrupt_share_hashes(data, debug=False):
|
||||||
"""Scramble the file data -- the field containing the share hash chain
|
"""Scramble the file data -- the field containing the share hash chain
|
||||||
will have one bit flipped or else will be changed to a random value.
|
will have one bit flipped or else will be changed to a random value.
|
||||||
"""
|
"""
|
||||||
@ -1364,7 +1381,7 @@ def _corrupt_share_hashes(data):
|
|||||||
|
|
||||||
return corrupt_field(data, sharehashesoffset, uriextoffset-sharehashesoffset)
|
return corrupt_field(data, sharehashesoffset, uriextoffset-sharehashesoffset)
|
||||||
|
|
||||||
def _corrupt_length_of_uri_extension(data):
|
def _corrupt_length_of_uri_extension(data, debug=False):
|
||||||
"""Scramble the file data -- the field showing the length of the uri
|
"""Scramble the file data -- the field showing the length of the uri
|
||||||
extension will have one bit flipped or else will be changed to a random
|
extension will have one bit flipped or else will be changed to a random
|
||||||
value."""
|
value."""
|
||||||
@ -1377,7 +1394,7 @@ def _corrupt_length_of_uri_extension(data):
|
|||||||
uriextoffset = struct.unpack(">Q", data[0x0c+0x3c:0x0c+0x3c+8])[0]
|
uriextoffset = struct.unpack(">Q", data[0x0c+0x3c:0x0c+0x3c+8])[0]
|
||||||
return corrupt_field(data, uriextoffset, 8)
|
return corrupt_field(data, uriextoffset, 8)
|
||||||
|
|
||||||
def _corrupt_uri_extension(data):
|
def _corrupt_uri_extension(data, debug=False):
|
||||||
"""Scramble the file data -- the field containing the uri extension will
|
"""Scramble the file data -- the field containing the uri extension will
|
||||||
have one bit flipped or else will be changed to a random value."""
|
have one bit flipped or else will be changed to a random value."""
|
||||||
sharevernum = struct.unpack(">L", data[0x0c:0x0c+4])[0]
|
sharevernum = struct.unpack(">L", data[0x0c:0x0c+4])[0]
|
||||||
|
@ -301,11 +301,11 @@ class GridTestMixin:
|
|||||||
corruptdata = corruptor_function(sharedata)
|
corruptdata = corruptor_function(sharedata)
|
||||||
open(sharefile, "wb").write(corruptdata)
|
open(sharefile, "wb").write(corruptdata)
|
||||||
|
|
||||||
def corrupt_shares_numbered(self, uri, shnums, corruptor):
|
def corrupt_shares_numbered(self, uri, shnums, corruptor, debug=False):
|
||||||
for (i_shnum, i_serverid, i_sharefile) in self.find_shares(uri):
|
for (i_shnum, i_serverid, i_sharefile) in self.find_shares(uri):
|
||||||
if i_shnum in shnums:
|
if i_shnum in shnums:
|
||||||
sharedata = open(i_sharefile, "rb").read()
|
sharedata = open(i_sharefile, "rb").read()
|
||||||
corruptdata = corruptor(sharedata)
|
corruptdata = corruptor(sharedata, debug=debug)
|
||||||
open(i_sharefile, "wb").write(corruptdata)
|
open(i_sharefile, "wb").write(corruptdata)
|
||||||
|
|
||||||
def GET(self, urlpath, followRedirect=False, return_response=False,
|
def GET(self, urlpath, followRedirect=False, return_response=False,
|
||||||
|
@ -101,13 +101,13 @@ class Verifier(GridTestMixin, unittest.TestCase, RepairTestMixin):
|
|||||||
d.addCallback(_check2)
|
d.addCallback(_check2)
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def _help_test_verify(self, corruptor, judgement, shnum=0):
|
def _help_test_verify(self, corruptor, judgement, shnum=0, debug=False):
|
||||||
self.set_up_grid(num_clients=2)
|
self.set_up_grid(num_clients=2)
|
||||||
d = self.upload_and_stash()
|
d = self.upload_and_stash()
|
||||||
d.addCallback(lambda ignored: self._stash_counts())
|
d.addCallback(lambda ignored: self._stash_counts())
|
||||||
|
|
||||||
d.addCallback(lambda ignored:
|
d.addCallback(lambda ignored:
|
||||||
self.corrupt_shares_numbered(self.uri, [shnum],corruptor))
|
self.corrupt_shares_numbered(self.uri, [shnum],corruptor,debug=debug))
|
||||||
d.addCallback(lambda ignored:
|
d.addCallback(lambda ignored:
|
||||||
self.c1_filenode.check(Monitor(), verify=True))
|
self.c1_filenode.check(Monitor(), verify=True))
|
||||||
def _check(vr):
|
def _check(vr):
|
||||||
@ -281,6 +281,12 @@ class Verifier(GridTestMixin, unittest.TestCase, RepairTestMixin):
|
|||||||
return self._help_test_verify(common._corrupt_crypttext_hash_tree,
|
return self._help_test_verify(common._corrupt_crypttext_hash_tree,
|
||||||
self.judge_invisible_corruption)
|
self.judge_invisible_corruption)
|
||||||
|
|
||||||
|
def test_corrupt_crypttext_hashtree_byte_9_bit_7(self):
|
||||||
|
self.basedir = "repairer/Verify/corrupt_crypttext_hashtree"
|
||||||
|
return self._help_test_verify(common._corrupt_crypttext_hash_tree_byte_9_bit_7,
|
||||||
|
self.judge_invisible_corruption, debug=False)
|
||||||
|
test_corrupt_crypttext_hashtree_byte_9_bit_7.todo = "fix this please! This is #819. (Brian or Zooko probably) (You can start by setting debug=True.)"
|
||||||
|
|
||||||
def test_corrupt_block_hashtree(self):
|
def test_corrupt_block_hashtree(self):
|
||||||
self.basedir = "repairer/Verify/corrupt_block_hashtree"
|
self.basedir = "repairer/Verify/corrupt_block_hashtree"
|
||||||
return self._help_test_verify(common._corrupt_block_hashes,
|
return self._help_test_verify(common._corrupt_block_hashes,
|
||||||
|
Loading…
Reference in New Issue
Block a user