Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1857)

Unified Diff: testing/clusterfuzz/common/fuzzy_types.py

Issue 2166913002: bluetooth: Generate random service uuid and generate random fake adapter. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@bluetooth-fuzzer-basics
Patch Set: Address scheib's comments Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « testing/clusterfuzz/common/__init__.py ('k') | testing/clusterfuzz/common/utils.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: testing/clusterfuzz/common/fuzzy_types.py
diff --git a/testing/clusterfuzz/common/fuzzy_types.py b/testing/clusterfuzz/common/fuzzy_types.py
new file mode 100644
index 0000000000000000000000000000000000000000..08bfea6739a3c850030b983ea3477b14d3a7b0aa
--- /dev/null
+++ b/testing/clusterfuzz/common/fuzzy_types.py
@@ -0,0 +1,207 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import base64
+import functools
+import itertools
+import os
+import random
+import re
+import string
+import sys
+import textwrap
+
+from . import utils
+
+
+def FuzzyInt(n):
+ """Returns an integer derived from the input by one of several mutations."""
+ int_sizes = [8, 16, 32, 64, 128]
+ mutations = [
+ lambda n: utils.UniformExpoInteger(0, sys.maxint.bit_length() + 1),
+ lambda n: -utils.UniformExpoInteger(0, sys.maxint.bit_length()),
+ lambda n: 2 ** random.choice(int_sizes) - 1,
+ lambda n: 2 ** random.choice(int_sizes),
+ lambda n: 0,
+ lambda n: -n,
+ lambda n: n + 1,
+ lambda n: n - 1,
+ lambda n: n + random.randint(-1024, 1024),
+ ]
+ return random.choice(mutations)(n)
+
+
+def FuzzyString(s):
+ """Returns a string derived from the input by one of several mutations."""
+ # First try some mutations that try to recognize certain types of strings
+ try:
+ s.decode("utf-8") # These mutations only make sense for textual data
+ except UnicodeDecodeError:
+ pass
+ else:
+ chained_mutations = [
+ FuzzIntsInString,
+ FuzzBase64InString,
+ FuzzListInString,
+ ]
+ original = s
+ for mutation in chained_mutations:
+ s = mutation(s)
+ # Stop if we've modified the string and our coin comes up heads
+ if s != original and random.getrandbits(1):
+ return s
+
+ # If we're still here, apply a more generic mutation
+ mutations = [
+ lambda s: "".join(random.choice(string.printable) for i in
+ xrange(utils.UniformExpoInteger(0, 14))),
+ lambda s: "".join(unichr(random.randint(0, sys.maxunicode)) for i in
+ xrange(utils.UniformExpoInteger(0, 14))).encode("utf-8"),
+ lambda s: os.urandom(utils.UniformExpoInteger(0, 14)),
+ lambda s: s * utils.UniformExpoInteger(1, 5),
+ lambda s: s + "A" * utils.UniformExpoInteger(0, 14),
+ lambda s: "A" * utils.UniformExpoInteger(0, 14) + s,
+ lambda s: s[:-random.randint(1, max(1, len(s) - 1))],
+ lambda s: textwrap.fill(s, random.randint(1, max(1, len(s) - 1))),
+ lambda s: "",
+ ]
+ return random.choice(mutations)(s)
+
+
+def FuzzIntsInString(s):
+ """Returns a string where some integers have been fuzzed with FuzzyInt."""
+ def ReplaceInt(m):
+ val = m.group()
+ if random.getrandbits(1): # Flip a coin to decide whether to fuzz
+ return val
+ if not random.getrandbits(4): # Delete the integer 1/16th of the time
+ return ""
+ decimal = val.isdigit() # Assume decimal digits means a decimal number
+ n = FuzzyInt(int(val) if decimal else int(val, 16))
+ return str(n) if decimal else "%x" % n
+ return re.sub(r"\b[a-fA-F]*\d[0-9a-fA-F]*\b", ReplaceInt, s)
+
+
+def FuzzBase64InString(s):
+ """Returns a string where Base64 components are fuzzed with FuzzyBuffer."""
+ def ReplaceBase64(m):
+ fb = FuzzyBuffer(base64.b64decode(m.group()))
+ fb.RandomMutation()
+ return base64.b64encode(fb)
+ # This only matches obvious Base64 words with trailing equals signs
+ return re.sub(r"(?<![A-Za-z0-9+/])"
+ r"(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)"
+ r"(?![A-Za-z0-9+/])", ReplaceBase64, s)
+
+
+def FuzzListInString(s, separators=r", |,|; |;|\r\n|\s"):
+ """Tries to interpret the string as a list, and fuzzes it if successful."""
+ seps = re.findall(separators, s)
+ if not seps:
+ return s
+ sep = random.choice(seps) # Ones that appear often are more likely
+ items = FuzzyList(s.split(sep))
+ items.RandomMutation()
+ return sep.join(items)
+
+
+class FuzzySequence(object):
+ """A helpful mixin for writing fuzzy mutable sequence types.
+
+ If a method parameter is left at its default value of None, an appropriate
+ random value will be chosen.
+ """
+
+ def Overwrite(self, value, location=None, amount=None):
+ """Overwrite amount elements starting at location with value.
+
+ Value can be a function of no arguments, in which case it will be called
+ every time a new value is needed.
+ """
+ if location is None:
+ location = random.randint(0, max(0, len(self) - 1))
+ if amount is None:
+ amount = utils.RandomLowInteger(min(1, len(self)), len(self) - location)
+ if hasattr(value, "__call__"):
+ new_elements = (value() for i in xrange(amount))
+ else:
+ new_elements = itertools.repeat(value, amount)
+ self[location:location+amount] = new_elements
+
+ def Insert(self, value, location=None, amount=None, max_exponent=14):
+ """Insert amount elements starting at location.
+
+ Value can be a function of no arguments, in which case it will be called
+ every time a new value is needed.
+ """
+ if location is None:
+ location = random.randint(0, max(0, len(self) - 1))
+ if amount is None:
+ amount = utils.UniformExpoInteger(0, max_exponent)
+ if hasattr(value, "__call__"):
+ new_elements = (value() for i in xrange(amount))
+ else:
+ new_elements = itertools.repeat(value, amount)
+ self[location:location] = new_elements
+
+ def Delete(self, location=None, amount=None):
+ """Delete amount elements starting at location."""
+ if location is None:
+ location = random.randint(0, max(0, len(self) - 1))
+ if amount is None:
+ amount = utils.RandomLowInteger(min(1, len(self)), len(self) - location)
+ del self[location:location+amount]
+
+
+class FuzzyList(list, FuzzySequence):
+ """A list with additional methods for fuzzing."""
+
+ def RandomMutation(self, count=None, new_element=""):
+ """Apply count random mutations chosen from a list."""
+ random_items = lambda: random.choice(self) if self else new_element
+ mutations = [
+ lambda: random.shuffle(self),
+ self.reverse,
+ functools.partial(self.Overwrite, new_element),
+ functools.partial(self.Overwrite, random_items),
+ functools.partial(self.Insert, new_element, max_exponent=10),
+ functools.partial(self.Insert, random_items, max_exponent=10),
+ self.Delete,
+ ]
+ if count is None:
+ count = utils.RandomLowInteger(1, 5, beta=3.0)
+ for _ in xrange(count):
+ random.choice(mutations)()
+
+
+class FuzzyBuffer(bytearray, FuzzySequence):
+ """A bytearray with additional methods for mutating the sequence of bytes."""
+
+ def __repr__(self):
+ return "%s(%r)" % (self.__class__.__name__, str(self))
+
+ def FlipBits(self, num_bits=None):
+ """Flip num_bits bits in the buffer at random."""
+ if num_bits is None:
+ num_bits = utils.RandomLowInteger(min(1, len(self)), len(self) * 8)
+ for bit in random.sample(xrange(len(self) * 8), num_bits):
+ self[bit / 8] ^= 1 << (bit % 8)
+
+ def RandomMutation(self, count=None):
+ """Apply count random mutations chosen from a weighted list."""
+ random_bytes = lambda: random.randint(0x00, 0xFF)
+ mutations = [
+ (self.FlipBits, 1),
+ (functools.partial(self.Overwrite, random_bytes), 1/3.0),
+ (functools.partial(self.Overwrite, 0xFF), 1/3.0),
+ (functools.partial(self.Overwrite, 0x00), 1/3.0),
+ (functools.partial(self.Insert, random_bytes), 1/3.0),
+ (functools.partial(self.Insert, 0xFF), 1/3.0),
+ (functools.partial(self.Insert, 0x00), 1/3.0),
+ (self.Delete, 1),
+ ]
+ if count is None:
+ count = utils.RandomLowInteger(1, 5, beta=3.0)
+ for _ in xrange(count):
+ utils.WeightedChoice(mutations)()
« no previous file with comments | « testing/clusterfuzz/common/__init__.py ('k') | testing/clusterfuzz/common/utils.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698