mirror of
				https://github.com/qemu/qemu.git
				synced 2025-10-31 12:07:31 +00:00 
			
		
		
		
	 7185845197
			
		
	
	
		7185845197
		
	
	
	
	
		
			
			No caller of fuzzer functions is interested in unicode string values, so replace them with bytes sequences. Signed-off-by: Eduardo Habkost <ehabkost@redhat.com> Reviewed-by: John Snow <jsnow@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> Message-id: 20191016192430.25098-7-ehabkost@redhat.com Message-Id: <20191016192430.25098-7-ehabkost@redhat.com> Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
		
			
				
	
	
		
			369 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			369 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # Fuzzing functions for qcow2 fields
 | |
| #
 | |
| # Copyright (C) 2014 Maria Kustova <maria.k@catit.be>
 | |
| #
 | |
| # 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
 | |
| # the Free Software Foundation, either version 2 of the License, or
 | |
| # (at your option) any later version.
 | |
| #
 | |
| # This program is distributed in the hope that it will be useful,
 | |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
| # GNU General Public License for more details.
 | |
| #
 | |
| # You should have received a copy of the GNU General Public License
 | |
| # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | |
| #
 | |
| 
 | |
| import random
 | |
| from functools import reduce
 | |
| 
 | |
| UINT8 = 0xff
 | |
| UINT16 = 0xffff
 | |
| UINT32 = 0xffffffff
 | |
| UINT64 = 0xffffffffffffffff
 | |
| # Most significant bit orders
 | |
| UINT32_M = 31
 | |
| UINT64_M = 63
 | |
| # Fuzz vectors
 | |
| UINT8_V = [0, 0x10, UINT8//4, UINT8//2 - 1, UINT8//2, UINT8//2 + 1, UINT8 - 1,
 | |
|            UINT8]
 | |
| UINT16_V = [0, 0x100, 0x1000, UINT16//4, UINT16//2 - 1, UINT16//2, UINT16//2 + 1,
 | |
|             UINT16 - 1, UINT16]
 | |
| UINT32_V = [0, 0x100, 0x1000, 0x10000, 0x100000, UINT32//4, UINT32//2 - 1,
 | |
|             UINT32//2, UINT32//2 + 1, UINT32 - 1, UINT32]
 | |
| UINT64_V = UINT32_V + [0x1000000, 0x10000000, 0x100000000, UINT64//4,
 | |
|                        UINT64//2 - 1, UINT64//2, UINT64//2 + 1, UINT64 - 1,
 | |
|                        UINT64]
 | |
| BYTES_V = [b'%s%p%x%d', b'.1024d', b'%.2049d', b'%p%p%p%p', b'%x%x%x%x',
 | |
|            b'%d%d%d%d', b'%s%s%s%s', b'%99999999999s', b'%08x', b'%%20d', b'%%20n',
 | |
|            b'%%20x', b'%%20s', b'%s%s%s%s%s%s%s%s%s%s', b'%p%p%p%p%p%p%p%p%p%p',
 | |
|            b'%#0123456x%08x%x%s%p%d%n%o%u%c%h%l%q%j%z%Z%t%i%e%g%f%a%C%S%08x%%',
 | |
|            b'%s x 129', b'%x x 257']
 | |
| 
 | |
| 
 | |
| def random_from_intervals(intervals):
 | |
|     """Select a random integer number from the list of specified intervals.
 | |
| 
 | |
|     Each interval is a tuple of lower and upper limits of the interval. The
 | |
|     limits are included. Intervals in a list should not overlap.
 | |
|     """
 | |
|     total = reduce(lambda x, y: x + y[1] - y[0] + 1, intervals, 0)
 | |
|     r = random.randint(0, total - 1) + intervals[0][0]
 | |
|     for x in zip(intervals, intervals[1:]):
 | |
|         r = r + (r > x[0][1]) * (x[1][0] - x[0][1] - 1)
 | |
|     return r
 | |
| 
 | |
| 
 | |
| def random_bits(bit_ranges):
 | |
|     """Generate random binary mask with ones in the specified bit ranges.
 | |
| 
 | |
|     Each bit_ranges is a list of tuples of lower and upper limits of bit
 | |
|     positions will be fuzzed. The limits are included. Random amount of bits
 | |
|     in range limits will be set to ones. The mask is returned in decimal
 | |
|     integer format.
 | |
|     """
 | |
|     bit_numbers = []
 | |
|     # Select random amount of random positions in bit_ranges
 | |
|     for rng in bit_ranges:
 | |
|         bit_numbers += random.sample(range(rng[0], rng[1] + 1),
 | |
|                                      random.randint(0, rng[1] - rng[0] + 1))
 | |
|     val = 0
 | |
|     # Set bits on selected positions to ones
 | |
|     for bit in bit_numbers:
 | |
|         val |= 1 << bit
 | |
|     return val
 | |
| 
 | |
| 
 | |
| def truncate_bytes(sequences, length):
 | |
|     """Return sequences truncated to specified length."""
 | |
|     if type(sequences) == list:
 | |
|         return [s[:length] for s in sequences]
 | |
|     else:
 | |
|         return sequences[:length]
 | |
| 
 | |
| 
 | |
| def validator(current, pick, choices):
 | |
|     """Return a value not equal to the current selected by the pick
 | |
|     function from choices.
 | |
|     """
 | |
|     while True:
 | |
|         val = pick(choices)
 | |
|         if not val == current:
 | |
|             return val
 | |
| 
 | |
| 
 | |
| def int_validator(current, intervals):
 | |
|     """Return a random value from intervals not equal to the current.
 | |
| 
 | |
|     This function is useful for selection from valid values except current one.
 | |
|     """
 | |
|     return validator(current, random_from_intervals, intervals)
 | |
| 
 | |
| 
 | |
| def bit_validator(current, bit_ranges):
 | |
|     """Return a random bit mask not equal to the current.
 | |
| 
 | |
|     This function is useful for selection from valid values except current one.
 | |
|     """
 | |
|     return validator(current, random_bits, bit_ranges)
 | |
| 
 | |
| 
 | |
| def bytes_validator(current, sequences):
 | |
|     """Return a random bytes value from the list not equal to the current.
 | |
| 
 | |
|     This function is useful for selection from valid values except current one.
 | |
|     """
 | |
|     return validator(current, random.choice, sequences)
 | |
| 
 | |
| 
 | |
| def selector(current, constraints, validate=int_validator):
 | |
|     """Select one value from all defined by constraints.
 | |
| 
 | |
|     Each constraint produces one random value satisfying to it. The function
 | |
|     randomly selects one value satisfying at least one constraint (depending on
 | |
|     constraints overlaps).
 | |
|     """
 | |
|     def iter_validate(c):
 | |
|         """Apply validate() only to constraints represented as lists.
 | |
| 
 | |
|         This auxiliary function replaces short circuit conditions not supported
 | |
|         in Python 2.4
 | |
|         """
 | |
|         if type(c) == list:
 | |
|             return validate(current, c)
 | |
|         else:
 | |
|             return c
 | |
| 
 | |
|     fuzz_values = [iter_validate(c) for c in constraints]
 | |
|     # Remove current for cases it's implicitly specified in constraints
 | |
|     # Duplicate validator functionality to prevent decreasing of probability
 | |
|     # to get one of allowable values
 | |
|     # TODO: remove validators after implementation of intelligent selection
 | |
|     # of fields will be fuzzed
 | |
|     try:
 | |
|         fuzz_values.remove(current)
 | |
|     except ValueError:
 | |
|         pass
 | |
|     return random.choice(fuzz_values)
 | |
| 
 | |
| 
 | |
| def magic(current):
 | |
|     """Fuzz magic header field.
 | |
| 
 | |
|     The function just returns the current magic value and provides uniformity
 | |
|     of calls for all fuzzing functions.
 | |
|     """
 | |
|     return current
 | |
| 
 | |
| 
 | |
| def version(current):
 | |
|     """Fuzz version header field."""
 | |
|     constraints = UINT32_V + [
 | |
|         [(2, 3)],  # correct values
 | |
|         [(0, 1), (4, UINT32)]
 | |
|     ]
 | |
|     return selector(current, constraints)
 | |
| 
 | |
| 
 | |
| def backing_file_offset(current):
 | |
|     """Fuzz backing file offset header field."""
 | |
|     constraints = UINT64_V
 | |
|     return selector(current, constraints)
 | |
| 
 | |
| 
 | |
| def backing_file_size(current):
 | |
|     """Fuzz backing file size header field."""
 | |
|     constraints = UINT32_V
 | |
|     return selector(current, constraints)
 | |
| 
 | |
| 
 | |
| def cluster_bits(current):
 | |
|     """Fuzz cluster bits header field."""
 | |
|     constraints = UINT32_V + [
 | |
|         [(9, 20)],  # correct values
 | |
|         [(0, 9), (20, UINT32)]
 | |
|     ]
 | |
|     return selector(current, constraints)
 | |
| 
 | |
| 
 | |
| def size(current):
 | |
|     """Fuzz image size header field."""
 | |
|     constraints = UINT64_V
 | |
|     return selector(current, constraints)
 | |
| 
 | |
| 
 | |
| def crypt_method(current):
 | |
|     """Fuzz crypt method header field."""
 | |
|     constraints = UINT32_V + [
 | |
|         1,
 | |
|         [(2, UINT32)]
 | |
|     ]
 | |
|     return selector(current, constraints)
 | |
| 
 | |
| 
 | |
| def l1_size(current):
 | |
|     """Fuzz L1 table size header field."""
 | |
|     constraints = UINT32_V
 | |
|     return selector(current, constraints)
 | |
| 
 | |
| 
 | |
| def l1_table_offset(current):
 | |
|     """Fuzz L1 table offset header field."""
 | |
|     constraints = UINT64_V
 | |
|     return selector(current, constraints)
 | |
| 
 | |
| 
 | |
| def refcount_table_offset(current):
 | |
|     """Fuzz refcount table offset header field."""
 | |
|     constraints = UINT64_V
 | |
|     return selector(current, constraints)
 | |
| 
 | |
| 
 | |
| def refcount_table_clusters(current):
 | |
|     """Fuzz refcount table clusters header field."""
 | |
|     constraints = UINT32_V
 | |
|     return selector(current, constraints)
 | |
| 
 | |
| 
 | |
| def nb_snapshots(current):
 | |
|     """Fuzz number of snapshots header field."""
 | |
|     constraints = UINT32_V
 | |
|     return selector(current, constraints)
 | |
| 
 | |
| 
 | |
| def snapshots_offset(current):
 | |
|     """Fuzz snapshots offset header field."""
 | |
|     constraints = UINT64_V
 | |
|     return selector(current, constraints)
 | |
| 
 | |
| 
 | |
| def incompatible_features(current):
 | |
|     """Fuzz incompatible features header field."""
 | |
|     constraints = [
 | |
|         [(0, 1)],  # allowable values
 | |
|         [(0, UINT64_M)]
 | |
|     ]
 | |
|     return selector(current, constraints, bit_validator)
 | |
| 
 | |
| 
 | |
| def compatible_features(current):
 | |
|     """Fuzz compatible features header field."""
 | |
|     constraints = [
 | |
|         [(0, UINT64_M)]
 | |
|     ]
 | |
|     return selector(current, constraints, bit_validator)
 | |
| 
 | |
| 
 | |
| def autoclear_features(current):
 | |
|     """Fuzz autoclear features header field."""
 | |
|     constraints = [
 | |
|         [(0, UINT64_M)]
 | |
|     ]
 | |
|     return selector(current, constraints, bit_validator)
 | |
| 
 | |
| 
 | |
| def refcount_order(current):
 | |
|     """Fuzz number of refcount order header field."""
 | |
|     constraints = UINT32_V
 | |
|     return selector(current, constraints)
 | |
| 
 | |
| 
 | |
| def header_length(current):
 | |
|     """Fuzz number of refcount order header field."""
 | |
|     constraints = UINT32_V + [
 | |
|         72,
 | |
|         104,
 | |
|         [(0, UINT32)]
 | |
|     ]
 | |
|     return selector(current, constraints)
 | |
| 
 | |
| 
 | |
| def bf_name(current):
 | |
|     """Fuzz the backing file name."""
 | |
|     constraints = [
 | |
|         truncate_bytes(BYTES_V, len(current))
 | |
|     ]
 | |
|     return selector(current, constraints, bytes_validator)
 | |
| 
 | |
| 
 | |
| def ext_magic(current):
 | |
|     """Fuzz magic field of a header extension."""
 | |
|     constraints = UINT32_V
 | |
|     return selector(current, constraints)
 | |
| 
 | |
| 
 | |
| def ext_length(current):
 | |
|     """Fuzz length field of a header extension."""
 | |
|     constraints = UINT32_V
 | |
|     return selector(current, constraints)
 | |
| 
 | |
| 
 | |
| def bf_format(current):
 | |
|     """Fuzz backing file format in the corresponding header extension."""
 | |
|     constraints = [
 | |
|         truncate_bytes(BYTES_V, len(current)),
 | |
|         truncate_bytes(BYTES_V, (len(current) + 7) & ~7)  # Fuzz padding
 | |
|     ]
 | |
|     return selector(current, constraints, bytes_validator)
 | |
| 
 | |
| 
 | |
| def feature_type(current):
 | |
|     """Fuzz feature type field of a feature name table header extension."""
 | |
|     constraints = UINT8_V
 | |
|     return selector(current, constraints)
 | |
| 
 | |
| 
 | |
| def feature_bit_number(current):
 | |
|     """Fuzz bit number field of a feature name table header extension."""
 | |
|     constraints = UINT8_V
 | |
|     return selector(current, constraints)
 | |
| 
 | |
| 
 | |
| def feature_name(current):
 | |
|     """Fuzz feature name field of a feature name table header extension."""
 | |
|     constraints = [
 | |
|         truncate_bytes(BYTES_V, len(current)),
 | |
|         truncate_bytes(BYTES_V, 46)  # Fuzz padding (field length = 46)
 | |
|     ]
 | |
|     return selector(current, constraints, bytes_validator)
 | |
| 
 | |
| 
 | |
| def l1_entry(current):
 | |
|     """Fuzz an entry of the L1 table."""
 | |
|     constraints = UINT64_V
 | |
|     # Reserved bits are ignored
 | |
|     # Added a possibility when only flags are fuzzed
 | |
|     offset = 0x7fffffffffffffff & \
 | |
|              random.choice([selector(current, constraints), current])
 | |
|     is_cow = random.randint(0, 1)
 | |
|     return offset + (is_cow << UINT64_M)
 | |
| 
 | |
| 
 | |
| def l2_entry(current):
 | |
|     """Fuzz an entry of an L2 table."""
 | |
|     constraints = UINT64_V
 | |
|     # Reserved bits are ignored
 | |
|     # Add a possibility when only flags are fuzzed
 | |
|     offset = 0x3ffffffffffffffe & \
 | |
|              random.choice([selector(current, constraints), current])
 | |
|     is_compressed = random.randint(0, 1)
 | |
|     is_cow = random.randint(0, 1)
 | |
|     is_zero = random.randint(0, 1)
 | |
|     value = offset + (is_cow << UINT64_M) + \
 | |
|             (is_compressed << UINT64_M - 1) + is_zero
 | |
|     return value
 | |
| 
 | |
| 
 | |
| def refcount_table_entry(current):
 | |
|     """Fuzz an entry of the refcount table."""
 | |
|     constraints = UINT64_V
 | |
|     return selector(current, constraints)
 | |
| 
 | |
| 
 | |
| def refcount_block_entry(current):
 | |
|     """Fuzz an entry of a refcount block."""
 | |
|     constraints = UINT16_V
 | |
|     return selector(current, constraints)
 |