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

Side by Side 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, 4 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 unified diff | Download patch
« no previous file with comments | « testing/clusterfuzz/common/__init__.py ('k') | testing/clusterfuzz/common/utils.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Copyright 2016 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 import base64
6 import functools
7 import itertools
8 import os
9 import random
10 import re
11 import string
12 import sys
13 import textwrap
14
15 from . import utils
16
17
18 def FuzzyInt(n):
19 """Returns an integer derived from the input by one of several mutations."""
20 int_sizes = [8, 16, 32, 64, 128]
21 mutations = [
22 lambda n: utils.UniformExpoInteger(0, sys.maxint.bit_length() + 1),
23 lambda n: -utils.UniformExpoInteger(0, sys.maxint.bit_length()),
24 lambda n: 2 ** random.choice(int_sizes) - 1,
25 lambda n: 2 ** random.choice(int_sizes),
26 lambda n: 0,
27 lambda n: -n,
28 lambda n: n + 1,
29 lambda n: n - 1,
30 lambda n: n + random.randint(-1024, 1024),
31 ]
32 return random.choice(mutations)(n)
33
34
35 def FuzzyString(s):
36 """Returns a string derived from the input by one of several mutations."""
37 # First try some mutations that try to recognize certain types of strings
38 try:
39 s.decode("utf-8") # These mutations only make sense for textual data
40 except UnicodeDecodeError:
41 pass
42 else:
43 chained_mutations = [
44 FuzzIntsInString,
45 FuzzBase64InString,
46 FuzzListInString,
47 ]
48 original = s
49 for mutation in chained_mutations:
50 s = mutation(s)
51 # Stop if we've modified the string and our coin comes up heads
52 if s != original and random.getrandbits(1):
53 return s
54
55 # If we're still here, apply a more generic mutation
56 mutations = [
57 lambda s: "".join(random.choice(string.printable) for i in
58 xrange(utils.UniformExpoInteger(0, 14))),
59 lambda s: "".join(unichr(random.randint(0, sys.maxunicode)) for i in
60 xrange(utils.UniformExpoInteger(0, 14))).encode("utf-8"),
61 lambda s: os.urandom(utils.UniformExpoInteger(0, 14)),
62 lambda s: s * utils.UniformExpoInteger(1, 5),
63 lambda s: s + "A" * utils.UniformExpoInteger(0, 14),
64 lambda s: "A" * utils.UniformExpoInteger(0, 14) + s,
65 lambda s: s[:-random.randint(1, max(1, len(s) - 1))],
66 lambda s: textwrap.fill(s, random.randint(1, max(1, len(s) - 1))),
67 lambda s: "",
68 ]
69 return random.choice(mutations)(s)
70
71
72 def FuzzIntsInString(s):
73 """Returns a string where some integers have been fuzzed with FuzzyInt."""
74 def ReplaceInt(m):
75 val = m.group()
76 if random.getrandbits(1): # Flip a coin to decide whether to fuzz
77 return val
78 if not random.getrandbits(4): # Delete the integer 1/16th of the time
79 return ""
80 decimal = val.isdigit() # Assume decimal digits means a decimal number
81 n = FuzzyInt(int(val) if decimal else int(val, 16))
82 return str(n) if decimal else "%x" % n
83 return re.sub(r"\b[a-fA-F]*\d[0-9a-fA-F]*\b", ReplaceInt, s)
84
85
86 def FuzzBase64InString(s):
87 """Returns a string where Base64 components are fuzzed with FuzzyBuffer."""
88 def ReplaceBase64(m):
89 fb = FuzzyBuffer(base64.b64decode(m.group()))
90 fb.RandomMutation()
91 return base64.b64encode(fb)
92 # This only matches obvious Base64 words with trailing equals signs
93 return re.sub(r"(?<![A-Za-z0-9+/])"
94 r"(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)"
95 r"(?![A-Za-z0-9+/])", ReplaceBase64, s)
96
97
98 def FuzzListInString(s, separators=r", |,|; |;|\r\n|\s"):
99 """Tries to interpret the string as a list, and fuzzes it if successful."""
100 seps = re.findall(separators, s)
101 if not seps:
102 return s
103 sep = random.choice(seps) # Ones that appear often are more likely
104 items = FuzzyList(s.split(sep))
105 items.RandomMutation()
106 return sep.join(items)
107
108
109 class FuzzySequence(object):
110 """A helpful mixin for writing fuzzy mutable sequence types.
111
112 If a method parameter is left at its default value of None, an appropriate
113 random value will be chosen.
114 """
115
116 def Overwrite(self, value, location=None, amount=None):
117 """Overwrite amount elements starting at location with value.
118
119 Value can be a function of no arguments, in which case it will be called
120 every time a new value is needed.
121 """
122 if location is None:
123 location = random.randint(0, max(0, len(self) - 1))
124 if amount is None:
125 amount = utils.RandomLowInteger(min(1, len(self)), len(self) - location)
126 if hasattr(value, "__call__"):
127 new_elements = (value() for i in xrange(amount))
128 else:
129 new_elements = itertools.repeat(value, amount)
130 self[location:location+amount] = new_elements
131
132 def Insert(self, value, location=None, amount=None, max_exponent=14):
133 """Insert amount elements starting at location.
134
135 Value can be a function of no arguments, in which case it will be called
136 every time a new value is needed.
137 """
138 if location is None:
139 location = random.randint(0, max(0, len(self) - 1))
140 if amount is None:
141 amount = utils.UniformExpoInteger(0, max_exponent)
142 if hasattr(value, "__call__"):
143 new_elements = (value() for i in xrange(amount))
144 else:
145 new_elements = itertools.repeat(value, amount)
146 self[location:location] = new_elements
147
148 def Delete(self, location=None, amount=None):
149 """Delete amount elements starting at location."""
150 if location is None:
151 location = random.randint(0, max(0, len(self) - 1))
152 if amount is None:
153 amount = utils.RandomLowInteger(min(1, len(self)), len(self) - location)
154 del self[location:location+amount]
155
156
157 class FuzzyList(list, FuzzySequence):
158 """A list with additional methods for fuzzing."""
159
160 def RandomMutation(self, count=None, new_element=""):
161 """Apply count random mutations chosen from a list."""
162 random_items = lambda: random.choice(self) if self else new_element
163 mutations = [
164 lambda: random.shuffle(self),
165 self.reverse,
166 functools.partial(self.Overwrite, new_element),
167 functools.partial(self.Overwrite, random_items),
168 functools.partial(self.Insert, new_element, max_exponent=10),
169 functools.partial(self.Insert, random_items, max_exponent=10),
170 self.Delete,
171 ]
172 if count is None:
173 count = utils.RandomLowInteger(1, 5, beta=3.0)
174 for _ in xrange(count):
175 random.choice(mutations)()
176
177
178 class FuzzyBuffer(bytearray, FuzzySequence):
179 """A bytearray with additional methods for mutating the sequence of bytes."""
180
181 def __repr__(self):
182 return "%s(%r)" % (self.__class__.__name__, str(self))
183
184 def FlipBits(self, num_bits=None):
185 """Flip num_bits bits in the buffer at random."""
186 if num_bits is None:
187 num_bits = utils.RandomLowInteger(min(1, len(self)), len(self) * 8)
188 for bit in random.sample(xrange(len(self) * 8), num_bits):
189 self[bit / 8] ^= 1 << (bit % 8)
190
191 def RandomMutation(self, count=None):
192 """Apply count random mutations chosen from a weighted list."""
193 random_bytes = lambda: random.randint(0x00, 0xFF)
194 mutations = [
195 (self.FlipBits, 1),
196 (functools.partial(self.Overwrite, random_bytes), 1/3.0),
197 (functools.partial(self.Overwrite, 0xFF), 1/3.0),
198 (functools.partial(self.Overwrite, 0x00), 1/3.0),
199 (functools.partial(self.Insert, random_bytes), 1/3.0),
200 (functools.partial(self.Insert, 0xFF), 1/3.0),
201 (functools.partial(self.Insert, 0x00), 1/3.0),
202 (self.Delete, 1),
203 ]
204 if count is None:
205 count = utils.RandomLowInteger(1, 5, beta=3.0)
206 for _ in xrange(count):
207 utils.WeightedChoice(mutations)()
OLDNEW
« 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