Adding upstream version 1.19.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
f46d8ce0c8
commit
d7ceba2005
31 changed files with 1468 additions and 963 deletions
117
unzcrash.cc
117
unzcrash.cc
|
@ -1,6 +1,6 @@
|
|||
/* Unzcrash - Tests robustness of decompressors to corrupted data.
|
||||
Inspired by unzcrash.c from Julian Seward's bzip2.
|
||||
Copyright (C) 2008-2016 Antonio Diaz Diaz.
|
||||
Copyright (C) 2008-2017 Antonio Diaz Diaz.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -40,12 +40,16 @@
|
|||
#error "Environments where CHAR_BIT != 8 are not supported."
|
||||
#endif
|
||||
|
||||
#ifndef INT64_MAX
|
||||
#define INT64_MAX 0x7FFFFFFFFFFFFFFFLL
|
||||
#endif
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
const char * const Program_name = "Unzcrash";
|
||||
const char * const program_name = "unzcrash";
|
||||
const char * const program_year = "2016";
|
||||
const char * const program_year = "2017";
|
||||
const char * invocation_name = 0;
|
||||
|
||||
int verbosity = 0;
|
||||
|
@ -55,14 +59,27 @@ void show_help()
|
|||
{
|
||||
std::printf( "%s - Tests robustness of decompressors to corrupted data.\n", Program_name );
|
||||
std::printf( "\nUsage: %s [options] \"lzip -tv\" filename.lz\n", invocation_name );
|
||||
std::printf( "\nThis program reads the specified file and then repeatedly decompresses\n"
|
||||
"it, increasing 256 times each byte of the compressed data, so as to test\n"
|
||||
"all possible one-byte errors. This should not cause any invalid memory\n"
|
||||
"accesses. If it does, please, report it as a bug.\n"
|
||||
std::printf( "\nBy default, unzcrash reads the specified file and then repeatedly\n"
|
||||
"decompresses it, increasing 256 times each byte of the compressed data,\n"
|
||||
"so as to test all possible one-byte errors.\n"
|
||||
"\nIf the '--block' option is given, unzcrash reads the specified file\n"
|
||||
"and then repeatedly decompresses it, setting all bytes in each\n"
|
||||
"successive block to the value given, so as to test all possible full\n"
|
||||
"sector errors.\n"
|
||||
"\nIf the '--truncate' option is given, unzcrash reads the specified\n"
|
||||
"file and then repeatedly decompresses it, truncating the file to\n"
|
||||
"increasing lengths, so as to test all possible truncation points.\n"
|
||||
"\nNone of the three test modes described above should cause any invalid\n"
|
||||
"memory accesses. If any of them does, please, report it as a bug to the\n"
|
||||
"maintainers of the decompressor being tested.\n"
|
||||
"\nIf the decompressor returns with zero status, unzcrash compares the\n"
|
||||
"output of the decompressor for the original and corrupt files. If the\n"
|
||||
"outputs differ, it means that the decompressor failed to recognize the\n"
|
||||
"corruption and produced garbage output. Please, report it as a bug.\n"
|
||||
"outputs differ, it means that the decompressor returned a false\n"
|
||||
"negative; it failed to recognize the corruption and produced garbage\n"
|
||||
"output. The only exception is when a multimember file is truncated just\n"
|
||||
"after the last byte of a member, producing a shorter but valid\n"
|
||||
"compressed file. Except in this latter case, please, report any false\n"
|
||||
"negative as a bug.\n"
|
||||
"\nIn order to compare the outputs, unzcrash needs a zcmp program able to\n"
|
||||
"understand the format being tested. For example the one provided by zutils.\n"
|
||||
"Use '--zcmp=false' to disable comparisons.\n"
|
||||
|
@ -72,6 +89,7 @@ void show_help()
|
|||
" -b, --bits=<range> test N-bit errors instead of full byte\n"
|
||||
" -B, --block[=<size>][,<val>] test blocks of given size [512,0]\n"
|
||||
" -d, --delta=<n> test one of every n bytes/blocks/truncations\n"
|
||||
" -e, --set-byte=<pos>,<val> set byte at position <pos> to value <val>\n"
|
||||
" -p, --position=<bytes> first byte position to test [default 0]\n"
|
||||
" -q, --quiet suppress all messages\n"
|
||||
" -s, --size=<bytes> number of byte positions to test [all]\n"
|
||||
|
@ -124,12 +142,13 @@ void internal_error( const char * const msg )
|
|||
}
|
||||
|
||||
|
||||
long getnum( const char * const ptr, const long llimit, const long ulimit,
|
||||
const bool comma = false )
|
||||
long long getnum( const char * const ptr, const long long llimit = -LLONG_MAX,
|
||||
const long long ulimit = LLONG_MAX,
|
||||
const char ** const tailp = 0 )
|
||||
{
|
||||
char * tail;
|
||||
errno = 0;
|
||||
long result = strtol( ptr, &tail, 0 );
|
||||
long long result = strtoll( ptr, &tail, 0 );
|
||||
if( tail == ptr )
|
||||
{
|
||||
show_error( "Bad or missing numerical argument.", 0, true );
|
||||
|
@ -138,11 +157,14 @@ long getnum( const char * const ptr, const long llimit, const long ulimit,
|
|||
|
||||
if( !errno && tail[0] )
|
||||
{
|
||||
const int factor = ( tail[1] == 'i' ) ? 1024 : 1000;
|
||||
char * const p = tail++;
|
||||
int factor;
|
||||
bool bsuf; // 'B' suffix is present
|
||||
if( tail[0] == 'i' ) { ++tail; factor = 1024; } else factor = 1000;
|
||||
if( tail[0] == 'B' ) { ++tail; bsuf = true; } else bsuf = false;
|
||||
int exponent = -1; // -1 = bad multiplier
|
||||
switch( tail[0] )
|
||||
switch( *p )
|
||||
{
|
||||
case ',': if( comma ) exponent = 0; break;
|
||||
case 'Y': exponent = 8; break;
|
||||
case 'Z': exponent = 7; break;
|
||||
case 'E': exponent = 6; break;
|
||||
|
@ -152,6 +174,8 @@ long getnum( const char * const ptr, const long llimit, const long ulimit,
|
|||
case 'M': exponent = 2; break;
|
||||
case 'K': if( factor == 1024 ) exponent = 1; break;
|
||||
case 'k': if( factor == 1000 ) exponent = 1; break;
|
||||
case 'B': if( factor == 1000 && !bsuf ) exponent = 0; break;
|
||||
default : if( tailp ) { tail = p; exponent = 0; } break;
|
||||
}
|
||||
if( exponent < 0 )
|
||||
{
|
||||
|
@ -160,7 +184,7 @@ long getnum( const char * const ptr, const long llimit, const long ulimit,
|
|||
}
|
||||
for( int i = 0; i < exponent; ++i )
|
||||
{
|
||||
if( LONG_MAX / factor >= std::labs( result ) ) result *= factor;
|
||||
if( LLONG_MAX / factor >= std::labs( result ) ) result *= factor;
|
||||
else { errno = ERANGE; break; }
|
||||
}
|
||||
}
|
||||
|
@ -170,23 +194,64 @@ long getnum( const char * const ptr, const long llimit, const long ulimit,
|
|||
show_error( "Numerical argument out of limits." );
|
||||
std::exit( 1 );
|
||||
}
|
||||
if( tailp ) *tailp = tail;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void parse_block( const char * const ptr, long & size, uint8_t & value )
|
||||
{
|
||||
const char * const ptr2 = std::strchr( ptr, ',' );
|
||||
const char * tail = ptr;
|
||||
|
||||
if( !ptr2 || ptr2 != ptr )
|
||||
size = getnum( ptr, 1, INT_MAX, true );
|
||||
if( ptr2 )
|
||||
value = getnum( ptr2 + 1, 0, 255 );
|
||||
if( tail[0] != ',' )
|
||||
size = getnum( ptr, 1, INT_MAX, &tail );
|
||||
if( tail[0] == ',' )
|
||||
value = getnum( tail + 1, 0, 255 );
|
||||
else if( tail[0] )
|
||||
{
|
||||
show_error( "Bad separator in argument of '--block'", 0, true );
|
||||
std::exit( 1 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct Bad_byte
|
||||
{
|
||||
enum Mode { literal, delta, flip };
|
||||
long long pos;
|
||||
Mode mode;
|
||||
uint8_t value;
|
||||
|
||||
Bad_byte() : pos( -1 ), mode( literal ), value( 0 ) {}
|
||||
uint8_t operator()( const uint8_t old_value ) const
|
||||
{
|
||||
if( mode == delta ) return old_value + value;
|
||||
if( mode == flip ) return old_value ^ value;
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Recognized formats: <pos>,<value> <pos>,+<value> <pos>,f<value>
|
||||
//
|
||||
void parse_pos_value( const char * const ptr, Bad_byte & bad_byte )
|
||||
{
|
||||
const char * tail;
|
||||
bad_byte.pos = getnum( ptr, 0, INT64_MAX, &tail );
|
||||
if( tail[0] != ',' )
|
||||
{
|
||||
show_error( "Bad separator between <pos> and <val>.", 0, true );
|
||||
std::exit( 1 );
|
||||
}
|
||||
if( tail[1] == '+' ) { ++tail; bad_byte.mode = Bad_byte::delta; }
|
||||
else if( tail[1] == 'f' ) { ++tail; bad_byte.mode = Bad_byte::flip; }
|
||||
else bad_byte.mode = Bad_byte::literal;
|
||||
bad_byte.value = getnum( tail + 1, 0, 255 );
|
||||
}
|
||||
|
||||
|
||||
/* Returns the address of a malloc'd buffer containing the file data and
|
||||
its size in '*size'.
|
||||
the file size in '*size'.
|
||||
In case of error, returns 0 and does not modify '*size'.
|
||||
*/
|
||||
uint8_t * read_file( const char * const name, long * const size )
|
||||
|
@ -309,6 +374,7 @@ int main( const int argc, const char * const argv[] )
|
|||
enum Mode { m_block, m_byte, m_truncate };
|
||||
const char * mode_str[3] = { "block", "byte", "size" };
|
||||
Bitset8 bits; // if Bitset8::parse not called test full byte
|
||||
Bad_byte bad_byte;
|
||||
const char * zcmp_program = "zcmp";
|
||||
long pos = 0;
|
||||
long max_size = LONG_MAX;
|
||||
|
@ -324,6 +390,7 @@ int main( const int argc, const char * const argv[] )
|
|||
{ 'b', "bits", Arg_parser::yes },
|
||||
{ 'B', "block", Arg_parser::maybe },
|
||||
{ 'd', "delta", Arg_parser::yes },
|
||||
{ 'e', "set-byte", Arg_parser::yes },
|
||||
{ 'p', "position", Arg_parser::yes },
|
||||
{ 'q', "quiet", Arg_parser::no },
|
||||
{ 's', "size", Arg_parser::yes },
|
||||
|
@ -331,7 +398,7 @@ int main( const int argc, const char * const argv[] )
|
|||
{ 'v', "verbose", Arg_parser::no },
|
||||
{ 'V', "version", Arg_parser::no },
|
||||
{ 'z', "zcmp", Arg_parser::yes },
|
||||
{ 0 , 0, Arg_parser::no } };
|
||||
{ 0 , 0, Arg_parser::no } };
|
||||
|
||||
const Arg_parser parser( argc, argv, options );
|
||||
if( parser.error().size() ) // bad option
|
||||
|
@ -350,6 +417,7 @@ int main( const int argc, const char * const argv[] )
|
|||
case 'B': if( arg[0] ) parse_block( arg, block_size, block_value );
|
||||
program_mode = m_block; break;
|
||||
case 'd': delta = getnum( arg, 1, INT_MAX ); break;
|
||||
case 'e': parse_pos_value( arg, bad_byte ); break;
|
||||
case 'p': pos = getnum( arg, -LONG_MAX, LONG_MAX ); break;
|
||||
case 'q': verbosity = -1; break;
|
||||
case 's': max_size = getnum( arg, -LONG_MAX, LONG_MAX ); break;
|
||||
|
@ -414,6 +482,11 @@ int main( const int argc, const char * const argv[] )
|
|||
{ show_error( "Nothing to do; domain is empty." ); return 0; }
|
||||
if( max_size < 0 ) max_size += file_size - pos;
|
||||
const long end = ( ( max_size < file_size - pos ) ? pos + max_size : file_size );
|
||||
if( bad_byte.pos >= file_size )
|
||||
{ show_error( "Position of '--set-byte' is beyond end of file." );
|
||||
return 1; }
|
||||
if( bad_byte.pos >= 0 )
|
||||
buffer[bad_byte.pos] = bad_byte( buffer[bad_byte.pos] );
|
||||
long positions = 0, decompressions = 0, successes = 0, failed_comparisons = 0;
|
||||
if( program_mode == m_truncate )
|
||||
for( long i = pos; i < end; i += std::min( delta, end - i ) )
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue